xref: /reactos/sdk/lib/scrnsave/scrnsave.c (revision c2c66aff)
1 /*
2  * PROJECT:         ReactOS Screen Saver Library
3  * LICENSE:         GPL v2 or any later version
4  * FILE:            lib/sdk/scrnsave/scrnsave.c
5  * PURPOSE:         Library for writing screen savers, compatible with
6  *                  MS' scrnsave.lib without Win9x support.
7  * PROGRAMMERS:     Anders Norlander <anorland@hem2.passagen.se>
8  *                  Colin Finck <mail@colinfinck.de>
9  */
10 
11 #include <stdarg.h>
12 #include <windef.h>
13 #include <winbase.h>
14 #include <wingdi.h>
15 #include <winuser.h>
16 #include <tchar.h>
17 #include <scrnsave.h>
18 
19 // Screen Saver window class
20 #define CLASS_SCRNSAVE TEXT("WindowsScreenSaverClass")
21 
22 // Globals
23 HWND        hMainWindow = NULL;
24 BOOL        fChildPreview = FALSE;
25 HINSTANCE   hMainInstance;
26 TCHAR       szName[TITLEBARNAMELEN];
27 TCHAR       szAppName[APPNAMEBUFFERLEN];
28 TCHAR       szIniFile[MAXFILELEN];
29 TCHAR       szScreenSaver[22];
30 TCHAR       szHelpFile[MAXFILELEN];
31 TCHAR       szNoHelpMemory[BUFFLEN];
32 UINT        MyHelpMessage;
33 
34 // Local house keeping
35 static POINT pt_orig;
36 
37 static int ISSPACE(TCHAR c)
38 {
39     return (c == ' ' || c == '\t');
40 }
41 
42 #define ISNUM(c) ((c) >= '0' && (c) <= '9')
43 
44 static ULONG_PTR _toulptr(const TCHAR *s)
45 {
46     ULONG_PTR res;
47     ULONG_PTR n;
48     const TCHAR *p;
49 
50     for (p = s; *p; p++)
51         if (!ISNUM(*p))
52             break;
53 
54     p--;
55     res = 0;
56 
57     for (n = 1; p >= s; p--, n *= 10)
58         res += (*p - '0') * n;
59 
60     return res;
61 }
62 
63 // This function takes care of *must* do tasks, like terminating screen saver
64 static LRESULT WINAPI SysScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
65 {
66     switch(uMsg)
67     {
68         case WM_CREATE:
69             // Mouse is not supposed to move from this position
70             GetCursorPos(&pt_orig);
71             break;
72 
73         case WM_DESTROY:
74             PostQuitMessage(0);
75             break;
76 
77         case WM_SYSCOMMAND:
78             if (!fChildPreview)
79             {
80                 switch (wParam)
81                 {
82                     case SC_CLOSE:      // - Closing the screen saver, or...
83                     case SC_NEXTWINDOW: // - Switching to
84                     case SC_PREVWINDOW: //   different windows, or...
85                     case SC_SCREENSAVE: // - Starting another screen saver:
86                         return FALSE;   // Fail it!
87                 }
88             }
89             break;
90     }
91 
92     return ScreenSaverProc(hWnd, uMsg, wParam, lParam);
93 }
94 
95 LRESULT WINAPI DefScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
96 {
97     // Don't do any special processing when in preview mode
98     if (fChildPreview)
99         return DefWindowProc(hWnd, uMsg, wParam, lParam);
100 
101     switch (uMsg)
102     {
103         case WM_NCACTIVATE:
104         case WM_ACTIVATE:
105         case WM_ACTIVATEAPP:
106             if (!wParam)
107             {
108                 // wParam is FALSE, so the screen saver is losing the focus.
109                 PostMessage(hWnd, WM_CLOSE, 0, 0);
110             }
111             break;
112 
113         case WM_MOUSEMOVE:
114         {
115             POINT pt;
116             GetCursorPos(&pt);
117             // TODO: Implement mouse move threshold. See:
118             // http://svn.reactos.org/svn/reactos/trunk/rosapps/applications/screensavers/starfield/screensaver.c?r1=67455&r2=67454&pathrev=67455
119             if (pt.x == pt_orig.x && pt.y == pt_orig.y)
120                 break;
121 
122             // Fall through
123         }
124 
125         case WM_LBUTTONDOWN:
126         case WM_MBUTTONDOWN:
127         case WM_RBUTTONDOWN:
128         case WM_XBUTTONDOWN:
129         case WM_KEYDOWN:
130         case WM_KEYUP:
131         case WM_SYSKEYDOWN:
132             // Send a WM_CLOSE to close the screen saver (allows
133             // the screen saver to perform clean-up tasks)
134             PostMessage(hWnd, WM_CLOSE, 0, 0);
135             break;
136 
137         case WM_SETCURSOR:
138             SetCursor(NULL);
139             return TRUE;
140     }
141 
142     return DefWindowProc(hWnd, uMsg, wParam, lParam);
143 }
144 
145 // Registers the screen saver window class
146 static BOOL RegisterScreenSaverClass(void)
147 {
148     WNDCLASS cls;
149 
150     cls.hCursor       = NULL;
151     cls.hIcon         = LoadIcon(hMainInstance, MAKEINTATOM(ID_APP));
152     cls.lpszMenuName  = NULL;
153     cls.lpszClassName = CLASS_SCRNSAVE;
154     cls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
155     cls.hInstance     = hMainInstance;
156     cls.style         = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_SAVEBITS | CS_PARENTDC;
157     cls.lpfnWndProc   = SysScreenSaverProc;
158     cls.cbWndExtra    = 0;
159     cls.cbClsExtra    = 0;
160 
161     return (RegisterClass(&cls) != 0);
162 }
163 
164 static int LaunchConfig(HWND hParent)
165 {
166     // Only show the dialog if the RegisterDialogClasses function succeeded.
167     // This is the same behaviour as MS' scrnsave.lib.
168     if (!RegisterDialogClasses(hMainInstance))
169         return -1;
170 
171     return DialogBox(hMainInstance, MAKEINTRESOURCE(DLG_SCRNSAVECONFIGURE),
172                      hParent, (DLGPROC)ScreenSaverConfigureDialog);
173 }
174 
175 static int LaunchScreenSaver(HWND hParent)
176 {
177     LPCTSTR lpWindowName;
178     UINT style, exstyle;
179     RECT rc;
180     MSG msg;
181 
182     if (!RegisterScreenSaverClass())
183     {
184         MessageBox(NULL, TEXT("RegisterClass() failed"), NULL, MB_ICONHAND);
185         return -1;
186     }
187 
188     // A slightly different approach needs to be used when displaying in a preview window
189     if (hParent)
190     {
191         fChildPreview = TRUE;
192         lpWindowName  = TEXT("Preview");
193 
194         style   = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN;
195         exstyle = 0;
196 
197         GetClientRect(hParent, &rc);
198         rc.left = 0;
199         rc.top  = 0;
200     }
201     else
202     {
203         fChildPreview = FALSE;
204         lpWindowName  = TEXT("Screen Saver");
205 
206         style   = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
207         exstyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
208 
209         // Get the left & top side coordinates of the virtual screen
210         rc.left   = GetSystemMetrics(SM_XVIRTUALSCREEN);
211         rc.top    = GetSystemMetrics(SM_YVIRTUALSCREEN);
212         // Get the width and height of the virtual screen
213         rc.right  = GetSystemMetrics(SM_CXVIRTUALSCREEN);
214         rc.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
215     }
216 
217     // Create the main screen saver window
218     hMainWindow = CreateWindowEx(exstyle, CLASS_SCRNSAVE, lpWindowName, style,
219                                  rc.left, rc.top, rc.right, rc.bottom,
220                                  hParent, NULL, hMainInstance, NULL);
221     if (!hMainWindow)
222         return -1;
223 
224     // Display window and start pumping messages
225     ShowWindow(hMainWindow, SW_SHOW);
226     if (!hParent)
227         SetCursor(NULL);
228 
229     while (GetMessage(&msg, NULL, 0, 0))
230     {
231         TranslateMessage(&msg);
232         DispatchMessage(&msg);
233     }
234 
235     return msg.wParam;
236 }
237 
238 // Screen Saver entry point
239 int APIENTRY _tWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPTSTR CmdLine, int nCmdShow)
240 {
241     LPTSTR p;
242 
243     UNREFERENCED_PARAMETER(nCmdShow);
244     UNREFERENCED_PARAMETER(hPrevInst);
245 
246     hMainInstance = hInst;
247 
248     // Parse the arguments:
249     //   -a <hwnd>  (Change the password; only for Win9x, unused on WinNT)
250     //   -s         (Run the screensaver)
251     //   -p <hwnd>  (Preview)
252     //   -c <hwnd>  (Configure)
253     for (p = CmdLine; *p; p++)
254     {
255         switch (*p)
256         {
257             case 'S':
258             case 's':
259                 // Start the screen saver
260                 return LaunchScreenSaver(NULL);
261 
262             case 'P':
263             case 'p':
264             {
265                 HWND hParent;
266 
267                 while (ISSPACE(*++p));
268                 hParent = (HWND)_toulptr(p);
269 
270                 // Start the screen saver in preview mode
271                 if (hParent && IsWindow(hParent))
272                     return LaunchScreenSaver(hParent);
273                 else
274                     return -1;
275             }
276 
277             case 'C':
278             case 'c':
279             {
280                 HWND hParent;
281 
282                 if (p[1] == ':')
283                     hParent = (HWND)_toulptr(p + 2);
284                 else
285                     hParent = GetForegroundWindow();
286 
287                 // Display the configuration dialog
288                 if (hParent && IsWindow(hParent))
289                     return LaunchConfig(hParent);
290                 else
291                     return -1;
292             }
293 
294             case '-':
295             case '/':
296             case ' ':
297             default:
298                 break;
299         }
300     }
301 
302     return LaunchConfig(NULL);
303 }
304