barrier/lib/platform/CMSWindowsScreenSaver.cpp
crs fd5625ba99 Improved handling of screen saver handling when windows 2k is
the client and the screen saver is password protected.  It used
to immediately turn off the screen saver (unintentionally) in
that case.
2002-12-25 18:44:54 +00:00

317 lines
8.2 KiB
C++

/*
* 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"
#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);
HWND hwnd = findScreenSaver();
if (hwnd == NULL) {
// didn't start
LOG((CLOG_DEBUG "can't find screen saver window"));
return false;
}
// get the process
DWORD processID;
GetWindowThreadProcessId(hwnd, &processID);
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
m_threadID = GetCurrentThreadId();
m_msg = msg;
m_wParam = wParam;
m_lParam = lParam;
watchProcess(process);
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<LPARAM>(&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<LPARAM>(&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<CFindScreenSaverInfo*>(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<bool*>(arg) = true;
}
return TRUE;
}
HWND
CMSWindowsScreenSaver::findScreenSaver()
{
if (!m_is95Family) {
// screen saver runs on a separate desktop
HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, MAXIMUM_ALLOWED);
if (desktop != NULL) {
// search
CFindScreenSaverInfo info;
info.m_desktop = desktop;
info.m_window = NULL;
EnumDesktopWindows(desktop,
&CMSWindowsScreenSaver::findScreenSaverFunc,
reinterpret_cast<LPARAM>(&info));
// done with desktop
CloseDesktop(desktop);
// return window if found
if (info.m_window != NULL) {
return info.m_window;
}
}
}
// try windows 95 way
return FindWindow("WindowsScreenSaverClass", NULL);
}
void
CMSWindowsScreenSaver::watchProcess(HANDLE process)
{
// stop watching previous process
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<CMSWindowsScreenSaver>(this,
&CMSWindowsScreenSaver::watchProcessThread));
}
}
void
CMSWindowsScreenSaver::unwatchProcess()
{
if (m_watch != NULL) {
LOG((CLOG_DEBUG "stopped watching screen saver process"));
m_watch->cancel();
m_watch->wait();
delete m_watch;
m_watch = NULL;
}
if (m_process != NULL) {
CloseHandle(m_process);
m_process = NULL;
}
}
void
CMSWindowsScreenSaver::watchProcessThread(void*)
{
for (;;) {
CThread::testCancel();
if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) {
// process terminated. send screen saver deactivation
// message.
LOG((CLOG_DEBUG "screen saver died"));
PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
return;
}
}
}