CMSWindowsScreen.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 "CMSWindowsScreen.h"
00016 #include "CMSWindowsClipboard.h"
00017 #include "CMSWindowsDesks.h"
00018 #include "CMSWindowsEventQueueBuffer.h"
00019 #include "CMSWindowsKeyState.h"
00020 #include "CMSWindowsScreenSaver.h"
00021 #include "CClipboard.h"
00022 #include "CKeyMap.h"
00023 #include "XScreen.h"
00024 #include "CLock.h"
00025 #include "CThread.h"
00026 #include "CFunctionJob.h"
00027 #include "CLog.h"
00028 #include "CString.h"
00029 #include "CStringUtil.h"
00030 #include "IEventQueue.h"
00031 #include "TMethodEventJob.h"
00032 #include "TMethodJob.h"
00033 #include "CArch.h"
00034 #include "CArchMiscWindows.h"
00035 #include <string.h>
00036 #include <pbt.h>
00037 
00038 //
00039 // add backwards compatible multihead support (and suppress bogus warning).
00040 // this isn't supported on MinGW yet AFAICT.
00041 //
00042 #if defined(_MSC_VER)
00043 #pragma warning(push)
00044 #pragma warning(disable: 4706) // assignment within conditional
00045 #define COMPILE_MULTIMON_STUBS
00046 #include <multimon.h>
00047 #pragma warning(pop)
00048 #endif
00049 
00050 // X button stuff
00051 #if !defined(WM_XBUTTONDOWN)
00052 #define WM_XBUTTONDOWN      0x020B
00053 #define WM_XBUTTONUP        0x020C
00054 #define WM_XBUTTONDBLCLK    0x020D
00055 #define WM_NCXBUTTONDOWN    0x00AB
00056 #define WM_NCXBUTTONUP      0x00AC
00057 #define WM_NCXBUTTONDBLCLK  0x00AD
00058 #define MOUSEEVENTF_XDOWN   0x0080
00059 #define MOUSEEVENTF_XUP     0x0100
00060 #define XBUTTON1            0x0001
00061 #define XBUTTON2            0x0002
00062 #endif
00063 #if !defined(VK_XBUTTON1)
00064 #define VK_XBUTTON1         0x05
00065 #define VK_XBUTTON2         0x06
00066 #endif
00067 
00068 // WM_POWERBROADCAST stuff
00069 #if !defined(PBT_APMRESUMEAUTOMATIC)
00070 #define PBT_APMRESUMEAUTOMATIC  0x0012
00071 #endif
00072 
00073 //
00074 // CMSWindowsScreen
00075 //
00076 
00077 HINSTANCE               CMSWindowsScreen::s_instance = NULL;
00078 CMSWindowsScreen*       CMSWindowsScreen::s_screen   = NULL;
00079 
00080 CMSWindowsScreen::CMSWindowsScreen(bool isPrimary) :
00081     m_isPrimary(isPrimary),
00082     m_is95Family(CArchMiscWindows::isWindows95Family()),
00083     m_isOnScreen(m_isPrimary),
00084     m_class(0),
00085     m_x(0), m_y(0),
00086     m_w(0), m_h(0),
00087     m_xCenter(0), m_yCenter(0),
00088     m_multimon(false),
00089     m_xCursor(0), m_yCursor(0),
00090     m_sequenceNumber(0),
00091     m_mark(0),
00092     m_markReceived(0),
00093     m_fixTimer(NULL),
00094     m_keyLayout(NULL),
00095     m_screensaver(NULL),
00096     m_screensaverNotify(false),
00097     m_screensaverActive(false),
00098     m_window(NULL),
00099     m_nextClipboardWindow(NULL),
00100     m_ownClipboard(false),
00101     m_desks(NULL),
00102     m_hookLibrary(NULL),
00103     m_init(NULL),
00104     m_cleanup(NULL),
00105     m_setSides(NULL),
00106     m_setZone(NULL),
00107     m_setMode(NULL),
00108     m_keyState(NULL),
00109     m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0),
00110     m_showingMouse(false)
00111 {
00112     assert(s_instance != NULL);
00113     assert(s_screen   == NULL);
00114 
00115     s_screen = this;
00116     try {
00117         if (m_isPrimary) {
00118             m_hookLibrary = openHookLibrary("synrgyhk");
00119         }
00120         m_screensaver = new CMSWindowsScreenSaver();
00121         m_desks       = new CMSWindowsDesks(m_isPrimary,
00122                             m_hookLibrary, m_screensaver,
00123                             new TMethodJob<CMSWindowsScreen>(this,
00124                                 &CMSWindowsScreen::updateKeysCB));
00125         m_keyState    = new CMSWindowsKeyState(m_desks, getEventTarget());
00126         updateScreenShape();
00127         m_class       = createWindowClass();
00128         m_window      = createWindow(m_class, "Synergy");
00129         forceShowCursor();
00130         LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
00131         LOG((CLOG_DEBUG "window is 0x%08x", m_window));
00132     }
00133     catch (...) {
00134         delete m_keyState;
00135         delete m_desks;
00136         delete m_screensaver;
00137         destroyWindow(m_window);
00138         destroyClass(m_class);
00139         closeHookLibrary(m_hookLibrary);
00140         s_screen = NULL;
00141         throw;
00142     }
00143 
00144     // install event handlers
00145     EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(),
00146                             new TMethodEventJob<CMSWindowsScreen>(this,
00147                                 &CMSWindowsScreen::handleSystemEvent));
00148 
00149     // install the platform event queue
00150     EVENTQUEUE->adoptBuffer(new CMSWindowsEventQueueBuffer);
00151 }
00152 
00153 CMSWindowsScreen::~CMSWindowsScreen()
00154 {
00155     assert(s_screen != NULL);
00156 
00157     disable();
00158     EVENTQUEUE->adoptBuffer(NULL);
00159     EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget());
00160     delete m_keyState;
00161     delete m_desks;
00162     delete m_screensaver;
00163     destroyWindow(m_window);
00164     destroyClass(m_class);
00165     closeHookLibrary(m_hookLibrary);
00166     s_screen = NULL;
00167 }
00168 
00169 void
00170 CMSWindowsScreen::init(HINSTANCE instance)
00171 {
00172     assert(s_instance == NULL);
00173     assert(instance   != NULL);
00174 
00175     s_instance = instance;
00176 }
00177 
00178 HINSTANCE
00179 CMSWindowsScreen::getInstance()
00180 {
00181     return s_instance;
00182 }
00183 
00184 void
00185 CMSWindowsScreen::enable()
00186 {
00187     assert(m_isOnScreen == m_isPrimary);
00188 
00189     // we need to poll some things to fix them
00190     m_fixTimer = EVENTQUEUE->newTimer(1.0, NULL);
00191     EVENTQUEUE->adoptHandler(CEvent::kTimer, m_fixTimer,
00192                             new TMethodEventJob<CMSWindowsScreen>(this,
00193                                 &CMSWindowsScreen::handleFixes));
00194 
00195     // install our clipboard snooper
00196     m_nextClipboardWindow = SetClipboardViewer(m_window);
00197 
00198     // track the active desk and (re)install the hooks
00199     m_desks->enable();
00200 
00201     if (m_isPrimary) {
00202         // set jump zones
00203         m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
00204 
00205         // watch jump zones
00206         m_setMode(kHOOK_WATCH_JUMP_ZONE);
00207     }
00208     else {
00209         // prevent the system from entering power saving modes.  if
00210         // it did we'd be forced to disconnect from the server and
00211         // the server would not be able to wake us up.
00212         CArchMiscWindows::addBusyState(CArchMiscWindows::kSYSTEM);
00213     }
00214 }
00215 
00216 void
00217 CMSWindowsScreen::disable()
00218 {
00219     // stop tracking the active desk
00220     m_desks->disable();
00221 
00222     if (m_isPrimary) {
00223         // disable hooks
00224         m_setMode(kHOOK_DISABLE);
00225 
00226         // enable special key sequences on win95 family
00227         enableSpecialKeys(true);
00228     }
00229     else {
00230         // allow the system to enter power saving mode
00231         CArchMiscWindows::removeBusyState(CArchMiscWindows::kSYSTEM |
00232                             CArchMiscWindows::kDISPLAY);
00233     }
00234 
00235     // tell key state
00236     m_keyState->disable();
00237 
00238     // stop snooping the clipboard
00239     ChangeClipboardChain(m_window, m_nextClipboardWindow);
00240     m_nextClipboardWindow = NULL;
00241 
00242     // uninstall fix timer
00243     if (m_fixTimer != NULL) {
00244         EVENTQUEUE->removeHandler(CEvent::kTimer, m_fixTimer);
00245         EVENTQUEUE->deleteTimer(m_fixTimer);
00246         m_fixTimer = NULL;
00247     }
00248 
00249     m_isOnScreen = m_isPrimary;
00250     forceShowCursor();
00251 }
00252 
00253 void
00254 CMSWindowsScreen::enter()
00255 {
00256     m_desks->enter();
00257     if (m_isPrimary) {
00258         // enable special key sequences on win95 family
00259         enableSpecialKeys(true);
00260 
00261         // watch jump zones
00262         m_setMode(kHOOK_WATCH_JUMP_ZONE);
00263 
00264         // all messages prior to now are invalid
00265         nextMark();
00266     } else {
00267         // Entering a secondary screen. Ensure that no screensaver is active
00268         // and that the screen is not in powersave mode.
00269         CArchMiscWindows::wakeupDisplay();
00270 
00271         if(m_screensaver != NULL && m_screensaverActive)
00272         {
00273             m_screensaver->deactivate();
00274             m_screensaverActive = 0;
00275         }
00276     }
00277 
00278     // now on screen
00279     m_isOnScreen = true;
00280     forceShowCursor();
00281 }
00282 
00283 bool
00284 CMSWindowsScreen::leave()
00285 {
00286     // get keyboard layout of foreground window.  we'll use this
00287     // keyboard layout for translating keys sent to clients.
00288     HWND window  = GetForegroundWindow();
00289     DWORD thread = GetWindowThreadProcessId(window, NULL);
00290     m_keyLayout  = GetKeyboardLayout(thread);
00291 
00292     // tell the key mapper about the keyboard layout
00293     m_keyState->setKeyLayout(m_keyLayout);
00294 
00295     // tell desk that we're leaving and tell it the keyboard layout
00296     m_desks->leave(m_keyLayout);
00297 
00298     if (m_isPrimary) {
00299         // warp to center
00300         warpCursor(m_xCenter, m_yCenter);
00301 
00302         // disable special key sequences on win95 family
00303         enableSpecialKeys(false);
00304 
00305         // all messages prior to now are invalid
00306         nextMark();
00307 
00308         // remember the modifier state.  this is the modifier state
00309         // reflected in the internal keyboard state.
00310         m_keyState->saveModifiers();
00311 
00312         // capture events
00313         m_setMode(kHOOK_RELAY_EVENTS);
00314     }
00315 
00316     // now off screen
00317     m_isOnScreen = false;
00318     forceShowCursor();
00319 
00320     return true;
00321 }
00322 
00323 bool
00324 CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src)
00325 {
00326     CMSWindowsClipboard dst(m_window);
00327     if (src != NULL) {
00328         // save clipboard data
00329         return CClipboard::copy(&dst, src);
00330     }
00331     else {
00332         // assert clipboard ownership
00333         if (!dst.open(0)) {
00334             return false;
00335         }
00336         dst.empty();
00337         dst.close();
00338         return true;
00339     }
00340 }
00341 
00342 void
00343 CMSWindowsScreen::checkClipboards()
00344 {
00345     // if we think we own the clipboard but we don't then somebody
00346     // grabbed the clipboard on this screen without us knowing.
00347     // tell the server that this screen grabbed the clipboard.
00348     //
00349     // this works around bugs in the clipboard viewer chain.
00350     // sometimes NT will simply never send WM_DRAWCLIPBOARD
00351     // messages for no apparent reason and rebooting fixes the
00352     // problem.  since we don't want a broken clipboard until the
00353     // next reboot we do this double check.  clipboard ownership
00354     // won't be reflected on other screens until we leave but at
00355     // least the clipboard itself will work.
00356     if (m_ownClipboard && !CMSWindowsClipboard::isOwnedBySynergy()) {
00357         LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
00358         m_ownClipboard = false;
00359         sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
00360         sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
00361     }
00362 }
00363 
00364 void
00365 CMSWindowsScreen::openScreensaver(bool notify)
00366 {
00367     assert(m_screensaver != NULL);
00368 
00369     m_screensaverNotify = notify;
00370     if (m_screensaverNotify) {
00371         m_desks->installScreensaverHooks(true);
00372     }
00373     else {
00374         m_screensaver->disable();
00375     }
00376 }
00377 
00378 void
00379 CMSWindowsScreen::closeScreensaver()
00380 {
00381     if (m_screensaver != NULL) {
00382         if (m_screensaverNotify) {
00383             m_desks->installScreensaverHooks(false);
00384         }
00385         else {
00386             m_screensaver->enable();
00387         }
00388     }
00389     m_screensaverNotify = false;
00390 }
00391 
00392 void
00393 CMSWindowsScreen::screensaver(bool activate)
00394 {
00395     assert(m_screensaver != NULL);
00396 
00397     if (activate) {
00398         m_screensaver->activate();
00399     }
00400     else {
00401         m_screensaver->deactivate();
00402     }
00403 }
00404 
00405 void
00406 CMSWindowsScreen::resetOptions()
00407 {
00408     m_desks->resetOptions();
00409 }
00410 
00411 void
00412 CMSWindowsScreen::setOptions(const COptionsList& options)
00413 {
00414     m_desks->setOptions(options);
00415 }
00416 
00417 void
00418 CMSWindowsScreen::setSequenceNumber(UInt32 seqNum)
00419 {
00420     m_sequenceNumber = seqNum;
00421 }
00422 
00423 bool
00424 CMSWindowsScreen::isPrimary() const
00425 {
00426     return m_isPrimary;
00427 }
00428 
00429 void*
00430 CMSWindowsScreen::getEventTarget() const
00431 {
00432     return const_cast<CMSWindowsScreen*>(this);
00433 }
00434 
00435 bool
00436 CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const
00437 {
00438     CMSWindowsClipboard src(m_window);
00439     CClipboard::copy(dst, &src);
00440     return true;
00441 }
00442 
00443 void
00444 CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
00445 {
00446     assert(m_class != 0);
00447 
00448     x = m_x;
00449     y = m_y;
00450     w = m_w;
00451     h = m_h;
00452 }
00453 
00454 void
00455 CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
00456 {
00457     m_desks->getCursorPos(x, y);
00458 }
00459 
00460 void
00461 CMSWindowsScreen::reconfigure(UInt32 activeSides)
00462 {
00463     assert(m_isPrimary);
00464 
00465     LOG((CLOG_DEBUG "active sides: %x", activeSides));
00466     m_setSides(activeSides);
00467 }
00468 
00469 void
00470 CMSWindowsScreen::warpCursor(SInt32 x, SInt32 y)
00471 {
00472     // warp mouse
00473     warpCursorNoFlush(x, y);
00474 
00475     // remove all input events before and including warp
00476     MSG msg;
00477     while (PeekMessage(&msg, NULL, SYNERGY_MSG_INPUT_FIRST,
00478                                 SYNERGY_MSG_INPUT_LAST, PM_REMOVE)) {
00479         // do nothing
00480     }
00481 
00482     // save position as last position
00483     m_xCursor = x;
00484     m_yCursor = y;
00485 }
00486 
00487 UInt32
00488 CMSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
00489 {
00490     // only allow certain modifiers
00491     if ((mask & ~(KeyModifierShift | KeyModifierControl |
00492                   KeyModifierAlt   | KeyModifierSuper)) != 0) {
00493         LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00494         return 0;
00495     }
00496 
00497     // fail if no keys
00498     if (key == kKeyNone && mask == 0) {
00499         return 0;
00500     }
00501 
00502     // convert to win32
00503     UINT modifiers = 0;
00504     if ((mask & KeyModifierShift) != 0) {
00505         modifiers |= MOD_SHIFT;
00506     }
00507     if ((mask & KeyModifierControl) != 0) {
00508         modifiers |= MOD_CONTROL;
00509     }
00510     if ((mask & KeyModifierAlt) != 0) {
00511         modifiers |= MOD_ALT;
00512     }
00513     if ((mask & KeyModifierSuper) != 0) {
00514         modifiers |= MOD_WIN;
00515     }
00516     UINT vk = m_keyState->mapKeyToVirtualKey(key);
00517     if (key != kKeyNone && vk == 0) {
00518         // can't map key
00519         LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00520         return 0;
00521     }
00522 
00523     // choose hotkey id
00524     UInt32 id;
00525     if (!m_oldHotKeyIDs.empty()) {
00526         id = m_oldHotKeyIDs.back();
00527         m_oldHotKeyIDs.pop_back();
00528     }
00529     else {
00530         id = m_hotKeys.size() + 1;
00531     }
00532 
00533     // if this hot key has modifiers only then we'll handle it specially
00534     bool err;
00535     if (key == kKeyNone) {
00536         // check if already registered
00537         err = (m_hotKeyToIDMap.count(CHotKeyItem(vk, modifiers)) > 0);
00538     }
00539     else {
00540         // register with OS
00541         err = (RegisterHotKey(NULL, id, modifiers, vk) == 0);
00542     }
00543 
00544     if (!err) {
00545         m_hotKeys.insert(std::make_pair(id, CHotKeyItem(vk, modifiers)));
00546         m_hotKeyToIDMap[CHotKeyItem(vk, modifiers)] = id;
00547     }
00548     else {
00549         m_oldHotKeyIDs.push_back(id);
00550         m_hotKeys.erase(id);
00551         LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask));
00552         return 0;
00553     }
00554     
00555     LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id));
00556     return id;
00557 }
00558 
00559 void
00560 CMSWindowsScreen::unregisterHotKey(UInt32 id)
00561 {
00562     // look up hotkey
00563     HotKeyMap::iterator i = m_hotKeys.find(id);
00564     if (i == m_hotKeys.end()) {
00565         return;
00566     }
00567 
00568     // unregister with OS
00569     bool err;
00570     if (i->second.getVirtualKey() != 0) {
00571         err = !UnregisterHotKey(NULL, id);
00572     }
00573     else {
00574         err = false;
00575     }
00576     if (err) {
00577         LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
00578     }
00579     else {
00580         LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
00581     }
00582 
00583     // discard hot key from map and record old id for reuse
00584     m_hotKeyToIDMap.erase(i->second);
00585     m_hotKeys.erase(i);
00586     m_oldHotKeyIDs.push_back(id);
00587 }
00588 
00589 void
00590 CMSWindowsScreen::fakeInputBegin()
00591 {
00592     assert(m_isPrimary);
00593 
00594     if (!m_isOnScreen) {
00595         m_keyState->useSavedModifiers(true);
00596     }
00597     m_desks->fakeInputBegin();
00598 }
00599 
00600 void
00601 CMSWindowsScreen::fakeInputEnd()
00602 {
00603     assert(m_isPrimary);
00604 
00605     m_desks->fakeInputEnd();
00606     if (!m_isOnScreen) {
00607         m_keyState->useSavedModifiers(false);
00608     }
00609 }
00610 
00611 SInt32
00612 CMSWindowsScreen::getJumpZoneSize() const
00613 {
00614     return 1;
00615 }
00616 
00617 bool
00618 CMSWindowsScreen::isAnyMouseButtonDown() const
00619 {
00620     static const char* buttonToName[] = {
00621         "<invalid>",
00622         "Left Button",
00623         "Middle Button",
00624         "Right Button",
00625         "X Button 1",
00626         "X Button 2"
00627     };
00628 
00629     for (UInt32 i = 1; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
00630         if (m_buttons[i]) {
00631             LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i]));
00632             return true;
00633         }
00634     }
00635 
00636     return false;
00637 }
00638 
00639 void
00640 CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
00641 {
00642     x = m_xCenter;
00643     y = m_yCenter;
00644 }
00645 
00646 void
00647 CMSWindowsScreen::fakeMouseButton(ButtonID id, bool press) const
00648 {
00649     m_desks->fakeMouseButton(id, press);
00650 }
00651 
00652 void
00653 CMSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const
00654 {
00655     m_desks->fakeMouseMove(x, y);
00656 }
00657 
00658 void
00659 CMSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
00660 {
00661     m_desks->fakeMouseRelativeMove(dx, dy);
00662 }
00663 
00664 void
00665 CMSWindowsScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
00666 {
00667     m_desks->fakeMouseWheel(xDelta, yDelta);
00668 }
00669 
00670 void
00671 CMSWindowsScreen::updateKeys()
00672 {
00673     m_desks->updateKeys();
00674 }
00675 
00676 void
00677 CMSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
00678                 KeyButton button)
00679 {
00680     CPlatformScreen::fakeKeyDown(id, mask, button);
00681     updateForceShowCursor();
00682 }
00683 
00684 void
00685 CMSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
00686                 SInt32 count, KeyButton button)
00687 {
00688     CPlatformScreen::fakeKeyRepeat(id, mask, count, button);
00689     updateForceShowCursor();
00690 }
00691 
00692 void
00693 CMSWindowsScreen::fakeKeyUp(KeyButton button)
00694 {
00695     CPlatformScreen::fakeKeyUp(button);
00696     updateForceShowCursor();
00697 }
00698 
00699 void
00700 CMSWindowsScreen::fakeAllKeysUp()
00701 {
00702     CPlatformScreen::fakeAllKeysUp();
00703     updateForceShowCursor();
00704 }
00705 
00706 HINSTANCE
00707 CMSWindowsScreen::openHookLibrary(const char* name)
00708 {
00709     // load the hook library
00710     HINSTANCE hookLibrary = LoadLibrary(name);
00711     if (hookLibrary == NULL) {
00712         LOG((CLOG_ERR "Failed to load hook library;  %s.dll is missing", name));
00713         throw XScreenOpenFailure();
00714     }
00715 
00716     // look up functions
00717     m_setSides  = (SetSidesFunc)GetProcAddress(hookLibrary, "setSides");
00718     m_setZone   = (SetZoneFunc)GetProcAddress(hookLibrary, "setZone");
00719     m_setMode   = (SetModeFunc)GetProcAddress(hookLibrary, "setMode");
00720     m_init      = (InitFunc)GetProcAddress(hookLibrary, "init");
00721     m_cleanup   = (CleanupFunc)GetProcAddress(hookLibrary, "cleanup");
00722     if (m_setSides             == NULL ||
00723         m_setZone              == NULL ||
00724         m_setMode              == NULL ||
00725         m_init                 == NULL ||
00726         m_cleanup              == NULL) {
00727         LOG((CLOG_ERR "Invalid hook library;  use a newer %s.dll", name));
00728         throw XScreenOpenFailure();
00729     }
00730 
00731     // initialize hook library
00732     if (m_init(GetCurrentThreadId()) == 0) {
00733         LOG((CLOG_ERR "Cannot initialize hook library;  is synergy already running?"));
00734         throw XScreenOpenFailure();
00735     }
00736 
00737     return hookLibrary;
00738 }
00739 
00740 void
00741 CMSWindowsScreen::closeHookLibrary(HINSTANCE hookLibrary) const
00742 {
00743     if (hookLibrary != NULL) {
00744         m_cleanup();
00745         FreeLibrary(hookLibrary);
00746     }
00747 }
00748 
00749 HCURSOR
00750 CMSWindowsScreen::createBlankCursor() const
00751 {
00752     // create a transparent cursor
00753     int cw = GetSystemMetrics(SM_CXCURSOR);
00754     int ch = GetSystemMetrics(SM_CYCURSOR);
00755     UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
00756     UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
00757     memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
00758     memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
00759     HCURSOR c = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR);
00760     delete[] cursorXOR;
00761     delete[] cursorAND;
00762     return c;
00763 }
00764 
00765 void
00766 CMSWindowsScreen::destroyCursor(HCURSOR cursor) const
00767 {
00768     if (cursor != NULL) {
00769         DestroyCursor(cursor);
00770     }
00771 }
00772 
00773 ATOM
00774 CMSWindowsScreen::createWindowClass() const
00775 {
00776     WNDCLASSEX classInfo;
00777     classInfo.cbSize        = sizeof(classInfo);
00778     classInfo.style         = CS_DBLCLKS | CS_NOCLOSE;
00779     classInfo.lpfnWndProc   = &CMSWindowsScreen::wndProc;
00780     classInfo.cbClsExtra    = 0;
00781     classInfo.cbWndExtra    = 0;
00782     classInfo.hInstance     = s_instance;
00783     classInfo.hIcon         = NULL;
00784     classInfo.hCursor       = NULL;
00785     classInfo.hbrBackground = NULL;
00786     classInfo.lpszMenuName  = NULL;
00787     classInfo.lpszClassName = "Synergy";
00788     classInfo.hIconSm       = NULL;
00789     return RegisterClassEx(&classInfo);
00790 }
00791 
00792 void
00793 CMSWindowsScreen::destroyClass(ATOM windowClass) const
00794 {
00795     if (windowClass != 0) {
00796         UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_instance);
00797     }
00798 }
00799 
00800 HWND
00801 CMSWindowsScreen::createWindow(ATOM windowClass, const char* name) const
00802 {
00803     HWND window = CreateWindowEx(WS_EX_TOPMOST |
00804                                     WS_EX_TRANSPARENT |
00805                                     WS_EX_TOOLWINDOW,
00806                                 reinterpret_cast<LPCTSTR>(windowClass),
00807                                 name,
00808                                 WS_POPUP,
00809                                 0, 0, 1, 1,
00810                                 NULL, NULL,
00811                                 s_instance,
00812                                 NULL);
00813     if (window == NULL) {
00814         LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
00815         throw XScreenOpenFailure();
00816     }
00817     return window;
00818 }
00819 
00820 void
00821 CMSWindowsScreen::destroyWindow(HWND hwnd) const
00822 {
00823     if (hwnd != NULL) {
00824         DestroyWindow(hwnd);
00825     }
00826 }
00827 
00828 void
00829 CMSWindowsScreen::sendEvent(CEvent::Type type, void* data)
00830 {
00831     EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
00832 }
00833 
00834 void
00835 CMSWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
00836 {
00837     CClipboardInfo* info   = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
00838     info->m_id             = id;
00839     info->m_sequenceNumber = m_sequenceNumber;
00840     sendEvent(type, info);
00841 }
00842 
00843 void
00844 CMSWindowsScreen::handleSystemEvent(const CEvent& event, void*)
00845 {
00846     MSG* msg = reinterpret_cast<MSG*>(event.getData());
00847     assert(msg != NULL);
00848 
00849     if (CArchMiscWindows::processDialog(msg)) {
00850         return;
00851     }
00852     if (onPreDispatch(msg->hwnd, msg->message, msg->wParam, msg->lParam)) {
00853         return;
00854     }
00855     TranslateMessage(msg);
00856     DispatchMessage(msg);
00857 }
00858 
00859 void
00860 CMSWindowsScreen::updateButtons()
00861 {
00862     int numButtons               = GetSystemMetrics(SM_CMOUSEBUTTONS);
00863     m_buttons[kButtonNone]       = false;
00864     m_buttons[kButtonLeft]       = (GetKeyState(VK_LBUTTON)  < 0);
00865     m_buttons[kButtonRight]      = (GetKeyState(VK_RBUTTON)  < 0);
00866     m_buttons[kButtonMiddle]     = (GetKeyState(VK_MBUTTON)  < 0);
00867     m_buttons[kButtonExtra0 + 0] = (numButtons >= 4) &&
00868                                    (GetKeyState(VK_XBUTTON1) < 0);
00869     m_buttons[kButtonExtra0 + 1] = (numButtons >= 5) &&
00870                                    (GetKeyState(VK_XBUTTON2) < 0);
00871 }
00872 
00873 IKeyState*
00874 CMSWindowsScreen::getKeyState() const
00875 {
00876     return m_keyState;
00877 }
00878 
00879 bool
00880 CMSWindowsScreen::onPreDispatch(HWND hwnd,
00881                 UINT message, WPARAM wParam, LPARAM lParam)
00882 {
00883     // handle event
00884     switch (message) {
00885     case SYNERGY_MSG_SCREEN_SAVER:
00886         return onScreensaver(wParam != 0);
00887 
00888     case SYNERGY_MSG_DEBUG:
00889         LOG((CLOG_DEBUG1 "hook: 0x%08x 0x%08x", wParam, lParam));
00890         return true;
00891     }
00892 
00893     if (m_isPrimary) {
00894         return onPreDispatchPrimary(hwnd, message, wParam, lParam);
00895     }
00896 
00897     return false;
00898 }
00899 
00900 bool
00901 CMSWindowsScreen::onPreDispatchPrimary(HWND,
00902                 UINT message, WPARAM wParam, LPARAM lParam)
00903 {
00904     // handle event
00905     switch (message) {
00906     case SYNERGY_MSG_MARK:
00907         return onMark(static_cast<UInt32>(wParam));
00908 
00909     case SYNERGY_MSG_KEY:
00910         return onKey(wParam, lParam);
00911 
00912     case SYNERGY_MSG_MOUSE_BUTTON:
00913         return onMouseButton(wParam, lParam);
00914 
00915     case SYNERGY_MSG_MOUSE_MOVE:
00916         return onMouseMove(static_cast<SInt32>(wParam),
00917                             static_cast<SInt32>(lParam));
00918 
00919     case SYNERGY_MSG_MOUSE_WHEEL:
00920         // XXX -- support x-axis scrolling
00921         return onMouseWheel(0, static_cast<SInt32>(wParam));
00922 
00923     case SYNERGY_MSG_PRE_WARP:
00924         {
00925             // save position to compute delta of next motion
00926             m_xCursor = static_cast<SInt32>(wParam);
00927             m_yCursor = static_cast<SInt32>(lParam);
00928 
00929             // we warped the mouse.  discard events until we find the
00930             // matching post warp event.  see warpCursorNoFlush() for
00931             // where the events are sent.  we discard the matching
00932             // post warp event and can be sure we've skipped the warp
00933             // event.
00934             MSG msg;
00935             do {
00936                 GetMessage(&msg, NULL, SYNERGY_MSG_MOUSE_MOVE,
00937                                         SYNERGY_MSG_POST_WARP);
00938             } while (msg.message != SYNERGY_MSG_POST_WARP);
00939         }
00940         return true;
00941 
00942     case SYNERGY_MSG_POST_WARP:
00943         LOG((CLOG_WARN "unmatched post warp"));
00944         return true;
00945 
00946     case WM_HOTKEY:
00947         // we discard these messages.  we'll catch the hot key in the
00948         // regular key event handling, where we can detect both key
00949         // press and release.  we only register the hot key so no other
00950         // app will act on the key combination.
00951         break;
00952     }
00953 
00954     return false;
00955 }
00956 
00957 bool
00958 CMSWindowsScreen::onEvent(HWND, UINT msg,
00959                 WPARAM wParam, LPARAM lParam, LRESULT* result)
00960 {
00961     switch (msg) {
00962     case WM_QUERYENDSESSION:
00963         if (m_is95Family) {
00964             *result = TRUE;
00965             return true;
00966         }
00967         break;
00968 
00969     case WM_ENDSESSION:
00970         if (m_is95Family) {
00971             if (wParam == TRUE && lParam == 0) {
00972                 EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00973             }
00974             return true;
00975         }
00976         break;
00977 
00978     case WM_DRAWCLIPBOARD:
00979         // first pass on the message
00980         if (m_nextClipboardWindow != NULL) {
00981             SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
00982         }
00983 
00984         // now handle the message
00985         return onClipboardChange();
00986 
00987     case WM_CHANGECBCHAIN:
00988         if (m_nextClipboardWindow == (HWND)wParam) {
00989             m_nextClipboardWindow = (HWND)lParam;
00990             LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow));
00991         }
00992         else if (m_nextClipboardWindow != NULL) {
00993             SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
00994         }
00995         return true;
00996 
00997     case WM_DISPLAYCHANGE:
00998         return onDisplayChange();
00999 
01000     case WM_POWERBROADCAST:
01001         switch (wParam) {
01002         case PBT_APMRESUMEAUTOMATIC:
01003         case PBT_APMRESUMECRITICAL:
01004         case PBT_APMRESUMESUSPEND:
01005             EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(),
01006                             getEventTarget(), NULL,
01007                             CEvent::kDeliverImmediately));
01008             break;
01009 
01010         case PBT_APMSUSPEND:
01011             EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(),
01012                             getEventTarget(), NULL,
01013                             CEvent::kDeliverImmediately));
01014             break;
01015         }
01016         *result = TRUE;
01017         return true;
01018 
01019     case WM_DEVICECHANGE:
01020         forceShowCursor();
01021         break;
01022 
01023     case WM_SETTINGCHANGE:
01024         if (wParam == SPI_SETMOUSEKEYS) {
01025             forceShowCursor();
01026         }
01027         break;
01028     }
01029 
01030     return false;
01031 }
01032 
01033 bool
01034 CMSWindowsScreen::onMark(UInt32 mark)
01035 {
01036     m_markReceived = mark;
01037     return true;
01038 }
01039 
01040 bool
01041 CMSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
01042 {
01043     static const KeyModifierMask s_ctrlAlt =
01044         KeyModifierControl | KeyModifierAlt;
01045 
01046     LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam));
01047 
01048     // get event info
01049     KeyButton button         = (KeyButton)((lParam & 0x01ff0000) >> 16);
01050     bool down                = ((lParam & 0x80000000u) == 0x00000000u);
01051     bool wasDown             = isKeyDown(button);
01052     KeyModifierMask oldState = pollActiveModifiers();
01053 
01054     // check for autorepeat
01055     if (m_keyState->testAutoRepeat(down, (lParam & 0x40000000u) == 1, button)) {
01056         lParam |= 0x40000000u;
01057     }
01058 
01059     // if the button is zero then guess what the button should be.
01060     // these are badly synthesized key events and logitech software
01061     // that maps mouse buttons to keys is known to do this.
01062     // alternatively, we could just throw these events out.
01063     if (button == 0) {
01064         button = m_keyState->virtualKeyToButton(wParam & 0xffu);
01065         if (button == 0) {
01066             return true;
01067         }
01068         wasDown = isKeyDown(button);
01069     }
01070 
01071     // record keyboard state
01072     m_keyState->onKey(button, down, oldState);
01073 
01074     // windows doesn't tell us the modifier key state on mouse or key
01075     // events so we have to figure it out.  most apps would use
01076     // GetKeyState() or even GetAsyncKeyState() for that but we can't
01077     // because our hook doesn't pass on key events for several modifiers.
01078     // it can't otherwise the system would interpret them normally on
01079     // the primary screen even when on a secondary screen.  so tapping
01080     // alt would activate menus and tapping the windows key would open
01081     // the start menu.  if you don't pass those events on in the hook
01082     // then GetKeyState() understandably doesn't reflect the effect of
01083     // the event.  curiously, neither does GetAsyncKeyState(), which is
01084     // surprising.
01085     //
01086     // so anyway, we have to track the modifier state ourselves for
01087     // at least those modifiers we don't pass on.  pollActiveModifiers()
01088     // does that but we have to update the keyboard state before calling
01089     // pollActiveModifiers() to get the right answer.  but the only way
01090     // to set the modifier state or to set the up/down state of a key
01091     // is via onKey().  so we have to call onKey() twice.
01092     KeyModifierMask state = pollActiveModifiers();
01093     m_keyState->onKey(button, down, state);
01094 
01095     // check for hot keys
01096     if (oldState != state) {
01097         // modifier key was pressed/released
01098         if (onHotKey(0, lParam)) {
01099             return true;
01100         }
01101     }
01102     else {
01103         // non-modifier was pressed/released
01104         if (onHotKey(wParam, lParam)) {
01105             return true;
01106         }
01107     }
01108 
01109     // ignore message if posted prior to last mark change
01110     if (!ignore()) {
01111         // check for ctrl+alt+del.  we do not want to pass that to the
01112         // client.  the user can use ctrl+alt+pause to emulate it.
01113         UINT virtKey = (wParam & 0xffu);
01114         if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) {
01115             LOG((CLOG_DEBUG "discard ctrl+alt+del"));
01116             return true;
01117         }
01118 
01119         // check for ctrl+alt+del emulation
01120         if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) &&
01121             (state & s_ctrlAlt) == s_ctrlAlt) {
01122             LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
01123             // switch wParam and lParam to be as if VK_DELETE was
01124             // pressed or released.  when mapping the key we require that
01125             // we not use AltGr (the 0x10000 flag in wParam) and we not
01126             // use the keypad delete key (the 0x01000000 flag in lParam).
01127             wParam  = VK_DELETE | 0x00010000u;
01128             lParam &= 0xfe000000;
01129             lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16;
01130             lParam |= 0x01000001;
01131         }
01132 
01133         // process key
01134         KeyModifierMask mask;
01135         KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask);
01136         button    = static_cast<KeyButton>((lParam & 0x01ff0000u) >> 16);
01137         if (key != kKeyNone) {
01138             // fix key up.  if the key isn't down according to
01139             // our table then we never got the key press event
01140             // for it.  if it's not a modifier key then we'll
01141             // synthesize the press first.  only do this on
01142             // the windows 95 family, which eats certain special
01143             // keys like alt+tab, ctrl+esc, etc.
01144             if (m_is95Family && !wasDown && !down) {
01145                 switch (virtKey) {
01146                 case VK_SHIFT:
01147                 case VK_LSHIFT:
01148                 case VK_RSHIFT:
01149                 case VK_CONTROL:
01150                 case VK_LCONTROL:
01151                 case VK_RCONTROL:
01152                 case VK_MENU:
01153                 case VK_LMENU:
01154                 case VK_RMENU:
01155                 case VK_LWIN:
01156                 case VK_RWIN:
01157                 case VK_CAPITAL:
01158                 case VK_NUMLOCK:
01159                 case VK_SCROLL:
01160                     break;
01161 
01162                 default:
01163                     m_keyState->sendKeyEvent(getEventTarget(),
01164                             true, false, key, mask, 1, button);
01165                     break;
01166                 }
01167             }
01168 
01169             // do it
01170             m_keyState->sendKeyEvent(getEventTarget(),
01171                             ((lParam & 0x80000000u) == 0),
01172                             ((lParam & 0x40000000u) != 0),
01173                             key, mask, (SInt32)(lParam & 0xffff), button);
01174         }
01175         else {
01176             LOG((CLOG_DEBUG1 "cannot map key"));
01177         }
01178     }
01179 
01180     return true;
01181 }
01182 
01183 bool
01184 CMSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam)
01185 {
01186     // get the key info
01187     KeyModifierMask state = getActiveModifiers();
01188     UINT virtKey   = (wParam & 0xffu);
01189     UINT modifiers = 0;
01190     if ((state & KeyModifierShift) != 0) {
01191         modifiers |= MOD_SHIFT;
01192     }
01193     if ((state & KeyModifierControl) != 0) {
01194         modifiers |= MOD_CONTROL;
01195     }
01196     if ((state & KeyModifierAlt) != 0) {
01197         modifiers |= MOD_ALT;
01198     }
01199     if ((state & KeyModifierSuper) != 0) {
01200         modifiers |= MOD_WIN;
01201     }
01202 
01203     // find the hot key id
01204     HotKeyToIDMap::const_iterator i =
01205         m_hotKeyToIDMap.find(CHotKeyItem(virtKey, modifiers));
01206     if (i == m_hotKeyToIDMap.end()) {
01207         return false;
01208     }
01209 
01210     // find what kind of event
01211     CEvent::Type type;
01212     if ((lParam & 0x80000000u) == 0u) {
01213         if ((lParam & 0x40000000u) != 0u) {
01214             // ignore key repeats but it counts as a hot key
01215             return true;
01216         }
01217         type = getHotKeyDownEvent();
01218     }
01219     else {
01220         type = getHotKeyUpEvent();
01221     }
01222 
01223     // generate event
01224     EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
01225                             CHotKeyInfo::alloc(i->second)));
01226 
01227     return true;
01228 }
01229 
01230 bool
01231 CMSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam)
01232 {
01233     // get which button
01234     bool pressed    = mapPressFromEvent(wParam, lParam);
01235     ButtonID button = mapButtonFromEvent(wParam, lParam);
01236 
01237     // keep our shadow key state up to date
01238     if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
01239         if (pressed) {
01240             m_buttons[button] = true;
01241         }
01242         else {
01243             m_buttons[button] = false;
01244         }
01245     }
01246 
01247     // ignore message if posted prior to last mark change
01248     if (!ignore()) {
01249         KeyModifierMask mask = m_keyState->getActiveModifiers();
01250         if (pressed) {
01251             LOG((CLOG_DEBUG1 "event: button press button=%d", button));
01252             if (button != kButtonNone) {
01253                 sendEvent(getButtonDownEvent(),
01254                                 CButtonInfo::alloc(button, mask));
01255             }
01256         }
01257         else {
01258             LOG((CLOG_DEBUG1 "event: button release button=%d", button));
01259             if (button != kButtonNone) {
01260                 sendEvent(getButtonUpEvent(),
01261                                 CButtonInfo::alloc(button, mask));
01262             }
01263         }
01264     }
01265 
01266     return true;
01267 }
01268 
01269 bool
01270 CMSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
01271 {
01272     // compute motion delta (relative to the last known
01273     // mouse position)
01274     SInt32 x = mx - m_xCursor;
01275     SInt32 y = my - m_yCursor;
01276 
01277     // ignore if the mouse didn't move or if message posted prior
01278     // to last mark change.
01279     if (ignore() || (x == 0 && y == 0)) {
01280         return true;
01281     }
01282 
01283     // save position to compute delta of next motion
01284     m_xCursor = mx;
01285     m_yCursor = my;
01286 
01287     if (m_isOnScreen) {
01288         // motion on primary screen
01289         sendEvent(getMotionOnPrimaryEvent(),
01290                             CMotionInfo::alloc(m_xCursor, m_yCursor));
01291     }
01292     else {
01293         // motion on secondary screen.  warp mouse back to
01294         // center.
01295         warpCursorNoFlush(m_xCenter, m_yCenter);
01296 
01297         // examine the motion.  if it's about the distance
01298         // from the center of the screen to an edge then
01299         // it's probably a bogus motion that we want to
01300         // ignore (see warpCursorNoFlush() for a further
01301         // description).
01302         static SInt32 bogusZoneSize = 10;
01303         if (-x + bogusZoneSize > m_xCenter - m_x ||
01304              x + bogusZoneSize > m_x + m_w - m_xCenter ||
01305             -y + bogusZoneSize > m_yCenter - m_y ||
01306              y + bogusZoneSize > m_y + m_h - m_yCenter) {
01307             LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
01308         }
01309         else {
01310             // send motion
01311             sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
01312         }
01313     }
01314 
01315     return true;
01316 }
01317 
01318 bool
01319 CMSWindowsScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
01320 {
01321     // ignore message if posted prior to last mark change
01322     if (!ignore()) {
01323         LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta));
01324         sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta));
01325     }
01326     return true;
01327 }
01328 
01329 bool
01330 CMSWindowsScreen::onScreensaver(bool activated)
01331 {
01332     // ignore this message if there are any other screen saver
01333     // messages already in the queue.  this is important because
01334     // our checkStarted() function has a deliberate delay, so it
01335     // can't respond to events at full CPU speed and will fall
01336     // behind if a lot of screen saver events are generated.
01337     // that can easily happen because windows will continually
01338     // send SC_SCREENSAVE until the screen saver starts, even if
01339     // the screen saver is disabled!
01340     MSG msg;
01341     if (PeekMessage(&msg, NULL, SYNERGY_MSG_SCREEN_SAVER,
01342                         SYNERGY_MSG_SCREEN_SAVER, PM_NOREMOVE)) {
01343         return true;
01344     }
01345 
01346     if (activated) {
01347         if (!m_screensaverActive &&
01348             m_screensaver->checkStarted(SYNERGY_MSG_SCREEN_SAVER, FALSE, 0)) {
01349             m_screensaverActive = true;
01350             sendEvent(getScreensaverActivatedEvent());
01351 
01352             // enable display power down
01353             CArchMiscWindows::removeBusyState(CArchMiscWindows::kDISPLAY);
01354         }
01355     }
01356     else {
01357         if (m_screensaverActive) {
01358             m_screensaverActive = false;
01359             sendEvent(getScreensaverDeactivatedEvent());
01360 
01361             // disable display power down
01362             CArchMiscWindows::addBusyState(CArchMiscWindows::kDISPLAY);
01363         }
01364     }
01365 
01366     return true;
01367 }
01368 
01369 bool
01370 CMSWindowsScreen::onDisplayChange()
01371 {
01372     // screen resolution may have changed.  save old shape.
01373     SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h;
01374 
01375     // update shape
01376     updateScreenShape();
01377 
01378     // do nothing if resolution hasn't changed
01379     if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) {
01380         if (m_isPrimary) {
01381             // warp mouse to center if off screen
01382             if (!m_isOnScreen) {
01383                 warpCursor(m_xCenter, m_yCenter);
01384             }
01385 
01386             // tell hook about resize if on screen
01387             else {
01388                 m_setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
01389             }
01390         }
01391 
01392         // send new screen info
01393         sendEvent(getShapeChangedEvent());
01394 
01395         LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
01396     }
01397 
01398     return true;
01399 }
01400 
01401 bool
01402 CMSWindowsScreen::onClipboardChange()
01403 {
01404     // now notify client that somebody changed the clipboard (unless
01405     // we're the owner).
01406     if (!CMSWindowsClipboard::isOwnedBySynergy()) {
01407         if (m_ownClipboard) {
01408             LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
01409             m_ownClipboard = false;
01410             sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
01411             sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
01412         }
01413     }
01414     else if (!m_ownClipboard) {
01415         LOG((CLOG_DEBUG "clipboard changed: synergy owned"));
01416         m_ownClipboard = true;
01417     }
01418 
01419     return true;
01420 }
01421 
01422 void
01423 CMSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
01424 {
01425     // send an event that we can recognize before the mouse warp
01426     PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_PRE_WARP, x, y);
01427 
01428     // warp mouse.  hopefully this inserts a mouse motion event
01429     // between the previous message and the following message.
01430     SetCursorPos(x, y);
01431 
01432     // yield the CPU.  there's a race condition when warping:
01433     //   a hardware mouse event occurs
01434     //   the mouse hook is not called because that process doesn't have the CPU
01435     //   we send PRE_WARP, SetCursorPos(), send POST_WARP
01436     //   we process all of those events and update m_x, m_y
01437     //   we finish our time slice
01438     //   the hook is called
01439     //   the hook sends us a mouse event from the pre-warp position
01440     //   we get the CPU
01441     //   we compute a bogus warp
01442     // we need the hook to process all mouse events that occur
01443     // before we warp before we do the warp but i'm not sure how
01444     // to guarantee that.  yielding the CPU here may reduce the
01445     // chance of undesired behavior.  we'll also check for very
01446     // large motions that look suspiciously like about half width
01447     // or height of the screen.
01448     ARCH->sleep(0.0);
01449 
01450     // send an event that we can recognize after the mouse warp
01451     PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_POST_WARP, 0, 0);
01452 }
01453 
01454 void
01455 CMSWindowsScreen::nextMark()
01456 {
01457     // next mark
01458     ++m_mark;
01459 
01460     // mark point in message queue where the mark was changed
01461     PostThreadMessage(GetCurrentThreadId(), SYNERGY_MSG_MARK, m_mark, 0);
01462 }
01463 
01464 bool
01465 CMSWindowsScreen::ignore() const
01466 {
01467     return (m_mark != m_markReceived);
01468 }
01469 
01470 void
01471 CMSWindowsScreen::updateScreenShape()
01472 {
01473     // get shape
01474     m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
01475     m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
01476     m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
01477     m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
01478 
01479     // get center for cursor
01480     m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
01481     m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
01482 
01483     // check for multiple monitors
01484     m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
01485                   m_h != GetSystemMetrics(SM_CYSCREEN));
01486 
01487     // tell the desks
01488     m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon);
01489 }
01490 
01491 void
01492 CMSWindowsScreen::handleFixes(const CEvent&, void*)
01493 {
01494     // fix clipboard chain
01495     fixClipboardViewer();
01496 
01497     // update keys if keyboard layouts have changed
01498     if (m_keyState->didGroupsChange()) {
01499         updateKeys();
01500     }
01501 }
01502 
01503 void
01504 CMSWindowsScreen::fixClipboardViewer()
01505 {
01506     // XXX -- disable this code for now.  somehow it can cause an infinite
01507     // recursion in the WM_DRAWCLIPBOARD handler.  either we're sending
01508     // the message to our own window or some window farther down the chain
01509     // forwards the message to our window or a window farther up the chain.
01510     // i'm not sure how that could happen.  the m_nextClipboardWindow = NULL
01511     // was not in the code that infinite loops and may fix the bug but i
01512     // doubt it.
01513 /*
01514     ChangeClipboardChain(m_window, m_nextClipboardWindow);
01515     m_nextClipboardWindow = NULL;
01516     m_nextClipboardWindow = SetClipboardViewer(m_window);
01517 */
01518 }
01519 
01520 void
01521 CMSWindowsScreen::enableSpecialKeys(bool enable) const
01522 {
01523     // enable/disable ctrl+alt+del, alt+tab, etc on win95 family.
01524     // since the win95 family doesn't support low-level hooks, we
01525     // use this undocumented feature to suppress normal handling
01526     // of certain key combinations.
01527     if (m_is95Family) {
01528         DWORD dummy = 0;
01529         SystemParametersInfo(SPI_SETSCREENSAVERRUNNING,
01530                             enable ? FALSE : TRUE, &dummy, 0);
01531     }
01532 }
01533 
01534 ButtonID
01535 CMSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const
01536 {
01537     switch (msg) {
01538     case WM_LBUTTONDOWN:
01539     case WM_LBUTTONDBLCLK:
01540     case WM_LBUTTONUP:
01541     case WM_NCLBUTTONDOWN:
01542     case WM_NCLBUTTONDBLCLK:
01543     case WM_NCLBUTTONUP:
01544         return kButtonLeft;
01545 
01546     case WM_MBUTTONDOWN:
01547     case WM_MBUTTONDBLCLK:
01548     case WM_MBUTTONUP:
01549     case WM_NCMBUTTONDOWN:
01550     case WM_NCMBUTTONDBLCLK:
01551     case WM_NCMBUTTONUP:
01552         return kButtonMiddle;
01553 
01554     case WM_RBUTTONDOWN:
01555     case WM_RBUTTONDBLCLK:
01556     case WM_RBUTTONUP:
01557     case WM_NCRBUTTONDOWN:
01558     case WM_NCRBUTTONDBLCLK:
01559     case WM_NCRBUTTONUP:
01560         return kButtonRight;
01561 
01562     case WM_XBUTTONDOWN:
01563     case WM_XBUTTONDBLCLK:
01564     case WM_XBUTTONUP:
01565     case WM_NCXBUTTONDOWN:
01566     case WM_NCXBUTTONDBLCLK:
01567     case WM_NCXBUTTONUP:
01568         switch (button) {
01569         case XBUTTON1:
01570             if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 4) {
01571                 return kButtonExtra0 + 0;
01572             }
01573             break;
01574 
01575         case XBUTTON2:
01576             if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 5) {
01577                 return kButtonExtra0 + 1;
01578             }
01579             break;
01580         }
01581         return kButtonNone;
01582 
01583     default:
01584         return kButtonNone;
01585     }
01586 }
01587 
01588 bool
01589 CMSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const
01590 {
01591     switch (msg) {
01592     case WM_LBUTTONDOWN:
01593     case WM_MBUTTONDOWN:
01594     case WM_RBUTTONDOWN:
01595     case WM_XBUTTONDOWN:
01596     case WM_LBUTTONDBLCLK:
01597     case WM_MBUTTONDBLCLK:
01598     case WM_RBUTTONDBLCLK:
01599     case WM_XBUTTONDBLCLK:
01600     case WM_NCLBUTTONDOWN:
01601     case WM_NCMBUTTONDOWN:
01602     case WM_NCRBUTTONDOWN:
01603     case WM_NCXBUTTONDOWN:
01604     case WM_NCLBUTTONDBLCLK:
01605     case WM_NCMBUTTONDBLCLK:
01606     case WM_NCRBUTTONDBLCLK:
01607     case WM_NCXBUTTONDBLCLK:
01608         return true;
01609 
01610     case WM_LBUTTONUP:
01611     case WM_MBUTTONUP:
01612     case WM_RBUTTONUP:
01613     case WM_XBUTTONUP:
01614     case WM_NCLBUTTONUP:
01615     case WM_NCMBUTTONUP:
01616     case WM_NCRBUTTONUP:
01617     case WM_NCXBUTTONUP:
01618         return false;
01619 
01620     default:
01621         return false;
01622     }
01623 }
01624 
01625 void
01626 CMSWindowsScreen::updateKeysCB(void*)
01627 {
01628     // record which keys we think are down
01629     bool down[IKeyState::kNumButtons];
01630     bool sendFixes = (isPrimary() && !m_isOnScreen);
01631     if (sendFixes) {
01632         for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
01633             down[i] = m_keyState->isKeyDown(i);
01634         }
01635     }
01636 
01637     // update layouts if necessary
01638     if (m_keyState->didGroupsChange()) {
01639         CPlatformScreen::updateKeyMap();
01640     }
01641 
01642     // now update the keyboard state
01643     CPlatformScreen::updateKeyState();
01644 
01645     // now see which keys we thought were down but now think are up.
01646     // send key releases for these keys to the active client.
01647     if (sendFixes) {
01648         KeyModifierMask mask = pollActiveModifiers();
01649         for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
01650             if (down[i] && !m_keyState->isKeyDown(i)) {
01651                 m_keyState->sendKeyEvent(getEventTarget(),
01652                             false, false, kKeyNone, mask, 1, i);
01653             }
01654         }
01655     }
01656 }
01657 
01658 void
01659 CMSWindowsScreen::forceShowCursor()
01660 {
01661     // check for mouse
01662     m_hasMouse = (GetSystemMetrics(SM_MOUSEPRESENT) != 0);
01663 
01664     // decide if we should show the mouse
01665     bool showMouse = (!m_hasMouse && !m_isPrimary && m_isOnScreen);
01666 
01667     // show/hide the mouse
01668     if (showMouse != m_showingMouse) {
01669         if (showMouse) {
01670             m_oldMouseKeys.cbSize = sizeof(m_oldMouseKeys);
01671             m_gotOldMouseKeys =
01672                 (SystemParametersInfo(SPI_GETMOUSEKEYS,
01673                             m_oldMouseKeys.cbSize,  &m_oldMouseKeys, 0) != 0);
01674             if (m_gotOldMouseKeys) {
01675                 m_mouseKeys    = m_oldMouseKeys;
01676                 m_showingMouse = true;
01677                 updateForceShowCursor();
01678             }
01679         }
01680         else {
01681             if (m_gotOldMouseKeys) {
01682                 SystemParametersInfo(SPI_SETMOUSEKEYS,
01683                             m_oldMouseKeys.cbSize,
01684                             &m_oldMouseKeys, SPIF_SENDCHANGE);
01685                 m_showingMouse = false;
01686             }
01687         }
01688     }
01689 }
01690 
01691 void
01692 CMSWindowsScreen::updateForceShowCursor()
01693 {
01694     DWORD oldFlags = m_mouseKeys.dwFlags;
01695 
01696     // turn on MouseKeys
01697     m_mouseKeys.dwFlags = MKF_AVAILABLE | MKF_MOUSEKEYSON;
01698 
01699     // make sure MouseKeys is active in whatever state the NumLock is
01700     // not currently in.
01701     if ((m_keyState->getActiveModifiers() & KeyModifierNumLock) != 0) {
01702         m_mouseKeys.dwFlags |= MKF_REPLACENUMBERS;
01703     }
01704 
01705     // update MouseKeys
01706     if (oldFlags != m_mouseKeys.dwFlags) {
01707         SystemParametersInfo(SPI_SETMOUSEKEYS,
01708                             m_mouseKeys.cbSize, &m_mouseKeys, SPIF_SENDCHANGE);
01709     }
01710 }
01711 
01712 LRESULT CALLBACK
01713 CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
01714 {
01715     assert(s_screen != NULL);
01716 
01717     LRESULT result = 0;
01718     if (!s_screen->onEvent(hwnd, msg, wParam, lParam, &result)) {
01719         result = DefWindowProc(hwnd, msg, wParam, lParam);
01720     }
01721 
01722     return result;
01723 }
01724 
01725 
01726 //
01727 // CMSWindowsScreen::CHotKeyItem
01728 //
01729 
01730 CMSWindowsScreen::CHotKeyItem::CHotKeyItem(UINT keycode, UINT mask) :
01731     m_keycode(keycode),
01732     m_mask(mask)
01733 {
01734     // do nothing
01735 }
01736 
01737 UINT
01738 CMSWindowsScreen::CHotKeyItem::getVirtualKey() const
01739 {
01740     return m_keycode;
01741 }
01742 
01743 bool
01744 CMSWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const
01745 {
01746     return (m_keycode < x.m_keycode ||
01747             (m_keycode == x.m_keycode && m_mask < x.m_mask));
01748 }

Generated on Fri Nov 6 00:18:45 2009 for synergy-plus by  doxygen 1.4.7