// Minimal Windows Screen Saver
//
// This particular screen saver takes a snapshot of the desktop upon startup,
// keeps the snapshot as a back-buffer, and draws blobs on it. There's a
// configuration option to change the size of the blobs.
//
// The basic things to change are marked TODO. They are: load/save the global
// settings, create an animation for the saver, make its Configuration dialog,
// and set its name in the Stringtable resource.
// But the source code is very short and clear, so the programmer should feel
// confident to modify all of it.
//
#pragma warning( disable: 4127 4800 4702 )
#include <string>
#include <vector>
#include <math.h>
#include <windows.h>
#include <commctrl.h>
#include <regstr.h>
#include <stdlib.h>
#include <tchar.h>
typedef std::basic_string<TCHAR> tstring;
using namespace std;
//
// TODO: set the saver's name and URL in the STRINGTABLE resource.
//
// If you want debugging output, set this to "OutputDebugString" or a filename. The second line controls the saver's "friendliness"
const tstring DebugFile=_T("OutputDebugString");
const bool SCRDEBUG = (DebugFile!=_T(""));
//
// These global variables are loaded at the start of WinMain
BOOL MuteSound;
DWORD MouseThreshold; // In pixels
DWORD PasswordDelay; // In seconds. Doesn't apply to NT/XP/Win2k.
// also these, which are used by the General properties dialog
DWORD PasswordDelayIndex; // 0=seconds, 1=minutes. Purely a visual thing.
DWORD MouseThresholdIndex; // 0=high, 1=normal, 2=low, 3=ignore. Purely visual
TCHAR Corners[5]; // "-YN-" or something similar
BOOL HotServices; // whether they're present or not
// and these are created when the dialog/saver starts
POINT InitCursorPos;
DWORD InitTime; // in ms
bool IsDialogActive;
bool ReallyClose; // for NT, so we know if a WM_CLOSE came from us or it.
// Some other minor global variables and prototypes
HINSTANCE hInstance=0;
HICON hiconsm=0,hiconbg=0;
HBITMAP hbmmonitor=0; // bitmap for the monitor class
tstring SaverName; // this is retrieved at the start of WinMain from String Resource 1
vector<RECT> monitors; // the rectangles of each monitor (smSaver) or of the preview window (smPreview)
struct TSaverWindow;
const UINT SCRM_GETMONITORAREA=WM_APP; // gets the client rectangle. 0=all, 1=topleft, 2=topright, 3=botright, 4=botleft corner
inline void Debug(const tstring s);
tstring GetLastErrorString();
void SetDlgItemUrl(HWND hdlg,int id,const TCHAR *url);
void RegSave(const tstring name,int val);
void RegSave(const tstring name,bool val);
void RegSave(const tstring name,tstring val);
int RegLoad(const tstring name,int def);
bool RegLoad(const tstring name,bool def);
tstring RegLoad(const tstring name,tstring def);
//
// IMPORTANT GLOBAL VARIABLES:
enum TScrMode {smNone,smConfig,smPassword,smPreview,smSaver,smInstall,smUninstall} ScrMode=smNone;
vector<TSaverWindow*> SaverWindow; // the saver windows, one per monitor. In preview mode there's just one.
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// These are the saver's global settings
int refcount=0; // we use a global refcount so that settings aren't loaded more than necessary
bool BigBlobs=false; // this is our setting that we're storing in the registry
void CommonInit()
{ refcount++; if (refcount!=1) return;
//
// TODO: saver initialization - load settings from registry &c.
// Some things are naturally global, and shared between configuration
// dialog and saver-preview, or between multiple monitors when running full-screen.
//
BigBlobs=RegLoad(_T("bigblobs"),true);
}
// TSaverWindow: one is created for each saver window (be it preview, or the
// preview in the config dialog, or one for each monitor when running full-screen)
// For things like back-buffer that are inherently related to a particular window,
// I store them here. (But things that are shared between all saver windows on a multi
// monitor system, I leave as global instead).
//
struct TSaverWindow
{ HWND hwnd; int id; // id=-1 for a preview, or 0..n for full-screen on the specified monitor
HANDLE hbmBuffer; // this is a back-buffer used by the saver
int bw,bh; // dimensions of the back-buffer
//
TSaverWindow(HWND _hwnd,int _id) : hwnd(_hwnd),id(_id)
{ CommonInit(); SetTimer(hwnd,1,100,NULL);
// In this example we take a snapshot of the screen at startup. This trick doesn't
// really work under NT/Win2k/XP, where savers are run on their own private desktops.
// If we're a preview window, we snapshot the primary desktop. If we're a full-screen
// saver, we snapshot whatever monitor we were running on.
RECT rc; GetClientRect(hwnd,&rc); bw=rc.right; bh=rc.bottom;
if (id==-1) {rc.left=0; rc.top=0; rc.right=GetSystemMetrics(SM_CXSCREEN); rc.bottom=GetSystemMetrics(SM_CYSCREEN);}
else GetWindowRect(hwnd,&rc);
//
HDC sdc=GetDC(0), bufdc=CreateCompatibleDC(sdc);
hbmBuffer=CreateCompatibleBitmap(sdc,bw,bh); SelectObject(bufdc,hbmBuffer);
SetStretchBltMode(bufdc,COLORONCOLOR);
StretchBlt(bufdc,0,0,bw,bh,sdc,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,SRCCOPY);
DeleteDC(bufdc);
ReleaseDC(0,sdc);
}
~TSaverWindow()
{ KillTimer(hwnd,1);
if (hbmBuffer!=0) DeleteObject(hbmBuffer); hbmBuffer=0;
}
void OnTimer()
{ HDC sdc=GetDC(0), bufdc=CreateCompatibleDC(sdc); ReleaseDC(0,sdc);
SelectObject(bufdc,hbmBuffer);
//
// TODO: make the saver animate!
//
int col=rand()%20;
int divisor=(BigBlobs?2:8);
int cx=rand()%bw, cy=rand()%bh, w=rand()%(bw/divisor), h=rand()%(bh/divisor);
SelectObject(bufdc,GetSysColorBrush(col));
Ellipse(bufdc,cx-w/2,cy-h/2,cx+w/2,cy+h/2);
//
DeleteDC(bufdc);
InvalidateRect(hwnd,NULL,TRUE);
}
void OtherWndProc(UINT,WPARAM,LPARAM) {}
void OnPaint(HDC hdc,const RECT &rc)
{ HDC bufdc=CreateCompatibleDC(hdc); SelectObject(bufdc,hbmBuffer);
StretchBlt(hdc,0,0,rc.right,rc.bottom,bufdc,0,0,bw,bh,SRCCOPY);
DeleteDC(bufdc);
}
};
BOOL CALLBACK OptionsDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ switch (msg)
{ case WM_INITDIALOG:
{ CommonInit();
//
// TODO: set up the "options" dialog.
// Also, in the dialog-editor, remember to remove the "Mute" checkbox if you don't have sound!
//
CheckDlgButton(hwnd,102,BigBlobs?BST_CHECKED:BST_UNCHECKED);
//
} return TRUE;
case WM_COMMAND:
{ int id=LOWORD(wParam), code=HIWORD(wParam);
if (id==102 && code==BN_CLICKED) BigBlobs = (IsDlgButtonChecked(hwnd,102)==BST_CHECKED);
} return TRUE;
case WM_NOTIFY:
{ LPNMHDR nmh=(LPNMHDR)lParam; UINT code=nmh->code;
switch (code)
{ case (PSN_APPLY):
{ //
// TODO: save it to the registry
//
RegSave(_T("bigblobs"),BigBlobs);
//
SetWindowLong(hwnd,DWL_MSGRESULT,PSNRET_NOERROR);
} return TRUE;
}
} return FALSE;
}
return FALSE;
}
// ---------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------
BOOL VerifyPassword(HWND hwnd)
{ // Under NT, we return TRUE immediately. This lets the saver quit, and the system manages passwords.
// Under '95, we call VerifyScreenSavePwd. This checks the appropriate registry key and, if necessary, pops up a verify dialog
OSVERSIONINFO osv; osv.dwOSVersionInfoSize=sizeof(osv); GetVersionEx(&osv);
if (osv.dwPlatformId==VER_PLATFORM_WIN32_NT) return TRUE;
HINSTANCE hpwdcpl=::LoadLibrary(_T("PASSWORD.CPL"));
if (hpwdcpl==NULL) {Debug(_T("Unable to load PASSWORD.CPL. Aborting"));return TRUE;}
typedef BOOL (WINAPI *VERIFYSCREENSAVEPWD)(HWND hwnd);
VERIFYSCREENSAVEPWD VerifyScreenSavePwd;
VerifyScreenSavePwd=(VERIFYSCREENSAVEPWD)GetProcAddress(hpwdcpl,"VerifyScreenSavePwd");
if (VerifyScreenSavePwd==NULL) {Debug(_T("Unable to get VerifyPwProc address. Aborting"));FreeLibrary(hpwdcpl);return TRUE;}
Debug(_T("About to call VerifyPwProc")); BOOL bres=VerifyScreenSavePwd(hwnd); FreeLibrary(hpwdcpl);
return bres;
}
void ChangePassword(HWND hwnd)
{ // This only ever gets called under '95, when started with the /a option.
HINSTANCE hmpr=::LoadLibrary(_T("MPR.DLL"));
if (hmpr==NULL) {Debug(_T("MPR.DLL not found: cannot change password."));return;}
typedef VOID (WINAPI *PWDCHANGEPASSWORD) (LPCSTR lpcRegkeyname,HWND hwnd,UINT uiReserved1,UINT uiReserved2);
PWDCHANGEPASSWORD PwdChangePassword=(PWDCHANGEPASSWORD)::GetProcAddress(hmpr,"PwdChangePasswordA");
if (PwdChangePassword==NULL) {FreeLibrary(hmpr); Debug(_T("PwdChangeProc not found: cannot change password"));return;}
PwdChangePassword("SCRSAVE",hwnd,0,0); FreeLibrary(hmpr);
}
void ReadGeneralRegistry()
{ PasswordDelay=15; PasswordDelayIndex=0; MouseThreshold=50; IsDialogActive=false; // default values in case they're not in registry
LONG res; HKEY skey; DWORD valtype, valsize, val;
res=RegOpenKeyEx(HKEY_CURRENT_USER,REGSTR_PATH_SETUP _T("\\Screen Savers"),0,KEY_READ,&skey);
if (res!=ERROR_SUCCESS) return;
valsize=sizeof(val); res=RegQueryValueEx(skey,_T("Password Delay"),0,&valtype,(LPBYTE)&val,&valsize); if (res==ERROR_SUCCESS) PasswordDelay=val;
valsize=sizeof(val); res=RegQueryValueEx(skey,_T("Password Delay Index"),0,&valtype,(LPBYTE)&val,&valsize); if (res==ERROR_SUCCESS) PasswordDelayIndex=val;
valsize=sizeof(val); res=RegQueryValueEx(skey,_T("Mouse Threshold"),0,&valtype,(LPBYTE)&val,&valsize);if (res==ERROR_SUCCESS) MouseThreshold=val;
valsize=sizeof(val); res=RegQueryValueEx(skey,_T("Mouse Threshold Index"),0,&valtype,(LPBYTE)&val,&valsize);if (res==ERROR_SUCCESS) MouseThresholdIndex=val;
valsize=sizeof(val); res=RegQueryValueEx(skey,_T("Mute Sound"),0,&valtype,(LPBYTE)&val,&valsize); if (res==ERROR_SUCCESS) MuteSound=val;
valsize=5*sizeof(TCHAR); res=RegQueryValueEx(skey,_T("Mouse Corners"),0,&valtype,(LPBYTE)Corners,&valsize);
for (int i=0; i<4; i++) {if (Corners[i]!='Y' && Corners[i]!='N') Corners[i]='-';} Corners[4]=0;
RegCloseKey(skey);
//
HotServices=FALSE;
HINSTANCE sagedll=LoadLibrary(_T("sage.dll"));
if (sagedll==0) sagedll=LoadLibrary(_T("scrhots.dll"));
if (sagedll!=0)
{ typedef BOOL (WINAPI *SYSTEMAGENTDETECT)();
SYSTEMAGENTDETECT detectproc=(SYSTEMAGENTDETECT)GetProcAddress(sagedll,"System_Agent_Detect");
if (detectproc!=NULL) HotServices=detectproc();
FreeLibrary(sagedll);
}
}
void WriteGeneralRegistry()
{ LONG res; HKEY skey; DWORD val;
res=RegCreateKeyEx(HKEY_CURRENT_USER,REGSTR_PATH_SETUP _T("\\Screen Savers"),0,0,0,KEY_ALL_ACCESS,0,&skey,0);
if (res!=ERROR_SUCCESS) return;
val=PasswordDelay; RegSetValueEx(skey,_T("Password Delay"),0,REG_DWORD,(const BYTE*)&val,sizeof(val));
val=PasswordDelayIndex; RegSetValueEx(skey,_T("Password Delay Index"),0,REG_DWORD,(const BYTE*)&val,sizeof(val));
val=MouseThreshold; RegSetValueEx(skey,_T("Mouse Threshold"),0,REG_DWORD,(const BYTE*)&val,sizeof(val));
val=MouseThresholdIndex; RegSetValueEx(skey,_T("Mouse Threshold Index"),0,REG_DWORD,(const BYTE*)&val,sizeof(val));
val=MuteSound?1:0; RegSetValueEx(skey,_T("Mute Sound"),0,REG_DWORD,(const BYTE*)&val,sizeof(val));
RegSetValueEx(skey,_T("Mouse Corners"),0,REG_SZ,(const BYTE*)Corners,5*sizeof(TCHAR));
RegCloseKey(skey);
//
HINSTANCE sagedll=LoadLibrary(_T("sage.dll"));
if (sagedll==0) sagedll=LoadLibrary(_T("scrhots.dll"));
if (sagedll!=0)
{ typedef VOID (WINAPI *SCREENSAVERCHANGED)();
SCREENSAVERCHANGED changedproc=(SCREENSAVERCHANGED)GetProcAddress(sagedll,"Screen_Saver_Changed");
if (changedproc!=NULL) changedproc();
FreeLibrary(sagedll);
}
}
LRESULT CALLBACK SaverWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ TSaverWindow *sav; int id; HWND hmain;
if (msg==WM_CREATE)
{ CREATESTRUCT *cs=(CREATESTRUCT*)lParam; id=*(int*)cs->lpCreateParams; SetWindowLong(hwnd,0,id);
sav = new TSaverWindow(hwnd,id); SetWindowLong(hwnd,GWL_USERDATA,(LONG)sav);
SaverWindow.push_back(sav);
}
else
{ sav=(TSaverWindow*)GetWindowLong(hwnd,GWL_USERDATA);
id=GetWindowLong(hwnd,0);
}
if (id<=0) hmain=hwnd; else hmain=SaverWindow[0]->hwnd;
//
if (msg==WM_TIMER) sav->OnTimer();
else if (msg==WM_PAINT) {PAINTSTRUCT ps; BeginPaint(hwnd,&ps); RECT rc; GetClientRect(hwnd,&rc); if (sav!=0) sav->OnPaint(ps.hdc,rc); EndPaint(hwnd,&ps);}
else if (sav!=0) sav->OtherWndProc(msg,wParam,lParam);
//
switch (msg)
{ case WM_ACTIVATE: case WM_ACTIVATEAPP: case WM_NCACTIVATE:
{ TCHAR pn[100]; GetClassName((HWND)lParam,pn,100);
bool ispeer = (_tcsncmp(pn,_T("ScrClass"),8)==0);
if (ScrMode==smSaver && !IsDialogActive && LOWORD(wParam)==WA_INACTIVE && !ispeer && !SCRDEBUG)
{ Debug(_T("WM_ACTIVATE: about to inactive window, so sending close")); ReallyClose=true; PostMessage(hmain,WM_CLOSE,0,0); return 0;
}
} break;
case WM_SETCURSOR:
{ if (ScrMode==smSaver && !IsDialogActive && !SCRDEBUG) SetCursor(NULL);
else SetCursor(LoadCursor(NULL,IDC_ARROW));
} return 0;
case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN:
{ if (ScrMode==smSaver && !IsDialogActive) {Debug(_T("WM_BUTTONDOWN: sending close")); ReallyClose=true; PostMessage(hmain,WM_CLOSE,0,0); return 0;}
} break;
case WM_MOUSEMOVE:
{ if (ScrMode==smSaver && !IsDialogActive && !SCRDEBUG)
{ POINT pt; GetCursorPos(&pt);
int dx=pt.x-InitCursorPos.x; if (dx<0) dx=-dx; int dy=pt.y-InitCursorPos.y; if (dy<0) dy=-dy;
if (dx>(int)MouseThreshold || dy>(int)MouseThreshold)
{ Debug(_T("WM_MOUSEMOVE: gone beyond threshold, sending close")); ReallyClose=true; PostMessage(hmain,WM_CLOSE,0,0);
}
}
} return 0;
case (WM_SYSCOMMAND):
{ if (ScrMode==smSaver)
{ if (wParam==SC_SCREENSAVE) {Debug(_T("WM_SYSCOMMAND: gobbling up a SC_SCREENSAVE to stop a new saver from running."));return 0;}
if (wParam==SC_CLOSE && !SCRDEBUG) {Debug(_T("WM_SYSCOMMAND: gobbling up a SC_CLOSE"));return 0;}
}
} break;
case (WM_CLOSE):
{ if (id>0) return 0; // secondary windows ignore this message
if (id==-1) {DestroyWindow(hwnd); return 0;} // preview windows close obediently
if (ReallyClose && !IsDialogActive)
{ Debug(_T("WM_CLOSE: maybe we need a password"));
BOOL CanClose=TRUE;
if (GetTickCount()-InitTime > 1000*PasswordDelay)
{ IsDialogActive=true; SendMessage(hwnd,WM_SETCURSOR,0,0);
CanClose=VerifyPassword(hwnd);
IsDialogActive=false; SendMessage(hwnd,WM_SETCURSOR,0,0); GetCursorPos(&InitCursorPos);
}
// note: all secondary monitors are owned by the primary. And we're the primary (id==0)
// therefore, when we destroy ourself, windows will destroy the others first
if (CanClose) {Debug(_T("WM_CLOSE: doing a DestroyWindow")); DestroyWindow(hwnd);}
else {Debug(_T("WM_CLOSE: but failed password, so doing nothing"));}
}
} return 0;
case (WM_DESTROY):
{ Debug(_T("WM_DESTROY."));
SetWindowLong(hwnd,0,0); SetWindowLong(hwnd,GWL_USERDATA,0);
for (vector<TSaverWindow*>::iterator i=SaverWindow.begin(); i!=SaverWindow.end(); i++) {if (sav==*i) *i=0;}
delete sav;
if ((id==0 && ScrMode==smSaver) || ScrMode==smPreview) PostQuitMessage(0);
} break;
}
return DefWindowProc(hwnd,msg,wParam,lParam);
}
// ENUM-MONITOR-CALLBACK is part of DoSaver. Its stuff is in windef.h but
// requires WINVER>=0x0500. Well, we're doing LoadLibrary, so we know it's
// safe...
#ifndef SM_CMONITORS
DECLARE_HANDLE(HMONITOR);
#endif
//
BOOL CALLBACK EnumMonitorCallback(HMONITOR,HDC,LPRECT rc,LPARAM)
{ if (rc->left==0 && rc->top==0) monitors.insert(monitors.begin(),*rc);
else monitors.push_back(*rc);
return TRUE;
}
void DoSaver(HWND hparwnd, bool fakemulti)
{ if (ScrMode==smPreview)
{ RECT rc; GetWindowRect(hparwnd,&rc); monitors.push_back(rc);
}
else if (fakemulti)
{ int w=GetSystemMetrics(SM_CXSCREEN), x1=w/4, x2=w*2/3, h=x2-x1; RECT rc;
rc.left=x1; rc.top=x1; rc.right=x1+h; rc.bottom=x1+h; monitors.push_back(rc);
rc.left=0; rc.top=x1; rc.right=x1; rc.bottom=x1+x1; monitors.push_back(rc);
rc.left=x2; rc.top=x1+h+x2-w; rc.right=w; rc.bottom=x1+h; monitors.push_back(rc);
}
else
{ int num_monitors=GetSystemMetrics(80); // 80=SM_CMONITORS
if (num_monitors>1)
{ typedef BOOL (CALLBACK *LUMONITORENUMPROC)(HMONITOR,HDC,LPRECT,LPARAM);
typedef BOOL (WINAPI *LUENUMDISPLAYMONITORS)(HDC,LPCRECT,LUMONITORENUMPROC,LPARAM);
HINSTANCE husr=LoadLibrary(_T("user32.dll"));
LUENUMDISPLAYMONITORS pEnumDisplayMonitors=0;
if (husr!=NULL) pEnumDisplayMonitors=(LUENUMDISPLAYMONITORS)GetProcAddress(husr,"EnumDisplayMonitors");
if (pEnumDisplayMonitors!=NULL) (*pEnumDisplayMonitors)(NULL,NULL,EnumMonitorCallback,NULL);
if (husr!=NULL) FreeLibrary(husr);
}
if (monitors.size()==0)
{ RECT rc; rc.left=0; rc.top=0; rc.right=GetSystemMetrics(SM_CXSCREEN); rc.bottom=GetSystemMetrics(SM_CYSCREEN);
monitors.push_back(rc);
}
}
//
HWND hwnd=0;
if (ScrMode==smPreview)
{ RECT rc; GetWindowRect(hparwnd,&rc); int w=rc.right-rc.left, h=rc.bottom-rc.top;
int id=-1; hwnd=CreateWindowEx(0,_T("ScrClass"),_T(""),WS_CHILD|WS_VISIBLE,0,0,w,h,hparwnd,NULL,hInstance,&id);
}
else
{ GetCursorPos(&InitCursorPos); InitTime=GetTickCount();
for (int i=0; i<(int)monitors.size(); i++)
{ const RECT &rc = monitors[i];
DWORD exstyle=WS_EX_TOPMOST; if (SCRDEBUG) exstyle=0;
HWND hthis = CreateWindowEx(exstyle,_T("ScrClass"),_T(""),WS_POPUP|WS_VISIBLE,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,hwnd,NULL,hInstance,&i);
if (i==0) hwnd=hthis;
}
}
if (hwnd!=NULL)
{ UINT oldval;
if (ScrMode==smSaver) SystemParametersInfo(SPI_SETSCREENSAVERRUNNING,1,&oldval,0);
MSG msg;
while (GetMessage(&msg,NULL,0,0))
{ TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (ScrMode==smSaver) SystemParametersInfo(SPI_SETSCREENSAVERRUNNING,0,&oldval,0);
}
//
SaverWindow.clear();
return;
}
BOOL CALLBACK GeneralDlgProc(HWND hwnd,UINT msg,WPARAM,LPARAM lParam)
{ switch (msg)
{ case (WM_INITDIALOG):
{ ShowWindow(GetDlgItem(hwnd,HotServices?102:101),SW_HIDE);
SetDlgItemText(hwnd,112,Corners);
SendDlgItemMessage(hwnd,109,CB_ADDSTRING,0,(LPARAM)_T("seconds"));
SendDlgItemMessage(hwnd,109,CB_ADDSTRING,0,(LPARAM)_T("minutes"));
SendDlgItemMessage(hwnd,109,CB_SETCURSEL,PasswordDelayIndex,0);
int n=PasswordDelay; if (PasswordDelayIndex==1) n/=60;
TCHAR c[16]; wsprintf(c,_T("%i"),n); SetDlgItemText(hwnd,107,c);
SendDlgItemMessage(hwnd,108,UDM_SETRANGE,0,MAKELONG(99,0));
SendDlgItemMessage(hwnd,105,CB_ADDSTRING,0,(LPARAM)_T("High"));
SendDlgItemMessage(hwnd,105,CB_ADDSTRING,0,(LPARAM)_T("Normal"));
SendDlgItemMessage(hwnd,105,CB_ADDSTRING,0,(LPARAM)_T("Low"));
SendDlgItemMessage(hwnd,105,CB_ADDSTRING,0,(LPARAM)_T("Keyboard only (ignore mouse movement)"));
SendDlgItemMessage(hwnd,105,CB_SETCURSEL,MouseThresholdIndex,0);
if (MuteSound) CheckDlgButton(hwnd,113,BST_CHECKED);
OSVERSIONINFO ver; ZeroMemory(&ver,sizeof(ver)); ver.dwOSVersionInfoSize=sizeof(ver); GetVersionEx(&ver);
for (int i=106; i<111 && ver.dwPlatformId!=VER_PLATFORM_WIN32_WINDOWS; i++) ShowWindow(GetDlgItem(hwnd,i),SW_HIDE);
} return TRUE;
case (WM_NOTIFY):
{ LPNMHDR nmh=(LPNMHDR)lParam; UINT code=nmh->code;
switch (code)
{ case (PSN_APPLY):
{ GetDlgItemText(hwnd,112,Corners,5);
PasswordDelayIndex=SendDlgItemMessage(hwnd,109,CB_GETCURSEL,0,0);
TCHAR c[16]; GetDlgItemText(hwnd,107,c,16); int n=_ttoi(c); if (PasswordDelayIndex==1) n*=60; PasswordDelay=n;
MouseThresholdIndex=SendDlgItemMessage(hwnd,105,CB_GETCURSEL,0,0);
if (MouseThresholdIndex==0) MouseThreshold=0;
else if (MouseThresholdIndex==1) MouseThreshold=200;
else if (MouseThresholdIndex==2) MouseThreshold=400;
else MouseThreshold=999999;
MuteSound = (IsDlgButtonChecked(hwnd,113)==BST_CHECKED);
WriteGeneralRegistry();
SetWindowLong(hwnd,DWL_MSGRESULT,PSNRET_NOERROR);
} return TRUE;
}
} return FALSE;
}
return FALSE;
}
//
// MONITOR CONTROL -- either corners or a preview
//
LRESULT CALLBACK MonitorWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ switch (msg)
{ case WM_CREATE:
{ TCHAR c[5]; GetWindowText(hwnd,c,5); if (*c!=0) return 0;
int id=-1; RECT rc; SendMessage(hwnd,SCRM_GETMONITORAREA,0,(LPARAM)&rc);
CreateWindow(_T("ScrClass"),_T(""),WS_CHILD|WS_VISIBLE,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,hwnd,NULL,hInstance,&id);
} return 0;
case WM_PAINT:
{ if (hbmmonitor==0) hbmmonitor=LoadBitmap(hInstance,_T("Monitor"));
RECT rc; GetClientRect(hwnd,&rc);
//
PAINTSTRUCT ps; BeginPaint(hwnd,&ps);
HBITMAP hback = (HBITMAP)GetWindowLong(hwnd,GWL_USERDATA);
if (hback!=0)
{ BITMAP bmp; GetObject(hback,sizeof(bmp),&bmp);
if (bmp.bmWidth!=rc.right || bmp.bmHeight!=rc.bottom) {DeleteObject(hback); hback=0;}
}
if (hback==0) {hback=CreateCompatibleBitmap(ps.hdc,rc.right,rc.bottom); SetWindowLong(hwnd,GWL_USERDATA,(LONG)hback);}
HDC backdc=CreateCompatibleDC(ps.hdc);
HGDIOBJ holdback=SelectObject(backdc,hback);
BitBlt(backdc,0,0,rc.right,rc.bottom,ps.hdc,0,0,SRCCOPY);
//
TCHAR corners[5]; GetWindowText(hwnd,corners,5);
HDC hdc=CreateCompatibleDC(ps.hdc);
HGDIOBJ hold=SelectObject(hdc,hbmmonitor);
StretchBlt(backdc,0,0,rc.right,rc.bottom,hdc,0,0,184,170,SRCAND);
StretchBlt(backdc,0,0,rc.right,rc.bottom,hdc,184,0,184,170,SRCINVERT);
RECT crc; SendMessage(hwnd,SCRM_GETMONITORAREA,0,(LPARAM)&crc);
//
if (*corners!=0) FillRect(backdc,&crc,GetSysColorBrush(COLOR_DESKTOP));
for (int i=0; i<4 && *corners!=0; i++)
{ RECT crc; SendMessage(hwnd,SCRM_GETMONITORAREA,i+1,(LPARAM)&crc);
int y=0; if (corners[i]=='Y') y=22; else if (corners[i]=='N') y=44;
BitBlt(backdc,crc.left,crc.top,crc.right-crc.left,crc.bottom-crc.top,hdc,368,y,SRCCOPY);
if (!HotServices)
{ DWORD col=GetSysColor(COLOR_DESKTOP);
for (int y=crc.top; y<crc.bottom; y++)
{ for (int x=crc.left+(y&1); x<crc.right; x+=2) SetPixel(backdc,x,y,col);
}
}
}
SelectObject(hdc,hold);
DeleteDC(hdc);
//
BitBlt(ps.hdc,0,0,rc.right,rc.bottom,backdc,0,0,SRCCOPY);
SelectObject(backdc,holdback);
DeleteDC(backdc);
EndPaint(hwnd,&ps);
} return 0;
case SCRM_GETMONITORAREA:
{ RECT *prc=(RECT*)lParam;
if (hbmmonitor==0) hbmmonitor=LoadBitmap(hInstance,_T("Monitor"));
// those are the client coordinates unscalled
RECT wrc; GetClientRect(hwnd,&wrc); int ww=wrc.right, wh=wrc.bottom;
RECT rc; rc.left=16*ww/184; rc.right=168*ww/184; rc.top=17*wh/170; rc.bottom=130*wh/170;
*prc=rc; if (wParam==0) return 0;
if (wParam==1) {prc->right=rc.left+24; prc->bottom=rc.top+22;}
else if (wParam==2) {prc->left=rc.right-24; prc->bottom=rc.top+22;}
else if (wParam==3) {prc->left=rc.right-24; prc->top=rc.bottom-22;}
else if (wParam==4) {prc->right=rc.left+24; prc->top=rc.bottom-22;}
} return 0;
case WM_LBUTTONDOWN:
{ if (!HotServices) return 0;
int x=LOWORD(lParam), y=HIWORD(lParam);
TCHAR corners[5]; GetWindowText(hwnd,corners,5);
if (corners[0]==0) return 0;
int click=-1; for (int i=0; i<4; i++)
{ RECT rc; SendMessage(hwnd,SCRM_GETMONITORAREA,i+1,(LPARAM)&rc);
if (x>=rc.left && y>=rc.top && x<rc.right && y<rc.bottom) {click=i; break;}
}
if (click==-1) return 0;
for (int j=0; j<4; j++)
{ if (corners[j]!='-' && corners[j]!='Y' && corners[j]!='N') corners[j]='-';
}
corners[4]=0;
//
HMENU hmenu=CreatePopupMenu();
MENUITEMINFO mi; ZeroMemory(&mi,sizeof(mi)); mi.cbSize=sizeof(MENUITEMINFO);
mi.fMask=MIIM_TYPE|MIIM_ID|MIIM_STATE|MIIM_DATA;
mi.fType=MFT_STRING|MFT_RADIOCHECK;
mi.wID='N'; mi.fState=MFS_ENABLED; if (corners[click]=='N') mi.fState|=MFS_CHECKED;
mi.dwTypeData=_T("Never"); mi.cch=sizeof(TCHAR)*_tcslen(mi.dwTypeData);
InsertMenuItem(hmenu,0,TRUE,&mi);
mi.wID='Y'; mi.fState=MFS_ENABLED; if (corners[click]=='Y') mi.fState|=MFS_CHECKED;
mi.dwTypeData=_T("Now"); mi.cch=sizeof(TCHAR)*_tcslen(mi.dwTypeData);
InsertMenuItem(hmenu,0,TRUE,&mi);
mi.wID='-'; mi.fState=MFS_ENABLED; if (corners[click]!='Y' && corners[click]!='N') mi.fState|=MFS_CHECKED;
mi.dwTypeData=_T("Default"); mi.cch=sizeof(TCHAR)*_tcslen(mi.dwTypeData);
InsertMenuItem(hmenu,0,TRUE,&mi);
POINT pt; pt.x=x; pt.y=y; ClientToScreen(hwnd,&pt);
int cmd = TrackPopupMenuEx(hmenu,TPM_RETURNCMD|TPM_RIGHTBUTTON,pt.x,pt.y,hwnd,NULL);
if (cmd!=0) corners[click]=(char)cmd;
corners[4]=0; SetWindowText(hwnd,corners);
InvalidateRect(hwnd,NULL,FALSE);
} return 0;
case WM_DESTROY:
{ HBITMAP hback = (HBITMAP)SetWindowLong(hwnd,GWL_USERDATA,0);
if (hback!=0) DeleteObject(hback);
} return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
BOOL CALLBACK AboutDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM)
{ if (msg==WM_INITDIALOG)
{ SetDlgItemText(hdlg,101,SaverName.c_str());
SetDlgItemUrl(hdlg,102,_T("http://www.wischik.com/lu/scr/"));
SetDlgItemText(hdlg,102,_T("www.wischik.com/scr"));
return TRUE;
}
else if (msg==WM_COMMAND)
{ int id=LOWORD(wParam);
if (id==IDOK || id==IDCANCEL) EndDialog(hdlg,id);
return TRUE;
}
else return FALSE;
}
//
// PROPERTY SHEET SUBCLASSING -- this is to stick an "About" option on the sysmenu.
//
WNDPROC OldSubclassProc=0;
LRESULT CALLBACK PropertysheetSubclassProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ if (msg==WM_SYSCOMMAND && wParam==3500)
{ DialogBox(hInstance,_T("DLG_ABOUT"),hwnd,AboutDlgProc);
return 0;
}
if (OldSubclassProc!=NULL) return CallWindowProc(OldSubclassProc,hwnd,msg,wParam,lParam);
else return DefWindowProc(hwnd,msg,wParam,lParam);
}
int CALLBACK PropertysheetCallback(HWND hwnd,UINT msg,LPARAM)
{ if (msg!=PSCB_INITIALIZED) return 0;
HMENU hsysmenu=GetSystemMenu(hwnd,FALSE);
AppendMenu(hsysmenu,MF_SEPARATOR,1,_T("-"));
AppendMenu(hsysmenu,MF_STRING,3500,_T("About..."));
OldSubclassProc=(WNDPROC)SetWindowLong(hwnd,GWL_WNDPROC,(LONG)PropertysheetSubclassProc);
return 0;
}
void DoConfig(HWND hpar)
{ hiconbg = (HICON)LoadImage(hInstance,MAKEINTRESOURCE(1),IMAGE_ICON,GetSystemMetrics(SM_CXICON),GetSystemMetrics(SM_CYICON),0);
hiconsm = (HICON)LoadImage(hInstance,MAKEINTRESOURCE(1),IMAGE_ICON,GetSystemMetrics(SM_CXSMICON),GetSystemMetrics(SM_CYSMICON),0);
//
PROPSHEETHEADER psh; ZeroMemory(&psh,sizeof(psh));
PROPSHEETPAGE psp[2]; ZeroMemory(psp,2*sizeof(PROPSHEETPAGE));
psp[0].dwSize=sizeof(psp[0]);
psp[0].dwFlags=PSP_DEFAULT;
psp[0].hInstance=hInstance;
psp[0].pszTemplate=_T("DLG_GENERAL");
psp[0].pfnDlgProc=GeneralDlgProc;
psp[1].dwSize=sizeof(psp[1]);
psp[1].dwFlags=PSP_DEFAULT;
psp[1].hInstance=hInstance;
psp[1].pszTemplate=_T("DLG_OPTIONS");
psp[1].pfnDlgProc=OptionsDlgProc;
psh.dwSize=sizeof(psh);
psh.dwFlags=PSH_NOAPPLYNOW | PSH_PROPSHEETPAGE | PSH_USEHICON | PSH_USECALLBACK;
psh.hwndParent=hpar;
psh.hInstance=hInstance;
psh.hIcon=hiconsm;
tstring cap=_T("Options for ")+SaverName; psh.pszCaption=cap.c_str();
psh.nPages=2;
psh.nStartPage=1;
psh.ppsp=psp;
psh.pfnCallback=PropertysheetCallback;
Debug(_T("Config..."));
PropertySheet(&psh);
Debug(_T("Config done."));
if (hiconbg!=0) DestroyIcon(hiconbg); hiconbg=0;
if (hiconsm!=0) DestroyIcon(hiconsm); hiconsm=0;
if (hbmmonitor!=0) DeleteObject(hbmmonitor); hbmmonitor=0;
}
// This routine is for using ScrPrev. It's so that you can start the saver
// with the command line /p scrprev and it runs itself in a preview window.
// You must first copy ScrPrev somewhere in your search path
HWND CheckForScrprev()
{ HWND hwnd=FindWindow(_T("Scrprev"),NULL); // looks for the Scrprev class
if (hwnd==NULL) // try to load it
{ STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si,sizeof(si)); ZeroMemory(&pi,sizeof(pi));
si.cb=sizeof(si);
TCHAR cmd[MAX_PATH]; _tcscpy(cmd,_T("Scrprev")); // unicode CreateProcess requires it writeable
BOOL cres=CreateProcess(NULL,cmd,0,0,FALSE,CREATE_NEW_PROCESS_GROUP | CREATE_DEFAULT_ERROR_MODE,
0,0,&si,&pi);
if (!cres) {Debug(_T("Error creating scrprev process")); return NULL;}
DWORD wres=WaitForInputIdle(pi.hProcess,2000);
if (wres==WAIT_TIMEOUT) {Debug(_T("Scrprev never becomes idle")); return NULL;}
if (wres==0xFFFFFFFF) {Debug(_T("ScrPrev, misc error after ScrPrev execution"));return NULL;}
hwnd=FindWindow(_T("Scrprev"),NULL);
}
if (hwnd==NULL) {Debug(_T("Unable to find Scrprev window")); return NULL;}
::SetForegroundWindow(hwnd);
hwnd=GetWindow(hwnd,GW_CHILD);
if (hwnd==NULL) {Debug(_T("Couldn't find Scrprev child")); return NULL;}
return hwnd;
}
void DoInstall()
{ TCHAR windir[MAX_PATH]; GetWindowsDirectory(windir,MAX_PATH);
TCHAR tfn[MAX_PATH]; UINT ures=GetTempFileName(windir,_T("pst"),0,tfn);
if (ures==0) {MessageBox(NULL,_T("You must be logged on as system administrator to install screen savers"),_T("Saver Install"),MB_ICONINFORMATION|MB_OK); return;}
DeleteFile(tfn);
tstring fn=tstring(windir)+_T("\\")+SaverName+_T(".scr");
DWORD attr = GetFileAttributes(fn.c_str());
bool exists = (attr!=0xFFFFFFFF);
tstring msg=_T("Do you want to install '")+SaverName+_T("' ?");
if (exists) msg+=_T("\r\n\r\n(This will replace the version that you have currently)");
int res=MessageBox(NULL,msg.c_str(),_T("Saver Install"),MB_YESNOCANCEL);
if (res!=IDYES) return;
TCHAR cfn[MAX_PATH]; GetModuleFileName(hInstance,cfn,MAX_PATH);
SetCursor(LoadCursor(NULL,IDC_WAIT));
BOOL bres = CopyFile(cfn,fn.c_str(),FALSE);
if (!bres)
{ tstring msg = _T("There was an error installing the saver.\r\n\r\n\"")+GetLastErrorString()+_T("\"");
MessageBox(NULL,msg.c_str(),_T("Saver Install"),MB_ICONERROR|MB_OK);
SetCursor(LoadCursor(NULL,IDC_ARROW));
return;
}
LONG lres; HKEY skey; DWORD disp; tstring val;
tstring key=REGSTR_PATH_UNINSTALL _T("\\")+SaverName;
lres=RegCreateKeyEx(HKEY_LOCAL_MACHINE,key.c_str(),0,NULL,REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS,NULL,&skey,&disp);
if (lres==ERROR_SUCCESS)
{ val=SaverName+_T(" saver"); RegSetValueEx(skey,_T("DisplayName"),0,REG_SZ,(const BYTE*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
val=_T("\"")+fn+_T("\" /u"); RegSetValueEx(skey,_T("UninstallString"),0,REG_SZ,(const BYTE*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
RegSetValueEx(skey,_T("UninstallPath"),0,REG_SZ,(const BYTE*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
val=_T("\"")+fn+_T("\""); RegSetValueEx(skey,_T("ModifyPath"),0,REG_SZ,(const BYTE*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
val=fn; RegSetValueEx(skey,_T("DisplayIcon"),0,REG_SZ,(const BYTE*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
TCHAR url[1024]; int ures=LoadString(hInstance,2,url,1024); if (ures!=0) RegSetValueEx(skey,_T("HelpLink"),0,REG_SZ,(const BYTE*)url,sizeof(TCHAR)*(_tcslen(url)+1));
RegCloseKey(skey);
}
SHELLEXECUTEINFO sex; ZeroMemory(&sex,sizeof(sex)); sex.cbSize=sizeof(sex);
sex.fMask=SEE_MASK_NOCLOSEPROCESS;
sex.lpVerb=_T("install");
sex.lpFile=fn.c_str();
sex.nShow=SW_SHOWNORMAL;
bres = ShellExecuteEx(&sex);
if (!bres) {SetCursor(LoadCursor(NULL,IDC_ARROW)); MessageBox(NULL,_T("The saver has been installed"),SaverName.c_str(),MB_OK); return;}
WaitForInputIdle(sex.hProcess,2000);
CloseHandle(sex.hProcess);
SetCursor(LoadCursor(NULL,IDC_ARROW));
}
void DoUninstall()
{ tstring key=REGSTR_PATH_UNINSTALL _T("\\")+SaverName;
RegDeleteKey(HKEY_LOCAL_MACHINE,key.c_str());
TCHAR fn[MAX_PATH]; GetModuleFileName(hInstance,fn,MAX_PATH);
SetFileAttributes(fn,FILE_ATTRIBUTE_NORMAL); // remove readonly if necessary
BOOL res = MoveFileEx(fn,NULL,MOVEFILE_DELAY_UNTIL_REBOOT);
//
const TCHAR *c=fn, *lastslash=c;
while (*c!=0) {if (*c=='\\' || *c=='/') lastslash=c+1; c++;}
tstring cap=SaverName+_T(" uninstaller");
tstring msg;
if (res) msg=_T("Uninstall completed. The saver will be removed next time you reboot.");
else msg=_T("There was a problem uninstalling.\r\n")
_T("To complete the uninstall manually, you should go into your Windows ")
_T("directory and delete the file '")+tstring(lastslash)+_T("'");
MessageBox(NULL,msg.c_str(),cap.c_str(),MB_OK);
}
// --------------------------------------------------------------------------------
// SetDlgItemUrl(hwnd,IDC_MYSTATIC,"http://www.wischik.com/lu");
// This routine turns a dialog's static text control into an underlined hyperlink.
// You can call it in your WM_INITDIALOG, or anywhere.
// It will also set the text of the control... if you want to change the text
// back, you can just call SetDlgItemText() afterwards.
// --------------------------------------------------------------------------------
void SetDlgItemUrl(HWND hdlg,int id,const TCHAR *url);
// Implementation notes:
// We have to subclass both the static control (to set its cursor, to respond to click)
// and the dialog procedure (to set the font of the static control). Here are the two
// subclasses:
LRESULT CALLBACK UrlCtlProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK UrlDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);
// When the user calls SetDlgItemUrl, then the static-control-subclass is added
// if it wasn't already there, and the dialog-subclass is added if it wasn't
// already there. Both subclasses are removed in response to their respective
// WM_DESTROY messages. Also, each subclass stores a property in its window,
// which is a HGLOBAL handle to a GlobalAlloc'd structure:
typedef struct {TCHAR *url; WNDPROC oldproc; HFONT hf; HBRUSH hb;} TUrlData;
// I'm a miser and only defined a single structure, which is used by both
// the control-subclass and the dialog-subclass. Both of them use 'oldproc' of course.
// The control-subclass only uses 'url' (in response to WM_LBUTTONDOWN),
// and the dialog-subclass only uses 'hf' and 'hb' (in response to WM_CTLCOLORSTATIC)
// There is one sneaky thing to note. We create our underlined font *lazily*.
// Initially, hf is just NULL. But the first time the subclassed dialog received
// WM_CTLCOLORSTATIC, we sneak a peak at the font that was going to be used for
// the control, and we create our own copy of it but including the underline style.
// This way our code works fine on dialogs of any font.
// SetDlgItemUrl: this is the routine that sets up the subclassing.
void SetDlgItemUrl(HWND hdlg,int id,const TCHAR *url)
{ // nb. vc7 has crummy warnings about 32/64bit. My code's perfect! That's why I hide the warnings.
#pragma warning( push )
#pragma warning( disable: 4312 4244 )
// First we'll subclass the edit control
HWND hctl = GetDlgItem(hdlg,id);
SetWindowText(hctl,url);
HGLOBAL hold = (HGLOBAL)GetProp(hctl,_T("href_dat"));
if (hold!=NULL) // if it had been subclassed before, we merely need to tell it the new url
{ TUrlData *ud = (TUrlData*)GlobalLock(hold);
delete[] ud->url;
ud->url=new TCHAR[_tcslen(url)+1]; _tcscpy(ud->url,url);
}
else
{ HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE,sizeof(TUrlData));
TUrlData *ud = (TUrlData*)GlobalLock(hglob);
ud->oldproc = (WNDPROC)GetWindowLong(hctl,GWL_WNDPROC);
ud->url=new TCHAR[_tcslen(url)+1]; _tcscpy(ud->url,url);
ud->hf=0; ud->hb=0;
GlobalUnlock(hglob);
SetProp(hctl,_T("href_dat"),hglob);
SetWindowLong(hctl,GWL_WNDPROC,(LONG)UrlCtlProc);
}
//
// Second we subclass the dialog
hold = (HGLOBAL)GetProp(hdlg,_T("href_dlg"));
if (hold==NULL)
{ HGLOBAL hglob = GlobalAlloc(GMEM_MOVEABLE,sizeof(TUrlData));
TUrlData *ud = (TUrlData*)GlobalLock(hglob);
ud->url=0;
ud->oldproc = (WNDPROC)GetWindowLong(hdlg,GWL_WNDPROC);
ud->hb=CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
ud->hf=0; // the font will be created lazilly, the first time WM_CTLCOLORSTATIC gets called
GlobalUnlock(hglob);
SetProp(hdlg,_T("href_dlg"),hglob);
SetWindowLong(hdlg,GWL_WNDPROC,(LONG)UrlDlgProc);
}
#pragma warning( pop )
}
// UrlCtlProc: this is the subclass procedure for the static control
LRESULT CALLBACK UrlCtlProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ HGLOBAL hglob = (HGLOBAL)GetProp(hwnd,_T("href_dat"));
if (hglob==NULL) return DefWindowProc(hwnd,msg,wParam,lParam);
TUrlData *oud=(TUrlData*)GlobalLock(hglob); TUrlData ud=*oud;
GlobalUnlock(hglob); // I made a copy of the structure just so I could GlobalUnlock it now, to be more local in my code
switch (msg)
{ case WM_DESTROY:
{ RemoveProp(hwnd,_T("href_dat")); GlobalFree(hglob);
if (ud.url!=0) delete[] ud.url;
// nb. remember that ud.url is just a pointer to a memory block. It might look weird
// for us to delete ud.url instead of oud->url, but they're both equivalent.
} break;
case WM_LBUTTONDOWN:
{ HWND hdlg=GetParent(hwnd); if (hdlg==0) hdlg=hwnd;
ShellExecute(hdlg,_T("open"),ud.url,NULL,NULL,SW_SHOWNORMAL);
} break;
case WM_SETCURSOR:
{ HCURSOR hc=LoadCursor(NULL,MAKEINTRESOURCE(32649)); // =IDC_HAND
if (hc==0) hc=LoadCursor(NULL,IDC_ARROW); // systems before Win2k didn't have the hand
SetCursor(hc);
return TRUE;
}
case WM_NCHITTEST:
{ return HTCLIENT; // because normally a static returns HTTRANSPARENT, so disabling WM_SETCURSOR
}
}
return CallWindowProc(ud.oldproc,hwnd,msg,wParam,lParam);
}
// UrlDlgProc: this is the subclass procedure for the dialog
LRESULT CALLBACK UrlDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{ HGLOBAL hglob = (HGLOBAL)GetProp(hwnd,_T("href_dlg"));
if (hglob==NULL) return DefWindowProc(hwnd,msg,wParam,lParam);
TUrlData *oud=(TUrlData*)GlobalLock(hglob); TUrlData ud=*oud;
GlobalUnlock(hglob);
switch (msg)
{ case WM_DESTROY:
{ RemoveProp(hwnd,_T("href_dlg")); GlobalFree(hglob);
if (ud.hb!=0) DeleteObject(ud.hb);
if (ud.hf!=0) DeleteObject(ud.hf);
} break;
case WM_CTLCOLORSTATIC:
{ HDC hdc=(HDC)wParam; HWND hctl=(HWND)lParam;
// To check whether to handle this control, we look for its subclassed property!
HANDLE hprop=GetProp(hctl,_T("href_dat")); if (hprop==NULL) return CallWindowProc(ud.oldproc,hwnd,msg,wParam,lParam);
// There has been a lot of faulty discussion in the newsgroups about how to change
// the text colour of a static control. Lots of people mess around with the
// TRANSPARENT text mode. That is incorrect. The correct solution is here:
// (1) Leave the text opaque. This will allow us to re-SetDlgItemText without it looking wrong.
// (2) SetBkColor. This background colour will be used underneath each character cell.
// (3) return HBRUSH. This background colour will be used where there's no text.
SetTextColor(hdc,RGB(0,0,255));
SetBkColor(hdc,GetSysColor(COLOR_BTNFACE));
if (ud.hf==0)
{ // we use lazy creation of the font. That's so we can see font was currently being used.
TEXTMETRIC tm; GetTextMetrics(hdc,&tm);
LOGFONT lf;
lf.lfHeight=tm.tmHeight;
lf.lfWidth=0;
lf.lfEscapement=0;
lf.lfOrientation=0;
lf.lfWeight=tm.tmWeight;
lf.lfItalic=tm.tmItalic;
lf.lfUnderline=TRUE;
lf.lfStrikeOut=tm.tmStruckOut;
lf.lfCharSet=tm.tmCharSet;
lf.lfOutPrecision=OUT_DEFAULT_PRECIS;
lf.lfClipPrecision=CLIP_DEFAULT_PRECIS;
lf.lfQuality=DEFAULT_QUALITY;
lf.lfPitchAndFamily=tm.tmPitchAndFamily;
GetTextFace(hdc,LF_FACESIZE,lf.lfFaceName);
ud.hf=CreateFontIndirect(&lf);
TUrlData *oud = (TUrlData*)GlobalLock(hglob); oud->hf=ud.hf; GlobalUnlock(hglob);
}
SelectObject(hdc,ud.hf);
// Note: the win32 docs say to return an HBRUSH, typecast as a BOOL. But they
// fail to explain how this will work in 64bit windows where an HBRUSH is 64bit.
// I have supressed the warnings for now, because I hate them...
#pragma warning( push )
#pragma warning( disable: 4311 )
return (BOOL)ud.hb;
#pragma warning( pop )
}
}
return CallWindowProc(ud.oldproc,hwnd,msg,wParam,lParam);
}
inline void Debug(const tstring s)
{ if (DebugFile==_T("")) return;
if (DebugFile==_T("OutputDebugString")) {tstring err=s+_T("\r\n"); OutputDebugString(err.c_str());}
else {FILE *f = _tfopen(DebugFile.c_str(),_T("a+t")); _ftprintf(f,_T("%s\n"),s.c_str()); fclose(f);}
}
tstring GetLastErrorString()
{ LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,
GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
tstring s((TCHAR*)lpMsgBuf);
LocalFree( lpMsgBuf );
return s;
}
void RegSave(const tstring name,DWORD type,void*buf,int size)
{ tstring path = _T("Software\\Scrplus\\")+SaverName;
HKEY skey; LONG res=RegCreateKeyEx(HKEY_CURRENT_USER,path.c_str(),0,0,0,KEY_ALL_ACCESS,0,&skey,0);
if (res!=ERROR_SUCCESS) return;
RegSetValueEx(skey,name.c_str(),0,type,(const BYTE*)buf,size);
RegCloseKey(skey);
}
bool RegLoadDword(const tstring name,DWORD *buf)
{ tstring path = _T("Software\\Scrplus\\")+SaverName;
HKEY skey; LONG res=RegOpenKeyEx(HKEY_CURRENT_USER,path.c_str(),0,KEY_READ,&skey);
if (res!=ERROR_SUCCESS) return false;
DWORD size=sizeof(DWORD);
res=RegQueryValueEx(skey,name.c_str(),0,0,(LPBYTE)buf,&size);
RegCloseKey(skey);
return (res==ERROR_SUCCESS);
}
void RegSave(const tstring name,int val)
{ DWORD v=val; RegSave(name,REG_DWORD,&v,sizeof(v));
}
void RegSave(const tstring name,bool val)
{ RegSave(name,val?1:0);
}
void RegSave(const tstring name,tstring val)
{ RegSave(name,REG_SZ,(void*)val.c_str(),sizeof(TCHAR)*(val.length()+1));
}
int RegLoad(const tstring name,int def)
{ DWORD val; bool res=RegLoadDword(name,&val);
return res?val:def;
}
bool RegLoad(const tstring name,bool def)
{ int b=RegLoad(name,def?1:0); return (b!=0);
}
tstring RegLoad(const tstring name,tstring def)
{ tstring path = _T("Software\\Scrplus\\")+SaverName;
HKEY skey; LONG res=RegOpenKeyEx(HKEY_CURRENT_USER,path.c_str(),0,KEY_READ,&skey);
if (res!=ERROR_SUCCESS) return def;
DWORD size=0; res=RegQueryValueEx(skey,name.c_str(),0,0,0,&size);
if (res!=ERROR_SUCCESS) {RegCloseKey(skey); return def;}
TCHAR *buf = new TCHAR[size];
RegQueryValueEx(skey,name.c_str(),0,0,(LPBYTE)buf,&size);
tstring s(buf); delete[] buf;
RegCloseKey(skey);
return s;
}
int WINAPI WinMain(HINSTANCE h,HINSTANCE,LPSTR,int)
{ hInstance=h;
TCHAR name[MAX_PATH]; int sres=LoadString(hInstance,1,name,MAX_PATH);
if (sres==0) {MessageBox(NULL,_T("Must store saver name as String Resource 1"),_T("Saver"),MB_ICONERROR|MB_OK);return 0;}
SaverName=name;
//
TCHAR mod[MAX_PATH]; GetModuleFileName(hInstance,mod,MAX_PATH); tstring smod(mod);
bool isexe = (smod.find(_T(".exe"))!=tstring::npos || smod.find(_T(".EXE"))!=tstring::npos);
//
TCHAR *c=GetCommandLine();
if (*c=='\"') {c++; while (*c!=0 && *c!='\"') c++; if (*c=='\"') c++;} else {while (*c!=0 && *c!=' ') c++;}
while (*c==' ') c++;
HWND hwnd=NULL; bool fakemulti=false;
if (*c==0) {if (isexe) ScrMode=smInstall; else ScrMode=smConfig; hwnd=NULL;}
else
{ if (*c=='-' || *c=='/') c++;
if (*c=='u' || *c=='U') ScrMode=smUninstall;
if (*c=='p' || *c=='P' || *c=='l' || *c=='L')
{ c++; while (*c==' ' || *c==':') c++;
if (_tcsicmp(c,_T("scrprev"))==0) hwnd=CheckForScrprev(); else hwnd=(HWND)_ttoi(c);
ScrMode=smPreview;
}
else if (*c=='s' || *c=='S') {ScrMode=smSaver; fakemulti=(c[1]=='m'||c[1]=='M');}
else if (*c=='c' || *c=='C') {c++; while (*c==' ' || *c==':') c++; if (*c==0) hwnd=GetForegroundWindow(); else hwnd=(HWND)_ttoi(c); ScrMode=smConfig;}
else if (*c=='a' || *c=='A') {c++; while (*c==' ' || *c==':') c++; hwnd=(HWND)_ttoi(c); ScrMode=smPassword;}
}
//
if (ScrMode==smInstall) {DoInstall(); return 0;}
if (ScrMode==smUninstall) {DoUninstall(); return 0;}
if (ScrMode==smPassword) {ChangePassword(hwnd); return 0;}
//
ReadGeneralRegistry();
//
INITCOMMONCONTROLSEX icx; ZeroMemory(&icx,sizeof(icx));
icx.dwSize=sizeof(icx);
icx.dwICC=ICC_UPDOWN_CLASS;
InitCommonControlsEx(&icx);
//
WNDCLASS wc; ZeroMemory(&wc,sizeof(wc));
wc.hInstance=hInstance;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpszClassName=_T("ScrMonitor");
wc.lpfnWndProc=MonitorWndProc;
RegisterClass(&wc);
//
wc.lpfnWndProc=SaverWndProc;
wc.cbWndExtra=8;
wc.lpszClassName=_T("ScrClass");
RegisterClass(&wc);
//
if (ScrMode==smConfig) DoConfig(hwnd);
else if (ScrMode==smSaver || ScrMode==smPreview) DoSaver(hwnd,fakemulti);
//
return 0;
}