Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CMSWindowsScreenSaver.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2002 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "CMSWindowsScreenSaver.h"
00016 #include "CMSWindowsScreen.h"
00017 #include "CThread.h"
00018 #include "CLog.h"
00019 #include "TMethodJob.h"
00020 #include "CArch.h"
00021 #include "CArchMiscWindows.h"
00022 #include <malloc.h>
00023 #include <tchar.h>
00024 
00025 #if !defined(SPI_GETSCREENSAVERRUNNING)
00026 #define SPI_GETSCREENSAVERRUNNING 114
00027 #endif
00028 
00029 static const TCHAR* g_isSecureNT = "ScreenSaverIsSecure";
00030 static const TCHAR* g_isSecure9x = "ScreenSaveUsePassword";
00031 static const TCHAR* const g_pathScreenSaverIsSecure[] = {
00032     "Control Panel",
00033     "Desktop",
00034     NULL
00035 };
00036 
00037 //
00038 // CMSWindowsScreenSaver
00039 //
00040 
00041 CMSWindowsScreenSaver::CMSWindowsScreenSaver() :
00042     m_wasSecure(false),
00043     m_wasSecureAnInt(false),
00044     m_process(NULL),
00045     m_watch(NULL),
00046     m_threadID(0),
00047     m_active(false)
00048 {
00049     // detect OS
00050     m_is95Family = false;
00051     m_is95       = false;
00052     m_isNT       = false;
00053     OSVERSIONINFO info;
00054     info.dwOSVersionInfoSize = sizeof(info);
00055     if (GetVersionEx(&info)) {
00056         m_is95Family = (info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
00057         if (info.dwPlatformId   == VER_PLATFORM_WIN32_NT &&
00058             info.dwMajorVersion <= 4) {
00059             m_isNT = true;
00060         }
00061         else if (info.dwPlatformId  == VER_PLATFORM_WIN32_WINDOWS &&
00062                 info.dwMajorVersion == 4 &&
00063                 info.dwMinorVersion == 0) {
00064             m_is95 = true;
00065         }
00066     }
00067 
00068     // check if screen saver is enabled
00069     SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
00070 }
00071 
00072 CMSWindowsScreenSaver::~CMSWindowsScreenSaver()
00073 {
00074     unwatchProcess();
00075 }
00076 
00077 bool
00078 CMSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam)
00079 {
00080     // if already started then say it didn't just start
00081     if (m_active) {
00082         return false;
00083     }
00084 
00085     // screen saver may have started.  look for it and get
00086     // the process.  if we can't find it then assume it
00087     // didn't really start.  we wait a moment before
00088     // looking to give the screen saver a chance to start.
00089     // this shouldn't be a problem since we only get here
00090     // if the screen saver wants to kick in, meaning that
00091     // the system is idle or the user deliberately started
00092     // the screen saver.
00093     Sleep(250);
00094 
00095     // set parameters common to all screen saver handling
00096     m_threadID = GetCurrentThreadId();
00097     m_msg      = msg;
00098     m_wParam   = wParam;
00099     m_lParam   = lParam;
00100 
00101     // we handle the screen saver differently for the windows
00102     // 95 and nt families.
00103     if (m_is95Family) {
00104         // on windows 95 we wait for the screen saver process
00105         // to terminate.  get the process.
00106         DWORD processID = findScreenSaver();
00107         HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, processID);
00108         if (process == NULL) {
00109             // didn't start
00110             LOG((CLOG_DEBUG2 "can't open screen saver process"));
00111             return false;
00112         }
00113 
00114         // watch for the process to exit
00115         watchProcess(process);
00116     }
00117     else {
00118         // on the windows nt family we wait for the desktop to
00119         // change until it's neither the Screen-Saver desktop
00120         // nor a desktop we can't open (the login desktop).
00121         // since windows will send the request-to-start-screen-
00122         // saver message even when the screen saver is disabled
00123         // we first check that the screen saver is indeed active
00124         // before watching for it to stop.
00125         if (!isActive()) {
00126             LOG((CLOG_DEBUG2 "can't open screen saver desktop"));
00127             return false;
00128         }
00129 
00130         watchDesktop();
00131     }
00132 
00133     return true;
00134 }
00135 
00136 void
00137 CMSWindowsScreenSaver::enable()
00138 {
00139     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0);
00140 
00141     // restore password protection
00142     if (m_wasSecure) {
00143         setSecure(true, m_wasSecureAnInt);
00144     }
00145 
00146     // restore display power down
00147     CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
00148 }
00149 
00150 void
00151 CMSWindowsScreenSaver::disable()
00152 {
00153     SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
00154     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0);
00155 
00156     // disable password protected screensaver
00157     m_wasSecure = isSecure(&m_wasSecureAnInt);
00158     if (m_wasSecure) {
00159         setSecure(false, m_wasSecureAnInt);
00160     }
00161 
00162     // disable display power down
00163     CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY);
00164 }
00165 
00166 void
00167 CMSWindowsScreenSaver::activate()
00168 {
00169     // don't activate if already active
00170     if (!isActive()) {
00171         // activate
00172         HWND hwnd = GetForegroundWindow();
00173         if (hwnd != NULL) {
00174             PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
00175         }
00176         else {
00177             // no foreground window.  pretend we got the event instead.
00178             DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
00179         }
00180 
00181         // restore power save when screen saver activates
00182         CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
00183     }
00184 }
00185 
00186 void
00187 CMSWindowsScreenSaver::deactivate()
00188 {
00189     bool killed = false;
00190     if (!m_is95Family) {
00191         // NT runs screen saver in another desktop
00192         HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE,
00193                                 DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
00194         if (desktop != NULL) {
00195             EnumDesktopWindows(desktop,
00196                                 &CMSWindowsScreenSaver::killScreenSaverFunc,
00197                                 reinterpret_cast<LPARAM>(&killed));
00198             CloseDesktop(desktop);
00199         }
00200     }
00201 
00202     // if above failed or wasn't tried, try the windows 95 way
00203     if (!killed) {
00204         // find screen saver window and close it
00205         HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL);
00206         if (hwnd == NULL) {
00207             // win2k may use a different class
00208             hwnd = FindWindow("Default Screen Saver", NULL);
00209         }
00210         if (hwnd != NULL) {
00211             PostMessage(hwnd, WM_CLOSE, 0, 0);
00212         }
00213     }
00214 
00215     // force timer to restart
00216     SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
00217     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
00218                                 !m_wasEnabled, 0, SPIF_SENDWININICHANGE);
00219     SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
00220                                  m_wasEnabled, 0, SPIF_SENDWININICHANGE);
00221 
00222     // disable display power down
00223     CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
00224 }
00225 
00226 bool
00227 CMSWindowsScreenSaver::isActive() const
00228 {
00229     if (m_is95) {
00230         return (FindWindow("WindowsScreenSaverClass", NULL) != NULL);
00231     }
00232     else if (m_isNT) {
00233         // screen saver runs on a separate desktop
00234         HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED);
00235         if (desktop == NULL && GetLastError() != ERROR_ACCESS_DENIED) {
00236             // desktop doesn't exist so screen saver is not running
00237             return false;
00238         }
00239 
00240         // desktop exists.  this should indicate that the screen saver
00241         // is running but an OS bug can cause a valid handle to be
00242         // returned even if the screen saver isn't running (Q230117).
00243         // we'll try to enumerate the windows on the desktop and, if
00244         // there are any, we assume the screen saver is running.  (note
00245         // that if we don't have permission to enumerate then we'll
00246         // assume that the screen saver is not running.)  that'd be
00247         // easy enough except there's another OS bug (Q198590) that can
00248         // cause EnumDesktopWindows() to enumerate the windows of
00249         // another desktop if the requested desktop has no windows.  to
00250         // work around that we have to verify that the enumerated
00251         // windows are, in fact, on the expected desktop.
00252         CFindScreenSaverInfo info;
00253         info.m_desktop = desktop;
00254         info.m_window  = NULL;
00255         EnumDesktopWindows(desktop,
00256                                 &CMSWindowsScreenSaver::findScreenSaverFunc,
00257                                 reinterpret_cast<LPARAM>(&info));
00258 
00259         // done with desktop
00260         CloseDesktop(desktop);
00261 
00262         // screen saver is running if a window was found
00263         return (info.m_window != NULL);
00264     }
00265     else {
00266         BOOL running;
00267         SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
00268         return (running != FALSE);
00269     }
00270 }
00271 
00272 BOOL CALLBACK
00273 CMSWindowsScreenSaver::findScreenSaverFunc(HWND hwnd, LPARAM arg)
00274 {
00275     CFindScreenSaverInfo* info = reinterpret_cast<CFindScreenSaverInfo*>(arg);
00276 
00277     if (info->m_desktop != NULL) {
00278         DWORD threadID = GetWindowThreadProcessId(hwnd, NULL);
00279         HDESK desktop  = GetThreadDesktop(threadID);
00280         if (desktop != NULL && desktop != info->m_desktop) {
00281             // stop enumerating -- wrong desktop
00282             return FALSE;
00283         }
00284     }
00285 
00286     // found a window
00287     info->m_window = hwnd;
00288 
00289     // don't need to enumerate further
00290     return FALSE;
00291 }
00292 
00293 BOOL CALLBACK
00294 CMSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg)
00295 {
00296     if (IsWindowVisible(hwnd)) {
00297         HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
00298         if (instance != CMSWindowsScreen::getInstance()) {
00299             PostMessage(hwnd, WM_CLOSE, 0, 0);
00300             *reinterpret_cast<bool*>(arg) = true;
00301         }
00302     }
00303     return TRUE;
00304 }
00305 
00306 DWORD
00307 CMSWindowsScreenSaver::findScreenSaver()
00308 {
00309     // try windows 95 way
00310     HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL);
00311 
00312     // get process ID of process that owns the window, if found
00313     if (hwnd != NULL) {
00314         DWORD processID;
00315         GetWindowThreadProcessId(hwnd, &processID);
00316         return processID;
00317     }
00318 
00319     // not found
00320     return 0;
00321 }
00322 
00323 void
00324 CMSWindowsScreenSaver::watchDesktop()
00325 {
00326     // stop watching previous process/desktop
00327     unwatchProcess();
00328 
00329     // watch desktop in another thread
00330     LOG((CLOG_DEBUG "watching screen saver desktop"));
00331     m_active = true;
00332     m_watch  = new CThread(new TMethodJob<CMSWindowsScreenSaver>(this,
00333                                 &CMSWindowsScreenSaver::watchDesktopThread));
00334 }
00335 
00336 void
00337 CMSWindowsScreenSaver::watchProcess(HANDLE process)
00338 {
00339     // stop watching previous process/desktop
00340     unwatchProcess();
00341 
00342     // watch new process in another thread
00343     if (process != NULL) {
00344         LOG((CLOG_DEBUG "watching screen saver process"));
00345         m_process = process;
00346         m_active  = true;
00347         m_watch   = new CThread(new TMethodJob<CMSWindowsScreenSaver>(this,
00348                                 &CMSWindowsScreenSaver::watchProcessThread));
00349     }
00350 }
00351 
00352 void
00353 CMSWindowsScreenSaver::unwatchProcess()
00354 {
00355     if (m_watch != NULL) {
00356         LOG((CLOG_DEBUG "stopped watching screen saver process/desktop"));
00357         m_watch->cancel();
00358         m_watch->wait();
00359         delete m_watch;
00360         m_watch  = NULL;
00361         m_active = false;
00362     }
00363     if (m_process != NULL) {
00364         CloseHandle(m_process);
00365         m_process = NULL;
00366     }
00367 }
00368 
00369 void
00370 CMSWindowsScreenSaver::watchDesktopThread(void*)
00371 {
00372     DWORD reserved = 0;
00373     TCHAR* name    = NULL;
00374 
00375     for (;;) {
00376         // wait a bit
00377         ARCH->sleep(0.2);
00378 
00379         if (m_isNT) {
00380             // get current desktop
00381             HDESK desk = OpenInputDesktop(0, FALSE, GENERIC_READ);
00382             if (desk == NULL) {
00383                 // can't open desktop so keep waiting
00384                 continue;
00385             }
00386 
00387             // get current desktop name length
00388             DWORD size;
00389             GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
00390 
00391             // allocate more space for the name, if necessary
00392             if (size > reserved) {
00393                 reserved = size;
00394                 name     = (TCHAR*)alloca(reserved + sizeof(TCHAR));
00395             }
00396 
00397             // get current desktop name
00398             GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
00399             CloseDesktop(desk);
00400 
00401             // compare name to screen saver desktop name
00402             if (_tcsicmp(name, TEXT("Screen-saver")) == 0) {
00403                 // still the screen saver desktop so keep waiting
00404                 continue;
00405             }
00406         }
00407         else {
00408             // 2000/XP have a sane way to detect a runnin screensaver.
00409             BOOL running;
00410             SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
00411             if (running) {
00412                 continue;
00413             }
00414         }
00415 
00416         // send screen saver deactivation message
00417         m_active = false;
00418         PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
00419         return;
00420     }
00421 }
00422 
00423 void
00424 CMSWindowsScreenSaver::watchProcessThread(void*)
00425 {
00426     for (;;) {
00427         CThread::testCancel();
00428         if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) {
00429             // process terminated
00430             LOG((CLOG_DEBUG "screen saver died"));
00431 
00432             // send screen saver deactivation message
00433             m_active = false;
00434             PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
00435             return;
00436         }
00437     }
00438 }
00439 
00440 void
00441 CMSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt)
00442 {
00443     HKEY hkey =
00444         CArchMiscWindows::addKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
00445     if (hkey == NULL) {
00446         return;
00447     }
00448 
00449     const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT;
00450     if (saveSecureAsInt) {
00451         CArchMiscWindows::setValue(hkey, isSecure, secure ? 1 : 0);
00452     }
00453     else {
00454         CArchMiscWindows::setValue(hkey, isSecure, secure ? "1" : "0");
00455     }
00456 
00457     CArchMiscWindows::closeKey(hkey);
00458 }
00459 
00460 bool
00461 CMSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const
00462 {
00463     // get the password protection setting key
00464     HKEY hkey =
00465         CArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
00466     if (hkey == NULL) {
00467         return false;
00468     }
00469 
00470     // get the value.  the value may be an int or a string, depending
00471     // on the version of windows.
00472     bool result;
00473     const TCHAR* isSecure = m_is95Family ? g_isSecure9x : g_isSecureNT;
00474     switch (CArchMiscWindows::typeOfValue(hkey, isSecure)) {
00475     default:
00476         result = false;
00477         break;
00478 
00479     case CArchMiscWindows::kUINT: {
00480         DWORD value =
00481             CArchMiscWindows::readValueInt(hkey, isSecure);
00482         *wasSecureFlagAnInt = true;
00483         result = (value != 0);
00484         break;
00485     }
00486 
00487     case CArchMiscWindows::kSTRING: {
00488         std::string value =
00489             CArchMiscWindows::readValueString(hkey, isSecure);
00490         *wasSecureFlagAnInt = false;
00491         result = (value != "0");
00492         break;
00493     }
00494     }
00495 
00496     CArchMiscWindows::closeKey(hkey);
00497     return result;
00498 }

Generated on Fri Nov 6 00:21:14 2009 for synergy-plus by  doxygen 1.3.9.1