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