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