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_SCRNSAVER; 331 332 /* FIXME: run the screen saver on the screen saver desktop */ 333 ret = CreateProcessW(szApplicationName, 334 szCommandLine, 335 NULL, 336 NULL, 337 FALSE, 338 0, 339 NULL, 340 NULL, 341 &StartupInfo, 342 &ProcessInformation); 343 if (!ret) 344 { 345 ERR("WL: Unable to start %S, error %lu\n", szApplicationName, GetLastError()); 346 goto cleanup; 347 } 348 349 CloseHandle(ProcessInformation.hThread); 350 351 SystemParametersInfoW(SPI_SETSCREENSAVERRUNNING, TRUE, NULL, 0); 352 353 CallNotificationDlls(Session, StartScreenSaverHandler); 354 355 /* Wait the end of the process or some other activity */ 356 ResetEvent(Session->hUserActivity); 357 HandleArray[0] = ProcessInformation.hProcess; 358 HandleArray[1] = Session->hUserActivity; 359 Status = WaitForMultipleObjects(2, HandleArray, FALSE, INFINITE); 360 if (Status == WAIT_OBJECT_0 + 1) 361 { 362 /* Kill the screen saver */ 363 TerminateProcess(ProcessInformation.hProcess, 0); 364 } 365 366 SetEvent(Session->hEndOfScreenSaver); 367 368 CloseHandle(ProcessInformation.hProcess); 369 370 CallNotificationDlls(Session, StopScreenSaverHandler); 371 372 cleanup: 373 if (hKey) 374 RegCloseKey(hKey); 375 376 if (hCurrentUser) 377 RegCloseKey(hCurrentUser); 378 379 RevertToSelf(); 380 381 if (!ret) 382 { 383 PostMessageW(Session->SASWindow, WLX_WM_SAS, WLX_SAS_TYPE_SCRNSVR_ACTIVITY, 0); 384 #ifndef USE_GETLASTINPUTINFO 385 InterlockedExchange((LONG*)&Session->LastActivity, GetTickCount()); 386 #endif 387 } 388 } 389