[WINLOGON]
[reactos.git] / reactos / base / system / winlogon / screensaver.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Winlogon
4 * FILE: base/system/winlogon/screensaver.c
5 * PURPOSE: Screen saver management
6 * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #include "winlogon.h"
12
13 /* FUNCTIONS ****************************************************************/
14
15 #ifndef USE_GETLASTINPUTINFO
16 static
17 LRESULT
18 CALLBACK
19 KeyboardActivityProc(
20 IN INT nCode,
21 IN WPARAM wParam,
22 IN LPARAM lParam)
23 {
24 InterlockedExchange((LONG*)&WLSession->LastActivity, ((PKBDLLHOOKSTRUCT)lParam)->time);
25 return CallNextHookEx(NULL, nCode, wParam, lParam);
26 }
27
28
29 static
30 LRESULT
31 CALLBACK
32 MouseActivityProc(
33 IN INT nCode,
34 IN WPARAM wParam,
35 IN LPARAM lParam)
36 {
37 InterlockedExchange((LONG*)&WLSession->LastActivity, ((PMSLLHOOKSTRUCT)lParam)->time);
38 return CallNextHookEx(NULL, nCode, wParam, lParam);
39 }
40 #endif
41
42
43 static
44 VOID
45 LoadScreenSaverParameters(
46 OUT LPDWORD Timeout)
47 {
48 BOOL Enabled;
49
50 if (!SystemParametersInfoW(SPI_GETSCREENSAVETIMEOUT, 0, Timeout, 0))
51 {
52 WARN("WL: Unable to get screen saver timeout (error %lu). Disabling it\n", GetLastError());
53 *Timeout = INFINITE;
54 }
55 else if (!SystemParametersInfoW(SPI_GETSCREENSAVEACTIVE, 0, &Enabled, 0))
56 {
57 WARN("WL: Unable to check if screen saver is enabled (error %lu). Disabling it\n", GetLastError());
58 *Timeout = INFINITE;
59 }
60 else if (!Enabled)
61 {
62 TRACE("WL: Screen saver is disabled\n");
63 *Timeout = INFINITE;
64 }
65 else
66 {
67 TRACE("WL: Screen saver timeout: %lu seconds\n", *Timeout);
68 *Timeout *= 1000;
69 }
70 }
71
72
73 static
74 DWORD
75 WINAPI
76 ScreenSaverThreadMain(
77 IN LPVOID lpParameter)
78 {
79 PWLSESSION Session = (PWLSESSION)lpParameter;
80 HANDLE HandleArray[3];
81 #ifdef USE_GETLASTINPUTINFO
82 LASTINPUTINFO lastInputInfo;
83 #else
84 DWORD LastActivity;
85 #endif
86 DWORD TimeToWait;
87 DWORD Timeout; /* Timeout before screen saver starts, in milliseconds */
88 DWORD ret;
89
90 if (!ImpersonateLoggedOnUser(Session->UserToken))
91 {
92 ERR("ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
93 return 0;
94 }
95
96 Session->hUserActivity = CreateEventW(NULL, FALSE, FALSE, NULL);
97 if (!Session->hUserActivity)
98 {
99 ERR("WL: Unable to create event (error %lu)\n", GetLastError());
100 goto cleanup;
101 }
102
103 Session->hEndOfScreenSaver = CreateEventW(NULL, FALSE, FALSE, NULL);
104 if (!Session->hEndOfScreenSaver)
105 {
106 ERR("WL: Unable to create event (error %lu)\n", GetLastError());
107 goto cleanup;
108 }
109
110 HandleArray[0] = Session->hEndOfScreenSaverThread;
111 HandleArray[1] = Session->hScreenSaverParametersChanged;
112 HandleArray[2] = Session->hEndOfScreenSaver;
113
114 LoadScreenSaverParameters(&Timeout);
115
116 #ifndef USE_GETLASTINPUTINFO
117 InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount());
118 #else
119 lastInputInfo.cbSize = sizeof(LASTINPUTINFO);
120 #endif
121 for (;;)
122 {
123 /* See the time of last activity and calculate a timeout */
124 #ifndef USE_GETLASTINPUTINFO
125 LastActivity = InterlockedCompareExchange((LONG*)&Session->LastActivity, 0, 0);
126 TimeToWait = Timeout - (GetTickCount() - LastActivity);
127 #else
128 if (GetLastInputInfo(&lastInputInfo))
129 TimeToWait = Timeout - (GetTickCount() - lastInputInfo.dwTime);
130 else
131 {
132 WARN("GetLastInputInfo() failed with error %lu\n", GetLastError());
133 TimeToWait = 10; /* Try again in 10 ms */
134 }
135 #endif
136
137 if (TimeToWait > Timeout)
138 {
139 /* GetTickCount() got back to 0 */
140 TimeToWait = Timeout;
141 }
142
143 /* Wait for the timeout, or the end of this thread */
144 ret = WaitForMultipleObjects(2, HandleArray, FALSE, TimeToWait);
145 if (ret == WAIT_OBJECT_0)
146 break;
147 else if (ret == WAIT_OBJECT_0 + 1)
148 LoadScreenSaverParameters(&Timeout);
149
150 /* Check if we didn't had recent activity */
151 #ifndef USE_GETLASTINPUTINFO
152 LastActivity = InterlockedCompareExchange((LONG*)&Session->LastActivity, 0, 0);
153 if (LastActivity + Timeout > GetTickCount())
154 continue;
155 #else
156 if (!GetLastInputInfo(&lastInputInfo))
157 {
158 WARN("GetLastInputInfo() failed with error %lu\n", GetLastError());
159 continue;
160 }
161
162 if (lastInputInfo.dwTime + Timeout > GetTickCount())
163 continue;
164 #endif
165
166 /* Run screen saver */
167 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_TIMEOUT, 0);
168
169 /* Wait for the end of this thread or of the screen saver */
170 ret = WaitForMultipleObjects(3, HandleArray, FALSE, INFINITE);
171 if (ret == WAIT_OBJECT_0)
172 break;
173 else if (ret == WAIT_OBJECT_0 + 1)
174 LoadScreenSaverParameters(&Timeout);
175 else if (ret == WAIT_OBJECT_0 + 2)
176 SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, FALSE, NULL, 0);
177 }
178
179 cleanup:
180 RevertToSelf();
181 if (Session->hUserActivity)
182 CloseHandle(Session->hUserActivity);
183
184 if (Session->hEndOfScreenSaver)
185 CloseHandle(Session->hEndOfScreenSaver);
186
187 #ifndef USE_GETLASTINPUTINFO
188 if (Session->KeyboardHook)
189 UnhookWindowsHookEx(Session->KeyboardHook);
190
191 if (Session->MouseHook)
192 UnhookWindowsHookEx(Session->MouseHook);
193 #endif
194
195 CloseHandle(Session->hEndOfScreenSaverThread);
196 CloseHandle(Session->hScreenSaverParametersChanged);
197 return 0;
198 }
199
200
201 BOOL
202 InitializeScreenSaver(
203 IN OUT PWLSESSION Session)
204 {
205 HANDLE ScreenSaverThread;
206
207 #ifndef USE_GETLASTINPUTINFO
208 /* Register hooks to detect keyboard and mouse activity */
209 Session->KeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardActivityProc, hAppInstance, 0);
210 if (!Session->KeyboardHook)
211 {
212 ERR("WL: Unable to register keyboard hook\n");
213 return FALSE;
214 }
215
216 Session->MouseHook = SetWindowsHookEx(WH_MOUSE_LL, MouseActivityProc, hAppInstance, 0);
217 if (!Session->MouseHook)
218 {
219 ERR("WL: Unable to register mouse hook\n");
220 return FALSE;
221 }
222 #endif
223
224 Session->hScreenSaverParametersChanged = CreateEventW(NULL, FALSE, FALSE, NULL);
225 if (!Session->hScreenSaverParametersChanged)
226 {
227 WARN("WL: Unable to create screen saver event (error %lu)\n", GetLastError());
228 return TRUE;
229 }
230
231 Session->hEndOfScreenSaverThread = CreateEventW(NULL, FALSE, FALSE, NULL);
232 if (!Session->hEndOfScreenSaverThread)
233 {
234 WARN("WL: Unable to create screen saver event (error %lu)\n", GetLastError());
235 CloseHandle(Session->hScreenSaverParametersChanged);
236 return TRUE;
237 }
238
239 ScreenSaverThread = CreateThread(NULL,
240 0,
241 ScreenSaverThreadMain,
242 Session,
243 0,
244 NULL);
245 if (ScreenSaverThread)
246 CloseHandle(ScreenSaverThread);
247 else
248 ERR("WL: Unable to start screen saver thread\n");
249
250 return TRUE;
251 }
252
253
254 VOID
255 StartScreenSaver(
256 IN PWLSESSION Session)
257 {
258 HKEY hKey = NULL, hCurrentUser = NULL;
259 WCHAR szApplicationName[MAX_PATH];
260 WCHAR szCommandLine[MAX_PATH + 3];
261 DWORD bufferSize = sizeof(szApplicationName) - sizeof(WCHAR);
262 DWORD dwType;
263 STARTUPINFOW StartupInfo;
264 PROCESS_INFORMATION ProcessInformation;
265 HANDLE HandleArray[2];
266 LONG rc;
267 DWORD Status;
268 BOOL ret = FALSE;
269
270 if (!ImpersonateLoggedOnUser(Session->UserToken))
271 {
272 ERR("WL: ImpersonateLoggedOnUser() failed with error %lu\n", GetLastError());
273 goto cleanup;
274 }
275
276 rc = RegOpenCurrentUser(KEY_READ,
277 &hCurrentUser);
278 if (rc != ERROR_SUCCESS)
279 {
280 ERR("WL: RegOpenCurrentUser error %lu\n", rc);
281 goto cleanup;
282 }
283
284 rc = RegOpenKeyExW(hCurrentUser,
285 L"Control Panel\\Desktop",
286 0,
287 KEY_QUERY_VALUE,
288 &hKey);
289 if (rc != ERROR_SUCCESS)
290 {
291 ERR("WL: RegOpenKeyEx error %lu\n", rc);
292 goto cleanup;
293 }
294
295 rc = RegQueryValueExW(hKey,
296 L"SCRNSAVE.EXE",
297 0,
298 &dwType,
299 (LPBYTE)szApplicationName,
300 &bufferSize);
301 if (rc != ERROR_SUCCESS || dwType != REG_SZ)
302 {
303 if (rc != ERROR_FILE_NOT_FOUND)
304 ERR("WL: RegQueryValueEx error %lu\n", rc);
305 goto cleanup;
306 }
307
308 if (bufferSize == 0)
309 {
310 ERR("WL: Buffer size is NULL!\n");
311 goto cleanup;
312 }
313
314 szApplicationName[bufferSize / sizeof(WCHAR)] = 0; /* Terminate the string */
315
316 if (wcslen(szApplicationName) == 0)
317 {
318 ERR("WL: Application Name length is zero!\n");
319 goto cleanup;
320 }
321
322 wsprintfW(szCommandLine, L"%s /s", szApplicationName);
323 TRACE("WL: Executing %S\n", szCommandLine);
324
325 ZeroMemory(&StartupInfo, sizeof(STARTUPINFOW));
326 ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
327 StartupInfo.cb = sizeof(STARTUPINFOW);
328 StartupInfo.dwFlags = STARTF_SCRNSAVER;
329
330 /* FIXME: run the screen saver on the screen saver desktop */
331 ret = CreateProcessW(szApplicationName,
332 szCommandLine,
333 NULL,
334 NULL,
335 FALSE,
336 0,
337 NULL,
338 NULL,
339 &StartupInfo,
340 &ProcessInformation);
341 if (!ret)
342 {
343 ERR("WL: Unable to start %S, error %lu\n", szApplicationName, GetLastError());
344 goto cleanup;
345 }
346
347 CloseHandle(ProcessInformation.hThread);
348
349 SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, TRUE, NULL, 0);
350
351 /* Wait the end of the process or some other activity */
352 ResetEvent(Session->hUserActivity);
353 HandleArray[0] = ProcessInformation.hProcess;
354 HandleArray[1] = Session->hUserActivity;
355 Status = WaitForMultipleObjects(2, HandleArray, FALSE, INFINITE);
356 if (Status == WAIT_OBJECT_0 + 1)
357 {
358 /* Kill the screen saver */
359 TerminateProcess(ProcessInformation.hProcess, 0);
360 }
361
362 SetEvent(Session->hEndOfScreenSaver);
363
364 CloseHandle(ProcessInformation.hProcess);
365
366 cleanup:
367 RevertToSelf();
368 if (hKey)
369 RegCloseKey(hKey);
370
371 if (hCurrentUser)
372 RegCloseKey(hCurrentUser);
373
374 if (!ret)
375 {
376 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_ACTIVITY, 0);
377 #ifndef USE_GETLASTINPUTINFO
378 InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount());
379 #endif
380 }
381 }