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