[BROWSEUI] SHExplorerParseCmdLine: Fix parsing of /root (#6752)
[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 /* 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 if (Session->hUserActivity)
181 CloseHandle(Session->hUserActivity);
182
183 if (Session->hEndOfScreenSaver)
184 CloseHandle(Session->hEndOfScreenSaver);
185
186 RevertToSelf();
187
188 #ifndef USE_GETLASTINPUTINFO
189 if (Session->KeyboardHook)
190 UnhookWindowsHookEx(Session->KeyboardHook);
191
192 if (Session->MouseHook)
193 UnhookWindowsHookEx(Session->MouseHook);
194 #endif
195
196 CloseHandle(Session->hEndOfScreenSaverThread);
197 CloseHandle(Session->hScreenSaverParametersChanged);
198
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 %lu\n", rc);
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 %lu\n", rc);
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 if (rc != ERROR_FILE_NOT_FOUND)
306 ERR("WL: RegQueryValueEx error %lu\n", rc);
307 goto cleanup;
308 }
309
310 if (bufferSize == 0)
311 {
312 ERR("WL: Buffer size is NULL!\n");
313 goto cleanup;
314 }
315
316 szApplicationName[bufferSize / sizeof(WCHAR)] = 0; /* Terminate the string */
317
318 if (wcslen(szApplicationName) == 0)
319 {
320 ERR("WL: Application Name length is zero!\n");
321 goto cleanup;
322 }
323
324 wsprintfW(szCommandLine, L"%s /s", szApplicationName);
325 TRACE("WL: Executing %S\n", szCommandLine);
326
327 ZeroMemory(&StartupInfo, sizeof(STARTUPINFOW));
328 ZeroMemory(&ProcessInformation, sizeof(PROCESS_INFORMATION));
329 StartupInfo.cb = sizeof(STARTUPINFOW);
330 StartupInfo.dwFlags = STARTF_SCREENSAVER;
331
332 /* FIXME: Run the screen saver on the secure screen saver desktop if required */
333 StartupInfo.lpDesktop = L"WinSta0\\Default";
334
335 ret = CreateProcessW(szApplicationName,
336 szCommandLine,
337 NULL,
338 NULL,
339 FALSE,
340 0,
341 NULL,
342 NULL,
343 &StartupInfo,
344 &ProcessInformation);
345 if (!ret)
346 {
347 ERR("WL: Unable to start %S, error %lu\n", szApplicationName, GetLastError());
348 goto cleanup;
349 }
350
351 CloseHandle(ProcessInformation.hThread);
352
353 SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, TRUE, NULL, 0);
354
355 CallNotificationDlls(Session, StartScreenSaverHandler);
356
357 /* Wait the end of the process or some other activity */
358 ResetEvent(Session->hUserActivity);
359 HandleArray[0] = ProcessInformation.hProcess;
360 HandleArray[1] = Session->hUserActivity;
361 Status = WaitForMultipleObjects(2, HandleArray, FALSE, INFINITE);
362 if (Status == WAIT_OBJECT_0 + 1)
363 {
364 /* Kill the screen saver */
365 TerminateProcess(ProcessInformation.hProcess, 0);
366 }
367
368 SetEvent(Session->hEndOfScreenSaver);
369
370 CloseHandle(ProcessInformation.hProcess);
371
372 CallNotificationDlls(Session, StopScreenSaverHandler);
373
374 cleanup:
375 if (hKey)
376 RegCloseKey(hKey);
377
378 if (hCurrentUser)
379 RegCloseKey(hCurrentUser);
380
381 RevertToSelf();
382
383 if (!ret)
384 {
385 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_ACTIVITY, 0);
386 #ifndef USE_GETLASTINPUTINFO
387 InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount());
388 #endif
389 }
390 }