/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2002 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "CMSWindowsScreenSaver.h" #include "CThread.h" #include "CLog.h" #include "TMethodJob.h" #include "CArch.h" #include #include #if !defined(SPI_GETSCREENSAVERRUNNING) #define SPI_GETSCREENSAVERRUNNING 114 #endif // // CMSWindowsScreenSaver // CMSWindowsScreenSaver::CMSWindowsScreenSaver() : m_process(NULL), m_threadID(0), m_watch(NULL) { // detect OS m_is95Family = false; m_is95 = false; m_isNT = false; OSVERSIONINFO info; info.dwOSVersionInfoSize = sizeof(info); if (GetVersionEx(&info)) { m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS); if (info.dwPlatformId == VER_PLATFORM_WIN32_NT && info.dwMajorVersion <= 4) { m_isNT = true; } else if (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS && info.dwMajorVersion == 4 && info.dwMinorVersion == 0) { m_is95 = true; } } // check if screen saver is enabled SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); } CMSWindowsScreenSaver::~CMSWindowsScreenSaver() { unwatchProcess(); } bool CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam) { // screen saver may have started. look for it and get // the process. if we can't find it then assume it // didn't really start. we wait a moment before // looking to give the screen saver a chance to start. // this shouldn't be a problem since we only get here // if the screen saver wants to kick in, meaning that // the system is idle or the user deliberately started // the screen saver. Sleep(250); // set parameters common to all screen saver handling m_threadID = GetCurrentThreadId(); m_msg = msg; m_wParam = wParam; m_lParam = lParam; // we handle the screen saver differently for the windows // 95 and nt families. if (m_is95Family) { // on windows 95 we wait for the screen saver process // to terminate. get the process. DWORD processID = findScreenSaver(); HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID); if (process == NULL) { // didn't start LOG((CLOG_DEBUG "can't open screen saver process")); return false; } // watch for the process to exit watchProcess(process); } else { // on the windows nt family we wait for the desktop to // change until it's neither the Screen-Saver desktop // nor a desktop we can't open (the login desktop). // since windows will send the request-to-start-screen- // saver message even when the screen saver is disabled // we first check that the screen saver is indeed active // before watching for it to stop. if (!isActive()) { LOG((CLOG_DEBUG "can't open screen saver desktop")); return false; } watchDesktop(); } return true; } void CMSWindowsScreenSaver::enable() { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0); } void CMSWindowsScreenSaver::disable() { SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0); } void CMSWindowsScreenSaver::activate() { // don't activate if already active if (!isActive()) { HWND hwnd = GetForegroundWindow(); if (hwnd != NULL) { PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0); } else { // no foreground window. pretend we got the event instead. DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0); } } } void CMSWindowsScreenSaver::deactivate() { bool killed = false; if (!m_is95Family) { // NT runs screen saver in another desktop HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); if (desktop != NULL) { EnumDesktopWindows(desktop, &CMSWindowsScreenSaver::killScreenSaverFunc, reinterpret_cast(&killed)); CloseDesktop(desktop); } } // if above failed or wasn't tried, try the windows 95 way if (!killed) { // find screen saver window and close it HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); if (hwnd == NULL) { // win2k may use a different class hwnd = FindWindow("Default Screen Saver", NULL); } if (hwnd != NULL) { PostMessage(hwnd, WM_CLOSE, 0, 0); } } // force timer to restart SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, !m_wasEnabled, 0, SPIF_SENDWININICHANGE); SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, SPIF_SENDWININICHANGE); } bool CMSWindowsScreenSaver::isActive() const { if (m_is95) { return (FindWindow("WindowsScreenSaverClass", NULL) != NULL); } else if (m_isNT) { // screen saver runs on a separate desktop HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED); if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) { // desktop doesn't exist so screen saver is not running return false; } // desktop exists. this should indicate that the screen saver // is running but an OS bug can cause a valid handle to be // returned even if the screen saver isn't running (Q230117). // we'll try to enumerate the windows on the desktop and, if // there are any, we assume the screen saver is running. (note // that if we don't have permission to enumerate then we'll // assume that the screen saver is not running.) that'd be // easy enough except there's another OS bug (Q198590) that can // cause EnumDesktopWindows() to enumerate the windows of // another desktop if the requested desktop has no windows. to // work around that we have to verify that the enumerated // windows are, in fact, on the expected desktop. CFindScreenSaverInfo info; info.m_desktop = desktop; info.m_window = NULL; EnumDesktopWindows(desktop, &CMSWindowsScreenSaver::findScreenSaverFunc, reinterpret_cast(&info)); // done with desktop CloseDesktop(desktop); // screen saver is running if a window was found return (info.m_window != NULL); } else { BOOL running; SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0); return (running != FALSE); } } BOOL CALLBACK CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg) { CFindScreenSaverInfo* info = reinterpret_cast(arg); if (info->m_desktop != NULL) { DWORD threadID = GetWindowThreadProcessId(hwnd, NULL); HDESK desktop = GetThreadDesktop(threadID); if (desktop != NULL && desktop != info->m_desktop) { // stop enumerating -- wrong desktop return FALSE; } } // found a window info->m_window = hwnd; // don't need to enumerate further return FALSE; } BOOL CALLBACK CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg) { if (IsWindowVisible(hwnd)) { PostMessage(hwnd, WM_CLOSE, 0, 0); *reinterpret_cast(arg) = true; } return TRUE; } DWORD CMSWindowsScreenSaver::findScreenSaver() { // try windows 95 way HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL); // get process ID of process that owns the window, if found if (hwnd != NULL) { DWORD processID; GetWindowThreadProcessId(hwnd, &processID); return processID; } // not found return 0; } void CMSWindowsScreenSaver::watchDesktop() { // stop watching previous process/desktop unwatchProcess(); // watch desktop in another thread LOG((CLOG_DEBUG "watching screen saver desktop")); m_watch = new CThread(new TMethodJob(this, &CMSWindowsScreenSaver::watchDesktopThread)); } void CMSWindowsScreenSaver::watchProcess(HANDLE process) { // stop watching previous process/desktop unwatchProcess(); // watch new process in another thread if (process != NULL) { LOG((CLOG_DEBUG "watching screen saver process")); m_process = process; m_watch = new CThread(new TMethodJob(this, &CMSWindowsScreenSaver::watchProcessThread)); } } void CMSWindowsScreenSaver::unwatchProcess() { if (m_watch != NULL) { LOG((CLOG_DEBUG "stopped watching screen saver process/desktop")); m_watch->cancel(); m_watch->wait(); delete m_watch; m_watch = NULL; } if (m_process != NULL) { CloseHandle(m_process); m_process = NULL; } } void CMSWindowsScreenSaver::watchDesktopThread(void*) { DWORD reserved = 0; TCHAR* name = NULL; for (;;) { // wait a bit ARCH->sleep(0.2); // get current desktop HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ); if (desk == NULL) { // can't open desktop so keep waiting continue; } // get current desktop name length DWORD size; GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); // allocate more space for the name, if necessary if (size > reserved) { reserved = size; name = (TCHAR*)alloca(reserved + sizeof(TCHAR)); } // get current desktop name GetUserObjectInformation(desk, UOI_NAME, name, size, &size); // compare name to screen saver desktop name if (_tcsicmp(name, TEXT("Screen-saver")) == 0) { // still the screen saver desktop so keep waiting continue; } // send screen saver deactivation message PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } } void CMSWindowsScreenSaver::watchProcessThread(void*) { for (;;) { CThread::testCancel(); if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) { // process terminated LOG((CLOG_DEBUG "screen saver died")); // send screen saver deactivation message PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam); return; } } }