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