• Main Page
  • Classes
  • Files
  • File List

CMSWindowsScreen.cpp

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

Generated on Wed May 22 2013 00:00:05 for Synergy by  doxygen 1.7.1