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