CXWindowsScreen.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 "CXWindowsScreen.h"
00016 #include "CXWindowsClipboard.h"
00017 #include "CXWindowsEventQueueBuffer.h"
00018 #include "CXWindowsKeyState.h"
00019 #include "CXWindowsScreenSaver.h"
00020 #include "CXWindowsUtil.h"
00021 #include "CClipboard.h"
00022 #include "CKeyMap.h"
00023 #include "XScreen.h"
00024 #include "XArch.h"
00025 #include "CLog.h"
00026 #include "CStopwatch.h"
00027 #include "CStringUtil.h"
00028 #include "IEventQueue.h"
00029 #include "TMethodEventJob.h"
00030 #include <cstring>
00031 #include <cstdlib>
00032 #if X_DISPLAY_MISSING
00033 #   error X11 is required to build synergy
00034 #else
00035 #   include <X11/X.h>
00036 #   include <X11/Xutil.h>
00037 #   define XK_MISCELLANY
00038 #   define XK_XKB_KEYS
00039 #   include <X11/keysymdef.h>
00040     extern "C" {
00041 #   include <X11/extensions/dpms.h>
00042     }
00043 #   if HAVE_X11_EXTENSIONS_XTEST_H
00044 #       include <X11/extensions/XTest.h>
00045 #   else
00046 #       error The XTest extension is required to build synergy
00047 #   endif
00048 #   if HAVE_X11_EXTENSIONS_XINERAMA_H
00049         // Xinerama.h may lack extern "C" for inclusion by C++
00050         extern "C" {
00051 #       include <X11/extensions/Xinerama.h>
00052         }
00053 #   endif
00054 #   if HAVE_XKB_EXTENSION
00055 #       include <X11/XKBlib.h>
00056 #   endif
00057 #endif
00058 #include "CArch.h"
00059 
00060 
00061 //
00062 // CXWindowsScreen
00063 //
00064 
00065 // NOTE -- the X display is shared among several objects but is owned
00066 // by the CXWindowsScreen.  Xlib is not reentrant so we must ensure
00067 // that no two objects can simultaneously call Xlib with the display.
00068 // this is easy since we only make X11 calls from the main thread.
00069 // we must also ensure that these objects do not use the display in
00070 // their destructors or, if they do, we can tell them not to.  This
00071 // is to handle unexpected disconnection of the X display, when any
00072 // call on the display is invalid.  In that situation we discard the
00073 // display and the X11 event queue buffer, ignore any calls that try
00074 // to use the display, and wait to be destroyed.
00075 
00076 CXWindowsScreen*        CXWindowsScreen::s_screen = NULL;
00077 
00078 CXWindowsScreen::CXWindowsScreen(const char* displayName, bool isPrimary, int mouseScrollDelta) :
00079     m_isPrimary(isPrimary),
00080     m_mouseScrollDelta(mouseScrollDelta),
00081     m_display(NULL),
00082     m_root(None),
00083     m_window(None),
00084     m_isOnScreen(m_isPrimary),
00085     m_x(0), m_y(0),
00086     m_w(0), m_h(0),
00087     m_xCenter(0), m_yCenter(0),
00088     m_xCursor(0), m_yCursor(0),
00089     m_keyState(NULL),
00090     m_lastFocus(None),
00091     m_lastFocusRevert(RevertToNone),
00092     m_im(NULL),
00093     m_ic(NULL),
00094     m_lastKeycode(0),
00095     m_sequenceNumber(0),
00096     m_screensaver(NULL),
00097     m_screensaverNotify(false),
00098     m_xtestIsXineramaUnaware(true),
00099     m_preserveFocus(false),
00100     m_xkb(false)
00101 {
00102     assert(s_screen == NULL);
00103 
00104     if (mouseScrollDelta==0) m_mouseScrollDelta=120;
00105     s_screen = this;
00106     
00107     // initializes Xlib support for concurrent threads.
00108     if (XInitThreads() == 0)
00109     {
00110         throw XArch("XInitThreads() returned zero");
00111     }
00112     
00113 
00114     // set the X I/O error handler so we catch the display disconnecting
00115     XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler);
00116 
00117     try {
00118         m_display     = openDisplay(displayName);
00119         m_root        = DefaultRootWindow(m_display);
00120         saveShape();
00121         m_window      = openWindow();
00122         m_screensaver = new CXWindowsScreenSaver(m_display,
00123                                 m_window, getEventTarget());
00124         m_keyState    = new CXWindowsKeyState(m_display, m_xkb);
00125         LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : ""));
00126         LOG((CLOG_DEBUG "window is 0x%08x", m_window));
00127     }
00128     catch (...) {
00129         if (m_display != NULL) {
00130             XCloseDisplay(m_display);
00131         }
00132         throw;
00133     }
00134 
00135     // primary/secondary screen only initialization
00136     if (m_isPrimary) {
00137         // start watching for events on other windows
00138         selectEvents(m_root);
00139 
00140         // prepare to use input methods
00141         openIM();
00142     }
00143     else {
00144         // become impervious to server grabs
00145         XTestGrabControl(m_display, True);
00146     }
00147 
00148     // initialize the clipboards
00149     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00150         m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id);
00151     }
00152 
00153     // install event handlers
00154     EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(),
00155                             new TMethodEventJob<CXWindowsScreen>(this,
00156                                 &CXWindowsScreen::handleSystemEvent));
00157 
00158     // install the platform event queue
00159     EVENTQUEUE->adoptBuffer(new CXWindowsEventQueueBuffer(m_display, m_window));
00160 }
00161 
00162 CXWindowsScreen::~CXWindowsScreen()
00163 {
00164     assert(s_screen  != NULL);
00165     assert(m_display != NULL);
00166 
00167     EVENTQUEUE->adoptBuffer(NULL);
00168     EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget());
00169     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00170         delete m_clipboard[id];
00171     }
00172     delete m_keyState;
00173     delete m_screensaver;
00174     m_keyState    = NULL;
00175     m_screensaver = NULL;
00176     if (m_display != NULL) {
00177         // FIXME -- is it safe to clean up the IC and IM without a display?
00178         if (m_ic != NULL) {
00179             XDestroyIC(m_ic);
00180         }
00181         if (m_im != NULL) {
00182             XCloseIM(m_im);
00183         }
00184         XDestroyWindow(m_display, m_window);
00185         XCloseDisplay(m_display);
00186     }
00187     XSetIOErrorHandler(NULL);
00188 
00189     s_screen = NULL;
00190 }
00191 
00192 void
00193 CXWindowsScreen::enable()
00194 {
00195     if (!m_isPrimary) {
00196         // get the keyboard control state
00197         XKeyboardState keyControl;
00198         XGetKeyboardControl(m_display, &keyControl);
00199         m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
00200         m_keyState->setAutoRepeat(keyControl);
00201 
00202         // move hider window under the cursor center
00203         XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
00204 
00205         // raise and show the window
00206         // FIXME -- take focus?
00207         XMapRaised(m_display, m_window);
00208 
00209         // warp the mouse to the cursor center
00210         fakeMouseMove(m_xCenter, m_yCenter);
00211     }
00212 }
00213 
00214 void
00215 CXWindowsScreen::disable()
00216 {
00217     // release input context focus
00218     if (m_ic != NULL) {
00219         XUnsetICFocus(m_ic);
00220     }
00221 
00222     // unmap the hider/grab window.  this also ungrabs the mouse and
00223     // keyboard if they're grabbed.
00224     XUnmapWindow(m_display, m_window);
00225 
00226     // restore auto-repeat state
00227     if (!m_isPrimary && m_autoRepeat) {
00228         //XAutoRepeatOn(m_display);
00229     }
00230 }
00231 
00232 void
00233 CXWindowsScreen::enter()
00234 {
00235     screensaver(false);
00236 
00237     // release input context focus
00238     if (m_ic != NULL) {
00239         XUnsetICFocus(m_ic);
00240     }
00241 
00242     // set the input focus to what it had been when we took it
00243     if (m_lastFocus != None) {
00244         // the window may not exist anymore so ignore errors
00245         CXWindowsUtil::CErrorLock lock(m_display);
00246         XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime);
00247     }
00248 
00249     // Force the DPMS to turn screen back on since we don't
00250     // actually cause physical hardware input to trigger it
00251     int dummy;
00252     CARD16 powerlevel;
00253     BOOL enabled;
00254     if (DPMSQueryExtension(m_display, &dummy, &dummy) &&
00255         DPMSCapable(m_display) &&
00256         DPMSInfo(m_display, &powerlevel, &enabled))
00257     {
00258         if (enabled && powerlevel != DPMSModeOn)
00259             DPMSForceLevel(m_display, DPMSModeOn);
00260     }
00261 
00262     // unmap the hider/grab window.  this also ungrabs the mouse and
00263     // keyboard if they're grabbed.
00264     XUnmapWindow(m_display, m_window);
00265 
00266 /* maybe call this if entering for the screensaver
00267     // set keyboard focus to root window.  the screensaver should then
00268     // pick up key events for when the user enters a password to unlock. 
00269     XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime);
00270 */
00271 
00272     if (!m_isPrimary) {
00273         // get the keyboard control state
00274         XKeyboardState keyControl;
00275         XGetKeyboardControl(m_display, &keyControl);
00276         m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
00277         m_keyState->setAutoRepeat(keyControl);
00278 
00279         // turn off auto-repeat.  we do this so fake key press events don't
00280         // cause the local server to generate their own auto-repeats of
00281         // those keys.
00282         //XAutoRepeatOff(m_display);
00283     }
00284 
00285     // now on screen
00286     m_isOnScreen = true;
00287 }
00288 
00289 bool
00290 CXWindowsScreen::leave()
00291 {
00292     if (!m_isPrimary) {
00293         // restore the previous keyboard auto-repeat state.  if the user
00294         // changed the auto-repeat configuration while on the client then
00295         // that state is lost.  that's because we can't get notified by
00296         // the X server when the auto-repeat configuration is changed so
00297         // we can't track the desired configuration.
00298         if (m_autoRepeat) {
00299             //XAutoRepeatOn(m_display);
00300         }
00301 
00302         // move hider window under the cursor center
00303         XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
00304     }
00305 
00306     // raise and show the window
00307     XMapRaised(m_display, m_window);
00308 
00309     // grab the mouse and keyboard, if primary and possible
00310     if (m_isPrimary && !grabMouseAndKeyboard()) {
00311         XUnmapWindow(m_display, m_window);
00312         return false;
00313     }
00314 
00315     // save current focus
00316     XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert);
00317 
00318     // take focus
00319     if (m_isPrimary || !m_preserveFocus) {
00320         XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
00321     }
00322 
00323     // now warp the mouse.  we warp after showing the window so we're
00324     // guaranteed to get the mouse leave event and to prevent the
00325     // keyboard focus from changing under point-to-focus policies.
00326     if (m_isPrimary) {
00327         warpCursor(m_xCenter, m_yCenter);
00328     }
00329     else {
00330         fakeMouseMove(m_xCenter, m_yCenter);
00331     }
00332 
00333     // set input context focus to our window
00334     if (m_ic != NULL) {
00335         XmbResetIC(m_ic);
00336         XSetICFocus(m_ic);
00337         m_filtered.clear();
00338     }
00339 
00340     // now off screen
00341     m_isOnScreen = false;
00342 
00343     return true;
00344 }
00345 
00346 bool
00347 CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard)
00348 {
00349     // fail if we don't have the requested clipboard
00350     if (m_clipboard[id] == NULL) {
00351         return false;
00352     }
00353 
00354     // get the actual time.  ICCCM does not allow CurrentTime.
00355     Time timestamp = CXWindowsUtil::getCurrentTime(
00356                                 m_display, m_clipboard[id]->getWindow());
00357 
00358     if (clipboard != NULL) {
00359         // save clipboard data
00360         return CClipboard::copy(m_clipboard[id], clipboard, timestamp);
00361     }
00362     else {
00363         // assert clipboard ownership
00364         if (!m_clipboard[id]->open(timestamp)) {
00365             return false;
00366         }
00367         m_clipboard[id]->empty();
00368         m_clipboard[id]->close();
00369         return true;
00370     }
00371 }
00372 
00373 void
00374 CXWindowsScreen::checkClipboards()
00375 {
00376     // do nothing, we're always up to date
00377 }
00378 
00379 void
00380 CXWindowsScreen::openScreensaver(bool notify)
00381 {
00382     m_screensaverNotify = notify;
00383     if (!m_screensaverNotify) {
00384         m_screensaver->disable();
00385     }
00386 }
00387 
00388 void
00389 CXWindowsScreen::closeScreensaver()
00390 {
00391     if (!m_screensaverNotify) {
00392         m_screensaver->enable();
00393     }
00394 }
00395 
00396 void
00397 CXWindowsScreen::screensaver(bool activate)
00398 {
00399     if (activate) {
00400         m_screensaver->activate();
00401     }
00402     else {
00403         m_screensaver->deactivate();
00404     }
00405 }
00406 
00407 void
00408 CXWindowsScreen::resetOptions()
00409 {
00410     m_xtestIsXineramaUnaware = true;
00411     m_preserveFocus = false;
00412 }
00413 
00414 void
00415 CXWindowsScreen::setOptions(const COptionsList& options)
00416 {
00417     for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
00418         if (options[i] == kOptionXTestXineramaUnaware) {
00419             m_xtestIsXineramaUnaware = (options[i + 1] != 0);
00420             LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false"));
00421         }
00422         else if (options[i] == kOptionScreenPreserveFocus) {
00423             m_preserveFocus = (options[i + 1] != 0);
00424             LOG((CLOG_DEBUG1 "Preserve Focus = %s", m_preserveFocus ? "true" : "false"));
00425         }
00426     }
00427 }
00428 
00429 void
00430 CXWindowsScreen::setSequenceNumber(UInt32 seqNum)
00431 {
00432     m_sequenceNumber = seqNum;
00433 }
00434 
00435 bool
00436 CXWindowsScreen::isPrimary() const
00437 {
00438     return m_isPrimary;
00439 }
00440 
00441 void*
00442 CXWindowsScreen::getEventTarget() const
00443 {
00444     return const_cast<CXWindowsScreen*>(this);
00445 }
00446 
00447 bool
00448 CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const
00449 {
00450     assert(clipboard != NULL);
00451 
00452     // fail if we don't have the requested clipboard
00453     if (m_clipboard[id] == NULL) {
00454         return false;
00455     }
00456 
00457     // get the actual time.  ICCCM does not allow CurrentTime.
00458     Time timestamp = CXWindowsUtil::getCurrentTime(
00459                                 m_display, m_clipboard[id]->getWindow());
00460 
00461     // copy the clipboard
00462     return CClipboard::copy(clipboard, m_clipboard[id], timestamp);
00463 }
00464 
00465 void
00466 CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
00467 {
00468     x = m_x;
00469     y = m_y;
00470     w = m_w;
00471     h = m_h;
00472 }
00473 
00474 void
00475 CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
00476 {
00477     Window root, window;
00478     int mx, my, xWindow, yWindow;
00479     unsigned int mask;
00480     if (XQueryPointer(m_display, m_root, &root, &window,
00481                                 &mx, &my, &xWindow, &yWindow, &mask)) {
00482         x = mx;
00483         y = my;
00484     }
00485     else {
00486         x = m_xCenter;
00487         y = m_yCenter;
00488     }
00489 }
00490 
00491 void
00492 CXWindowsScreen::reconfigure(UInt32)
00493 {
00494     // do nothing
00495 }
00496 
00497 void
00498 CXWindowsScreen::warpCursor(SInt32 x, SInt32 y)
00499 {
00500     // warp mouse
00501     warpCursorNoFlush(x, y);
00502 
00503     // remove all input events before and including warp
00504     XEvent event;
00505     while (XCheckMaskEvent(m_display, PointerMotionMask |
00506                                 ButtonPressMask | ButtonReleaseMask |
00507                                 KeyPressMask | KeyReleaseMask |
00508                                 KeymapStateMask,
00509                                 &event)) {
00510         // do nothing
00511     }
00512 
00513     // save position as last position
00514     m_xCursor = x;
00515     m_yCursor = y;
00516 }
00517 
00518 UInt32
00519 CXWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
00520 {
00521     // only allow certain modifiers
00522     if ((mask & ~(KeyModifierShift | KeyModifierControl |
00523                   KeyModifierAlt   | KeyModifierSuper)) != 0) {
00524         LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00525         return 0;
00526     }
00527 
00528     // fail if no keys
00529     if (key == kKeyNone && mask == 0) {
00530         return 0;
00531     }
00532 
00533     // convert to X
00534     unsigned int modifiers;
00535     if (!m_keyState->mapModifiersToX(mask, modifiers)) {
00536         // can't map all modifiers
00537         LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00538         return 0;
00539     }
00540     CXWindowsKeyState::CKeycodeList keycodes;
00541     m_keyState->mapKeyToKeycodes(key, keycodes);
00542     if (key != kKeyNone && keycodes.empty()) {
00543         // can't map key
00544         LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00545         return 0;
00546     }
00547 
00548     // choose hotkey id
00549     UInt32 id;
00550     if (!m_oldHotKeyIDs.empty()) {
00551         id = m_oldHotKeyIDs.back();
00552         m_oldHotKeyIDs.pop_back();
00553     }
00554     else {
00555         id = m_hotKeys.size() + 1;
00556     }
00557     HotKeyList& hotKeys = m_hotKeys[id];
00558 
00559     // all modifier hotkey must be treated specially.  for each modifier
00560     // we need to grab the modifier key in combination with all the other
00561     // requested modifiers.
00562     bool err = false;
00563     {
00564         CXWindowsUtil::CErrorLock lock(m_display, &err);
00565         if (key == kKeyNone) {
00566             static const KeyModifierMask s_hotKeyModifiers[] = {
00567                 KeyModifierShift,
00568                 KeyModifierControl,
00569                 KeyModifierAlt,
00570                 KeyModifierMeta,
00571                 KeyModifierSuper
00572             };
00573 
00574             XModifierKeymap* modKeymap = XGetModifierMapping(m_display);
00575             for (size_t j = 0; j < sizeof(s_hotKeyModifiers) /
00576                                     sizeof(s_hotKeyModifiers[0]) && !err; ++j) {
00577                 // skip modifier if not in mask
00578                 if ((mask & s_hotKeyModifiers[j]) == 0) {
00579                     continue;
00580                 }
00581 
00582                 // skip with error if we can't map remaining modifiers
00583                 unsigned int modifiers2;
00584                 KeyModifierMask mask2 = (mask & ~s_hotKeyModifiers[j]);
00585                 if (!m_keyState->mapModifiersToX(mask2, modifiers2)) {
00586                     err = true;
00587                     continue;
00588                 }
00589 
00590                 // compute modifier index for modifier.  there should be
00591                 // exactly one X modifier missing
00592                 int index;
00593                 switch (modifiers ^ modifiers2) {
00594                 case ShiftMask:
00595                     index = ShiftMapIndex;
00596                     break;
00597 
00598                 case LockMask:
00599                     index = LockMapIndex;
00600                     break;
00601 
00602                 case ControlMask:
00603                     index = ControlMapIndex;
00604                     break;
00605 
00606                 case Mod1Mask:
00607                     index = Mod1MapIndex;
00608                     break;
00609 
00610                 case Mod2Mask:
00611                     index = Mod2MapIndex;
00612                     break;
00613 
00614                 case Mod3Mask:
00615                     index = Mod3MapIndex;
00616                     break;
00617 
00618                 case Mod4Mask:
00619                     index = Mod4MapIndex;
00620                     break;
00621 
00622                 case Mod5Mask:
00623                     index = Mod5MapIndex;
00624                     break;
00625 
00626                 default:
00627                     err = true;
00628                     continue;
00629                 }
00630 
00631                 // grab each key for the modifier
00632                 const KeyCode* modifiermap =
00633                     modKeymap->modifiermap + index * modKeymap->max_keypermod;
00634                 for (int k = 0; k < modKeymap->max_keypermod && !err; ++k) {
00635                     KeyCode code = modifiermap[k];
00636                     if (modifiermap[k] != 0) {
00637                         XGrabKey(m_display, code, modifiers2, m_root,
00638                                     False, GrabModeAsync, GrabModeAsync);
00639                         if (!err) {
00640                             hotKeys.push_back(std::make_pair(code, modifiers2));
00641                             m_hotKeyToIDMap[CHotKeyItem(code, modifiers2)] = id;
00642                         }
00643                     }
00644                 }
00645             }
00646             XFreeModifiermap(modKeymap);
00647         }
00648 
00649         // a non-modifier key must be insensitive to CapsLock, NumLock and
00650         // ScrollLock, so we have to grab the key with every combination of
00651         // those.
00652         else {
00653             // collect available toggle modifiers
00654             unsigned int modifier;
00655             unsigned int toggleModifiers[3];
00656             size_t numToggleModifiers = 0;
00657             if (m_keyState->mapModifiersToX(KeyModifierCapsLock, modifier)) {
00658                 toggleModifiers[numToggleModifiers++] = modifier;
00659             }
00660             if (m_keyState->mapModifiersToX(KeyModifierNumLock, modifier)) {
00661                 toggleModifiers[numToggleModifiers++] = modifier;
00662             }
00663             if (m_keyState->mapModifiersToX(KeyModifierScrollLock, modifier)) {
00664                 toggleModifiers[numToggleModifiers++] = modifier;
00665             }
00666 
00667 
00668             for (CXWindowsKeyState::CKeycodeList::iterator j = keycodes.begin();
00669                                     j != keycodes.end() && !err; ++j) {
00670                 for (size_t i = 0; i < (1u << numToggleModifiers); ++i) {
00671                     // add toggle modifiers for index i
00672                     unsigned int tmpModifiers = modifiers;
00673                     if ((i & 1) != 0) {
00674                         tmpModifiers |= toggleModifiers[0];
00675                     }
00676                     if ((i & 2) != 0) {
00677                         tmpModifiers |= toggleModifiers[1];
00678                     }
00679                     if ((i & 4) != 0) {
00680                         tmpModifiers |= toggleModifiers[2];
00681                     }
00682 
00683                     // add grab
00684                     XGrabKey(m_display, *j, tmpModifiers, m_root,
00685                                         False, GrabModeAsync, GrabModeAsync);
00686                     if (!err) {
00687                         hotKeys.push_back(std::make_pair(*j, tmpModifiers));
00688                         m_hotKeyToIDMap[CHotKeyItem(*j, tmpModifiers)] = id;
00689                     }
00690                 }
00691             }
00692         }
00693     }
00694 
00695     if (err) {
00696         // if any failed then unregister any we did get
00697         for (HotKeyList::iterator j = hotKeys.begin();
00698                                 j != hotKeys.end(); ++j) {
00699             XUngrabKey(m_display, j->first, j->second, m_root);
00700             m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second));
00701         }
00702 
00703         m_oldHotKeyIDs.push_back(id);
00704         m_hotKeys.erase(id);
00705         LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask));
00706         return 0;
00707     }
00708     
00709     LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id));
00710     return id;
00711 }
00712 
00713 void
00714 CXWindowsScreen::unregisterHotKey(UInt32 id)
00715 {
00716     // look up hotkey
00717     HotKeyMap::iterator i = m_hotKeys.find(id);
00718     if (i == m_hotKeys.end()) {
00719         return;
00720     }
00721 
00722     // unregister with OS
00723     bool err = false;
00724     {
00725         CXWindowsUtil::CErrorLock lock(m_display, &err);
00726         HotKeyList& hotKeys = i->second;
00727         for (HotKeyList::iterator j = hotKeys.begin();
00728                                 j != hotKeys.end(); ++j) {
00729             XUngrabKey(m_display, j->first, j->second, m_root);
00730             m_hotKeyToIDMap.erase(CHotKeyItem(j->first, j->second));
00731         }
00732     }
00733     if (err) {
00734         LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
00735     }
00736     else {
00737         LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
00738     }
00739 
00740     // discard hot key from map and record old id for reuse
00741     m_hotKeys.erase(i);
00742     m_oldHotKeyIDs.push_back(id);
00743 }
00744 
00745 void
00746 CXWindowsScreen::fakeInputBegin()
00747 {
00748     // FIXME -- not implemented
00749 }
00750 
00751 void
00752 CXWindowsScreen::fakeInputEnd()
00753 {
00754     // FIXME -- not implemented
00755 }
00756 
00757 SInt32
00758 CXWindowsScreen::getJumpZoneSize() const
00759 {
00760     return 1;
00761 }
00762 
00763 bool
00764 CXWindowsScreen::isAnyMouseButtonDown() const
00765 {
00766     // query the pointer to get the button state
00767     Window root, window;
00768     int xRoot, yRoot, xWindow, yWindow;
00769     unsigned int state;
00770     if (XQueryPointer(m_display, m_root, &root, &window,
00771                                 &xRoot, &yRoot, &xWindow, &yWindow, &state)) {
00772         return ((state & (Button1Mask | Button2Mask | Button3Mask |
00773                             Button4Mask | Button5Mask)) != 0);
00774     }
00775 
00776     return false;
00777 }
00778 
00779 void
00780 CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
00781 {
00782     x = m_xCenter;
00783     y = m_yCenter;
00784 }
00785 
00786 void
00787 CXWindowsScreen::fakeMouseButton(ButtonID button, bool press) const
00788 {
00789     const unsigned int xButton = mapButtonToX(button);
00790     if (xButton != 0) {
00791         XTestFakeButtonEvent(m_display, xButton,
00792                             press ? True : False, CurrentTime);
00793         XFlush(m_display);
00794     }
00795 }
00796 
00797 void
00798 CXWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y) const
00799 {
00800     if (m_xinerama && m_xtestIsXineramaUnaware) {
00801         XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
00802     }
00803     else {
00804         XTestFakeMotionEvent(m_display, DefaultScreen(m_display),
00805                             x, y, CurrentTime);
00806     }
00807     XFlush(m_display);
00808 }
00809 
00810 void
00811 CXWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
00812 {
00813     // FIXME -- ignore xinerama for now
00814     if (false && m_xinerama && m_xtestIsXineramaUnaware) {
00815 //      XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
00816     }
00817     else {
00818         XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime);
00819     }
00820     XFlush(m_display);
00821 }
00822 
00823 void
00824 CXWindowsScreen::fakeMouseWheel(SInt32, SInt32 yDelta) const
00825 {
00826     // XXX -- support x-axis scrolling
00827     if (yDelta == 0) {
00828         return;
00829     }
00830 
00831     // choose button depending on rotation direction
00832     const unsigned int xButton = mapButtonToX(static_cast<ButtonID>(
00833                                                 (yDelta >= 0) ? -1 : -2));
00834     if (xButton == 0) {
00835         // If we get here, then the XServer does not support the scroll
00836         // wheel buttons, so send PageUp/PageDown keystrokes instead.
00837         // Patch by Tom Chadwick.
00838         KeyCode keycode = 0;
00839         if (yDelta >= 0) {
00840             keycode = XKeysymToKeycode(m_display, XK_Page_Up);
00841         }
00842         else {
00843             keycode = XKeysymToKeycode(m_display, XK_Page_Down);
00844         }
00845         if (keycode != 0) {
00846             XTestFakeKeyEvent(m_display, keycode, True,  CurrentTime);
00847             XTestFakeKeyEvent(m_display, keycode, False, CurrentTime);
00848         }
00849         return;
00850     }
00851 
00852     // now use absolute value of delta
00853     if (yDelta < 0) {
00854         yDelta = -yDelta;
00855     }
00856 
00857     if (yDelta < m_mouseScrollDelta) {
00858         LOG((CLOG_WARN "Wheel scroll delta (%d) smaller than threshold (%d)", yDelta, m_mouseScrollDelta));
00859     }
00860 
00861     // send as many clicks as necessary
00862     for (; yDelta >= m_mouseScrollDelta; yDelta -= m_mouseScrollDelta) {
00863         XTestFakeButtonEvent(m_display, xButton, True, CurrentTime);
00864         XTestFakeButtonEvent(m_display, xButton, False, CurrentTime);
00865     }
00866     XFlush(m_display);
00867 }
00868 
00869 Display*
00870 CXWindowsScreen::openDisplay(const char* displayName)
00871 {
00872     // get the DISPLAY
00873     if (displayName == NULL) {
00874         displayName = getenv("DISPLAY");
00875         if (displayName == NULL) {
00876             displayName = ":0.0";
00877         }
00878     }
00879 
00880     // open the display
00881     LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName));
00882     Display* display = XOpenDisplay(displayName);
00883     if (display == NULL) {
00884         throw XScreenUnavailable(60.0);
00885     }
00886 
00887     // verify the availability of the XTest extension
00888     if (!m_isPrimary) {
00889         int majorOpcode, firstEvent, firstError;
00890         if (!XQueryExtension(display, XTestExtensionName,
00891                             &majorOpcode, &firstEvent, &firstError)) {
00892             LOG((CLOG_ERR "XTEST extension not available"));
00893             XCloseDisplay(display);
00894             throw XScreenOpenFailure();
00895         }
00896     }
00897 
00898 #if HAVE_XKB_EXTENSION
00899     {
00900         m_xkb = false;
00901         int major = XkbMajorVersion, minor = XkbMinorVersion;
00902         if (XkbLibraryVersion(&major, &minor)) {
00903             int opcode, firstError;
00904             if (XkbQueryExtension(display, &opcode, &m_xkbEventBase,
00905                                 &firstError, &major, &minor)) {
00906                 m_xkb = true;
00907                 XkbSelectEvents(display, XkbUseCoreKbd,
00908                                 XkbMapNotifyMask, XkbMapNotifyMask);
00909                 XkbSelectEventDetails(display, XkbUseCoreKbd,
00910                                 XkbStateNotifyMask,
00911                                 XkbGroupStateMask, XkbGroupStateMask);
00912             }
00913         }
00914     }
00915 #endif
00916 
00917     return display;
00918 }
00919 
00920 void
00921 CXWindowsScreen::saveShape()
00922 {
00923     // get shape of default screen
00924     m_x = 0;
00925     m_y = 0;
00926     m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display));
00927     m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display));
00928 
00929     // get center of default screen
00930     m_xCenter = m_x + (m_w >> 1);
00931     m_yCenter = m_y + (m_h >> 1);
00932 
00933     // check if xinerama is enabled and there is more than one screen.
00934     // get center of first Xinerama screen.  Xinerama appears to have
00935     // a bug when XWarpPointer() is used in combination with
00936     // XGrabPointer().  in that case, the warp is successful but the
00937     // next pointer motion warps the pointer again, apparently to
00938     // constrain it to some unknown region, possibly the region from
00939     // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over
00940     // all physical screens.  this warp only seems to happen if the
00941     // pointer wasn't in that region before the XWarpPointer().  the
00942     // second (unexpected) warp causes synergy to think the pointer
00943     // has been moved when it hasn't.  to work around the problem,
00944     // we warp the pointer to the center of the first physical
00945     // screen instead of the logical screen.
00946     m_xinerama = false;
00947 #if HAVE_X11_EXTENSIONS_XINERAMA_H
00948     int eventBase, errorBase;
00949     if (XineramaQueryExtension(m_display, &eventBase, &errorBase) &&
00950         XineramaIsActive(m_display)) {
00951         int numScreens;
00952         XineramaScreenInfo* screens;
00953         screens = XineramaQueryScreens(m_display, &numScreens);
00954         if (screens != NULL) {
00955             if (numScreens > 1) {
00956                 m_xinerama = true;
00957                 m_xCenter  = screens[0].x_org + (screens[0].width  >> 1);
00958                 m_yCenter  = screens[0].y_org + (screens[0].height >> 1);
00959             }
00960             XFree(screens);
00961         }
00962     }
00963 #endif
00964 }
00965 
00966 Window
00967 CXWindowsScreen::openWindow() const
00968 {
00969     // default window attributes.  we don't want the window manager
00970     // messing with our window and we don't want the cursor to be
00971     // visible inside the window.
00972     XSetWindowAttributes attr;
00973     attr.do_not_propagate_mask = 0;
00974     attr.override_redirect     = True;
00975     attr.cursor                = createBlankCursor();
00976 
00977     // adjust attributes and get size and shape
00978     SInt32 x, y, w, h;
00979     if (m_isPrimary) {
00980         // grab window attributes.  this window is used to capture user
00981         // input when the user is focused on another client.  it covers
00982         // the whole screen.
00983         attr.event_mask = PointerMotionMask |
00984                              ButtonPressMask | ButtonReleaseMask |
00985                              KeyPressMask | KeyReleaseMask |
00986                              KeymapStateMask | PropertyChangeMask;
00987         x = m_x;
00988         y = m_y;
00989         w = m_w;
00990         h = m_h;
00991     }
00992     else {
00993         // cursor hider window attributes.  this window is used to hide the
00994         // cursor when it's not on the screen.  the window is hidden as soon
00995         // as the cursor enters the screen or the display's real mouse is
00996         // moved.  we'll reposition the window as necessary so its
00997         // position here doesn't matter.  it only needs to be 1x1 because
00998         // it only needs to contain the cursor's hotspot.
00999         attr.event_mask = LeaveWindowMask;
01000         x = 0;
01001         y = 0;
01002         w = 1;
01003         h = 1;
01004     }
01005 
01006     // create and return the window
01007     Window window = XCreateWindow(m_display, m_root, x, y, w, h, 0, 0,
01008                             InputOnly, CopyFromParent,
01009                             CWDontPropagate | CWEventMask |
01010                             CWOverrideRedirect | CWCursor,
01011                             &attr);
01012     if (window == None) {
01013         throw XScreenOpenFailure();
01014     }
01015     return window;
01016 }
01017 
01018 void
01019 CXWindowsScreen::openIM()
01020 {
01021     // open the input methods
01022     XIM im = XOpenIM(m_display, NULL, NULL, NULL);
01023     if (im == NULL) {
01024         LOG((CLOG_INFO "no support for IM"));
01025         return;
01026     }
01027 
01028     // find the appropriate style.  synergy supports XIMPreeditNothing
01029     // only at the moment.
01030     XIMStyles* styles;
01031     if (XGetIMValues(im, XNQueryInputStyle, &styles, NULL) != NULL ||
01032         styles == NULL) {
01033         LOG((CLOG_WARN "cannot get IM styles"));
01034         XCloseIM(im);
01035         return;
01036     }
01037     XIMStyle style = 0;
01038     for (unsigned short i = 0; i < styles->count_styles; ++i) {
01039         style = styles->supported_styles[i];
01040         if ((style & XIMPreeditNothing) != 0) {
01041             if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) {
01042                 break;
01043             }
01044         }
01045     }
01046     XFree(styles);
01047     if (style == 0) {
01048         LOG((CLOG_INFO "no supported IM styles"));
01049         XCloseIM(im);
01050         return;
01051     }
01052 
01053     // create an input context for the style and tell it about our window
01054     XIC ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, m_window, NULL);
01055     if (ic == NULL) {
01056         LOG((CLOG_WARN "cannot create IC"));
01057         XCloseIM(im);
01058         return;
01059     }
01060 
01061     // find out the events we must select for and do so
01062     unsigned long mask;
01063     if (XGetICValues(ic, XNFilterEvents, &mask, NULL) != NULL) {
01064         LOG((CLOG_WARN "cannot get IC filter events"));
01065         XDestroyIC(ic);
01066         XCloseIM(im);
01067         return;
01068     }
01069 
01070     // we have IM
01071     m_im          = im;
01072     m_ic          = ic;
01073     m_lastKeycode = 0;
01074 
01075     // select events on our window that IM requires
01076     XWindowAttributes attr;
01077     XGetWindowAttributes(m_display, m_window, &attr);
01078     XSelectInput(m_display, m_window, attr.your_event_mask | mask);
01079 }
01080 
01081 void
01082 CXWindowsScreen::sendEvent(CEvent::Type type, void* data)
01083 {
01084     EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
01085 }
01086 
01087 void
01088 CXWindowsScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id)
01089 {
01090     CClipboardInfo* info   = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
01091     info->m_id             = id;
01092     info->m_sequenceNumber = m_sequenceNumber;
01093     sendEvent(type, info);
01094 }
01095 
01096 IKeyState*
01097 CXWindowsScreen::getKeyState() const
01098 {
01099     return m_keyState;
01100 }
01101 
01102 Bool
01103 CXWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
01104 {
01105     CKeyEventFilter* filter = reinterpret_cast<CKeyEventFilter*>(arg);
01106     return (xevent->type         == filter->m_event &&
01107             xevent->xkey.window  == filter->m_window &&
01108             xevent->xkey.time    == filter->m_time &&
01109             xevent->xkey.keycode == filter->m_keycode) ? True : False;
01110 }
01111 
01112 void
01113 CXWindowsScreen::handleSystemEvent(const CEvent& event, void*)
01114 {
01115     XEvent* xevent = reinterpret_cast<XEvent*>(event.getData());
01116     assert(xevent != NULL);
01117 
01118     // update key state
01119     bool isRepeat = false;
01120     if (m_isPrimary) {
01121         if (xevent->type == KeyRelease) {
01122             // check if this is a key repeat by getting the next
01123             // KeyPress event that has the same key and time as
01124             // this release event, if any.  first prepare the
01125             // filter info.
01126             CKeyEventFilter filter;
01127             filter.m_event   = KeyPress;
01128             filter.m_window  = xevent->xkey.window;
01129             filter.m_time    = xevent->xkey.time;
01130             filter.m_keycode = xevent->xkey.keycode;
01131             XEvent xevent2;
01132             isRepeat = (XCheckIfEvent(m_display, &xevent2,
01133                             &CXWindowsScreen::findKeyEvent,
01134                             (XPointer)&filter) == True);
01135         }
01136 
01137         if (xevent->type == KeyPress || xevent->type == KeyRelease) {
01138             if (xevent->xkey.window == m_root) {
01139                 // this is a hot key
01140                 onHotKey(xevent->xkey, isRepeat);
01141                 return;
01142             }
01143             else if (!m_isOnScreen) {
01144                 // this might be a hot key
01145                 if (onHotKey(xevent->xkey, isRepeat)) {
01146                     return;
01147                 }
01148             }
01149 
01150             bool down             = (isRepeat || xevent->type == KeyPress);
01151             KeyModifierMask state =
01152                 m_keyState->mapModifiersFromX(xevent->xkey.state);
01153             m_keyState->onKey(xevent->xkey.keycode, down, state);
01154         }
01155     }
01156 
01157     // let input methods try to handle event first
01158     if (m_ic != NULL) {
01159         // XFilterEvent() may eat the event and generate a new KeyPress
01160         // event with a keycode of 0 because there isn't an actual key
01161         // associated with the keysym.  but the KeyRelease may pass
01162         // through XFilterEvent() and keep its keycode.  this means
01163         // there's a mismatch between KeyPress and KeyRelease keycodes.
01164         // since we use the keycode on the client to detect when a key
01165         // is released this won't do.  so we remember the keycode on
01166         // the most recent KeyPress (and clear it on a matching
01167         // KeyRelease) so we have a keycode for a synthesized KeyPress.
01168         if (xevent->type == KeyPress && xevent->xkey.keycode != 0) {
01169             m_lastKeycode = xevent->xkey.keycode;
01170         }
01171         else if (xevent->type == KeyRelease &&
01172             xevent->xkey.keycode == m_lastKeycode) {
01173             m_lastKeycode = 0;
01174         }
01175 
01176         // now filter the event
01177         if (XFilterEvent(xevent, None)) {
01178             if (xevent->type == KeyPress) {
01179                 // add filtered presses to the filtered list
01180                 m_filtered.insert(m_lastKeycode);
01181             }
01182             return;
01183         }
01184 
01185         // discard matching key releases for key presses that were
01186         // filtered and remove them from our filtered list.
01187         else if (xevent->type == KeyRelease &&
01188             m_filtered.count(xevent->xkey.keycode) > 0) {
01189             m_filtered.erase(xevent->xkey.keycode);
01190             return;
01191         }
01192     }
01193 
01194     // let screen saver have a go
01195     if (m_screensaver->handleXEvent(xevent)) {
01196         // screen saver handled it
01197         return;
01198     }
01199 
01200     // handle the event ourself
01201     switch (xevent->type) {
01202     case CreateNotify:
01203         if (m_isPrimary) {
01204             // select events on new window
01205             selectEvents(xevent->xcreatewindow.window);
01206         }
01207         break;
01208 
01209     case MappingNotify:
01210         refreshKeyboard(xevent);
01211         break;
01212 
01213     case LeaveNotify:
01214         if (!m_isPrimary) {
01215             // mouse moved out of hider window somehow.  hide the window.
01216             XUnmapWindow(m_display, m_window);
01217         }
01218         break;
01219 
01220     case SelectionClear:
01221         {
01222             // we just lost the selection.  that means someone else
01223             // grabbed the selection so this screen is now the
01224             // selection owner.  report that to the receiver.
01225             ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
01226             if (id != kClipboardEnd) {
01227                 LOG((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
01228                 m_clipboard[id]->lost(xevent->xselectionclear.time);
01229                 sendClipboardEvent(getClipboardGrabbedEvent(), id);
01230                 return;
01231             }
01232         }
01233         break;
01234 
01235     case SelectionNotify:
01236         // notification of selection transferred.  we shouldn't
01237         // get this here because we handle them in the selection
01238         // retrieval methods.  we'll just delete the property
01239         // with the data (satisfying the usual ICCCM protocol).
01240         if (xevent->xselection.property != None) {
01241             XDeleteProperty(m_display,
01242                                 xevent->xselection.requestor,
01243                                 xevent->xselection.property);
01244         }
01245         break;
01246 
01247     case SelectionRequest:
01248         {
01249             // somebody is asking for clipboard data
01250             ClipboardID id = getClipboardID(
01251                                 xevent->xselectionrequest.selection);
01252             if (id != kClipboardEnd) {
01253                 m_clipboard[id]->addRequest(
01254                                 xevent->xselectionrequest.owner,
01255                                 xevent->xselectionrequest.requestor,
01256                                 xevent->xselectionrequest.target,
01257                                 xevent->xselectionrequest.time,
01258                                 xevent->xselectionrequest.property);
01259                 return;
01260             }
01261         }
01262         break;
01263 
01264     case PropertyNotify:
01265         // property delete may be part of a selection conversion
01266         if (xevent->xproperty.state == PropertyDelete) {
01267             processClipboardRequest(xevent->xproperty.window,
01268                                 xevent->xproperty.time,
01269                                 xevent->xproperty.atom);
01270         }
01271         break;
01272 
01273     case DestroyNotify:
01274         // looks like one of the windows that requested a clipboard
01275         // transfer has gone bye-bye.
01276         destroyClipboardRequest(xevent->xdestroywindow.window);
01277         break;
01278 
01279     case KeyPress:
01280         if (m_isPrimary) {
01281             onKeyPress(xevent->xkey);
01282         }
01283         return;
01284 
01285     case KeyRelease:
01286         if (m_isPrimary) {
01287             onKeyRelease(xevent->xkey, isRepeat);
01288         }
01289         return;
01290 
01291     case ButtonPress:
01292         if (m_isPrimary) {
01293             onMousePress(xevent->xbutton);
01294         }
01295         return;
01296 
01297     case ButtonRelease:
01298         if (m_isPrimary) {
01299             onMouseRelease(xevent->xbutton);
01300         }
01301         return;
01302 
01303     case MotionNotify:
01304         if (m_isPrimary) {
01305             onMouseMove(xevent->xmotion);
01306         }
01307         return;
01308 
01309     default:
01310 #if HAVE_XKB_EXTENSION
01311         if (m_xkb && xevent->type == m_xkbEventBase) {
01312             XkbEvent* xkbEvent = reinterpret_cast<XkbEvent*>(xevent);
01313             switch (xkbEvent->any.xkb_type) {
01314             case XkbMapNotify:
01315                 refreshKeyboard(xevent);
01316                 return;
01317 
01318             case XkbStateNotify:
01319                 LOG((CLOG_INFO "group change: %d", xkbEvent->state.group));
01320                 m_keyState->setActiveGroup((SInt32)xkbEvent->state.group);
01321                 return;
01322             }
01323         }
01324 #endif
01325         break;
01326     }
01327 }
01328 
01329 void
01330 CXWindowsScreen::onKeyPress(XKeyEvent& xkey)
01331 {
01332     LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state));
01333     const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
01334     KeyID key                  = mapKeyFromX(&xkey);
01335     if (key != kKeyNone) {
01336         // check for ctrl+alt+del emulation
01337         if ((key == kKeyPause || key == kKeyBreak) &&
01338             (mask & (KeyModifierControl | KeyModifierAlt)) ==
01339                     (KeyModifierControl | KeyModifierAlt)) {
01340             // pretend it's ctrl+alt+del
01341             LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
01342             key = kKeyDelete;
01343         }
01344 
01345         // get which button.  see call to XFilterEvent() in onEvent()
01346         // for more info.
01347         bool isFake = false;
01348         KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
01349         if (keycode == 0) {
01350             isFake  = true;
01351             keycode = static_cast<KeyButton>(m_lastKeycode);
01352             if (keycode == 0) {
01353                 // no keycode
01354                 return;
01355             }
01356         }
01357 
01358         // handle key
01359         m_keyState->sendKeyEvent(getEventTarget(),
01360                             true, false, key, mask, 1, keycode);
01361 
01362         // do fake release if this is a fake press
01363         if (isFake) {
01364             m_keyState->sendKeyEvent(getEventTarget(),
01365                             false, false, key, mask, 1, keycode);
01366         }
01367     }
01368 }
01369 
01370 void
01371 CXWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
01372 {
01373     const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
01374     KeyID key                  = mapKeyFromX(&xkey);
01375     if (key != kKeyNone) {
01376         // check for ctrl+alt+del emulation
01377         if ((key == kKeyPause || key == kKeyBreak) &&
01378             (mask & (KeyModifierControl | KeyModifierAlt)) ==
01379                     (KeyModifierControl | KeyModifierAlt)) {
01380             // pretend it's ctrl+alt+del and ignore autorepeat
01381             LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
01382             key      = kKeyDelete;
01383             isRepeat = false;
01384         }
01385 
01386         KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
01387         if (!isRepeat) {
01388             // no press event follows so it's a plain release
01389             LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state));
01390             m_keyState->sendKeyEvent(getEventTarget(),
01391                             false, false, key, mask, 1, keycode);
01392         }
01393         else {
01394             // found a press event following so it's a repeat.
01395             // we could attempt to count the already queued
01396             // repeats but we'll just send a repeat of 1.
01397             // note that we discard the press event.
01398             LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
01399             m_keyState->sendKeyEvent(getEventTarget(),
01400                             false, true, key, mask, 1, keycode);
01401         }
01402     }
01403 }
01404 
01405 bool
01406 CXWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat)
01407 {
01408     // find the hot key id
01409     HotKeyToIDMap::const_iterator i =
01410         m_hotKeyToIDMap.find(CHotKeyItem(xkey.keycode, xkey.state));
01411     if (i == m_hotKeyToIDMap.end()) {
01412         return false;
01413     }
01414 
01415     // find what kind of event
01416     CEvent::Type type;
01417     if (xkey.type == KeyPress) {
01418         type = getHotKeyDownEvent();
01419     }
01420     else if (xkey.type == KeyRelease) {
01421         type = getHotKeyUpEvent();
01422     }
01423     else {
01424         return false;
01425     }
01426 
01427     // generate event (ignore key repeats)
01428     if (!isRepeat) {
01429         EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
01430                                 CHotKeyInfo::alloc(i->second)));
01431     }
01432     return true;
01433 }
01434 
01435 void
01436 CXWindowsScreen::onMousePress(const XButtonEvent& xbutton)
01437 {
01438     LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
01439     ButtonID button      = mapButtonFromX(&xbutton);
01440     KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
01441     if (button != kButtonNone) {
01442         sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask));
01443     }
01444 }
01445 
01446 void
01447 CXWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
01448 {
01449     LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
01450     ButtonID button      = mapButtonFromX(&xbutton);
01451     KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
01452     if (button != kButtonNone) {
01453         sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask));
01454     }
01455     else if (xbutton.button == 4) {
01456         // wheel forward (away from user)
01457         sendEvent(getWheelEvent(), CWheelInfo::alloc(0, 120));
01458     }
01459     else if (xbutton.button == 5) {
01460         // wheel backward (toward user)
01461         sendEvent(getWheelEvent(), CWheelInfo::alloc(0, -120));
01462     }
01463     // XXX -- support x-axis scrolling
01464 }
01465 
01466 void
01467 CXWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
01468 {
01469     LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xmotion.x_root, xmotion.y_root));
01470 
01471     // compute motion delta (relative to the last known
01472     // mouse position)
01473     SInt32 x = xmotion.x_root - m_xCursor;
01474     SInt32 y = xmotion.y_root - m_yCursor;
01475 
01476     // save position to compute delta of next motion
01477     m_xCursor = xmotion.x_root;
01478     m_yCursor = xmotion.y_root;
01479 
01480     if (xmotion.send_event) {
01481         // we warped the mouse.  discard events until we
01482         // find the matching sent event.  see
01483         // warpCursorNoFlush() for where the events are
01484         // sent.  we discard the matching sent event and
01485         // can be sure we've skipped the warp event.
01486         XEvent xevent;
01487         do {
01488             XMaskEvent(m_display, PointerMotionMask, &xevent);
01489         } while (!xevent.xany.send_event);
01490     }
01491     else if (m_isOnScreen) {
01492         // motion on primary screen
01493         sendEvent(getMotionOnPrimaryEvent(),
01494                             CMotionInfo::alloc(m_xCursor, m_yCursor));
01495     }
01496     else {
01497         // motion on secondary screen.  warp mouse back to
01498         // center.
01499         //
01500         // my lombard (powerbook g3) running linux and
01501         // using the adbmouse driver has two problems:
01502         // first, the driver only sends motions of +/-2
01503         // pixels and, second, it seems to discard some
01504         // physical input after a warp.  the former isn't a
01505         // big deal (we're just limited to every other
01506         // pixel) but the latter is a PITA.  to work around
01507         // it we only warp when the mouse has moved more
01508         // than s_size pixels from the center.
01509         static const SInt32 s_size = 32;
01510         if (xmotion.x_root - m_xCenter < -s_size ||
01511             xmotion.x_root - m_xCenter >  s_size ||
01512             xmotion.y_root - m_yCenter < -s_size ||
01513             xmotion.y_root - m_yCenter >  s_size) {
01514             warpCursorNoFlush(m_xCenter, m_yCenter);
01515         }
01516 
01517         // send event if mouse moved.  do this after warping
01518         // back to center in case the motion takes us onto
01519         // the primary screen.  if we sent the event first
01520         // in that case then the warp would happen after
01521         // warping to the primary screen's enter position,
01522         // effectively overriding it.
01523         if (x != 0 || y != 0) {
01524             sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
01525         }
01526     }
01527 }
01528 
01529 Cursor
01530 CXWindowsScreen::createBlankCursor() const
01531 {
01532     // this seems just a bit more complicated than really necessary
01533 
01534     // get the closet cursor size to 1x1
01535     unsigned int w, h;
01536     XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
01537 
01538     // make bitmap data for cursor of closet size.  since the cursor
01539     // is blank we can use the same bitmap for shape and mask:  all
01540     // zeros.
01541     const int size = ((w + 7) >> 3) * h;
01542     char* data = new char[size];
01543     memset(data, 0, size);
01544 
01545     // make bitmap
01546     Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
01547 
01548     // need an arbitrary color for the cursor
01549     XColor color;
01550     color.pixel = 0;
01551     color.red   = color.green = color.blue = 0;
01552     color.flags = DoRed | DoGreen | DoBlue;
01553 
01554     // make cursor from bitmap
01555     Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
01556                                 &color, &color, 0, 0);
01557 
01558     // don't need bitmap or the data anymore
01559     delete[] data;
01560     XFreePixmap(m_display, bitmap);
01561 
01562     return cursor;
01563 }
01564 
01565 ClipboardID
01566 CXWindowsScreen::getClipboardID(Atom selection) const
01567 {
01568     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
01569         if (m_clipboard[id] != NULL &&
01570             m_clipboard[id]->getSelection() == selection) {
01571             return id;
01572         }
01573     }
01574     return kClipboardEnd;
01575 }
01576 
01577 void
01578 CXWindowsScreen::processClipboardRequest(Window requestor,
01579                 Time time, Atom property)
01580 {
01581     // check every clipboard until one returns success
01582     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
01583         if (m_clipboard[id] != NULL &&
01584             m_clipboard[id]->processRequest(requestor, time, property)) {
01585             break;
01586         }
01587     }
01588 }
01589 
01590 void
01591 CXWindowsScreen::destroyClipboardRequest(Window requestor)
01592 {
01593     // check every clipboard until one returns success
01594     for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
01595         if (m_clipboard[id] != NULL &&
01596             m_clipboard[id]->destroyRequest(requestor)) {
01597             break;
01598         }
01599     }
01600 }
01601 
01602 void
01603 CXWindowsScreen::onError()
01604 {
01605     // prevent further access to the X display
01606     EVENTQUEUE->adoptBuffer(NULL);
01607     m_screensaver->destroy();
01608     m_screensaver = NULL;
01609     m_display     = NULL;
01610 
01611     // notify of failure
01612     sendEvent(getErrorEvent(), NULL);
01613 
01614     // FIXME -- should ensure that we ignore operations that involve
01615     // m_display from now on.  however, Xlib will simply exit the
01616     // application in response to the X I/O error so there's no
01617     // point in trying to really handle the error.  if we did want
01618     // to handle the error, it'd probably be easiest to delegate to
01619     // one of two objects.  one object would take the implementation
01620     // from this class.  the other object would be stub methods that
01621     // don't use X11.  on error, we'd switch to the latter.
01622 }
01623 
01624 int
01625 CXWindowsScreen::ioErrorHandler(Display*)
01626 {
01627     // the display has disconnected, probably because X is shutting
01628     // down.  X forces us to exit at this point which is annoying.
01629     // we'll pretend as if we won't exit so we try to make sure we
01630     // don't access the display anymore.
01631     LOG((CLOG_CRIT "X display has unexpectedly disconnected"));
01632     s_screen->onError();
01633     return 0;
01634 }
01635 
01636 void
01637 CXWindowsScreen::selectEvents(Window w) const
01638 {
01639     // ignore errors while we adjust event masks.  windows could be
01640     // destroyed at any time after the XQueryTree() in doSelectEvents()
01641     // so we must ignore BadWindow errors.
01642     CXWindowsUtil::CErrorLock lock(m_display);
01643 
01644     // adjust event masks
01645     doSelectEvents(w);
01646 }
01647 
01648 void
01649 CXWindowsScreen::doSelectEvents(Window w) const
01650 {
01651     // we want to track the mouse everywhere on the display.  to achieve
01652     // that we select PointerMotionMask on every window.  we also select
01653     // SubstructureNotifyMask in order to get CreateNotify events so we
01654     // select events on new windows too.
01655     //
01656     // note that this can break certain clients due a design flaw of X.
01657     // X will deliver a PointerMotion event to the deepest window in the
01658     // hierarchy that contains the pointer and has PointerMotionMask
01659     // selected by *any* client.  if another client doesn't select
01660     // motion events in a subwindow so the parent window will get them
01661     // then by selecting for motion events on the subwindow we break
01662     // that client because the parent will no longer get the events.
01663 
01664     // FIXME -- should provide some workaround for event selection
01665     // design flaw.  perhaps only select for motion events on windows
01666     // that already do or are top-level windows or don't propagate
01667     // pointer events.  or maybe an option to simply poll the mouse.
01668 
01669     // we don't want to adjust our grab window
01670     if (w == m_window) {
01671         return;
01672     }
01673 
01674     // select events of interest.  do this before querying the tree so
01675     // we'll get notifications of children created after the XQueryTree()
01676     // so we won't miss them.
01677     XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask);
01678 
01679     // recurse on child windows
01680     Window rw, pw, *cw;
01681     unsigned int nc;
01682     if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
01683         for (unsigned int i = 0; i < nc; ++i) {
01684             doSelectEvents(cw[i]);
01685         }
01686         XFree(cw);
01687     }
01688 }
01689 
01690 KeyID
01691 CXWindowsScreen::mapKeyFromX(XKeyEvent* event) const
01692 {
01693     // convert to a keysym
01694     KeySym keysym;
01695     if (event->type == KeyPress && m_ic != NULL) {
01696         // do multibyte lookup.  can only call XmbLookupString with a
01697         // key press event and a valid XIC so we checked those above.
01698         char scratch[32];
01699         int n        = sizeof(scratch) / sizeof(scratch[0]);
01700         char* buffer = scratch;
01701         int status;
01702         n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
01703         if (status == XBufferOverflow) {
01704             // not enough space.  grow buffer and try again.
01705             buffer = new char[n];
01706             n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
01707             delete[] buffer;
01708         }
01709 
01710         // see what we got.  since we don't care about the string
01711         // we'll just look for a keysym.
01712         switch (status) {
01713         default:
01714         case XLookupNone:
01715         case XLookupChars:
01716             keysym = 0;
01717             break;
01718 
01719         case XLookupKeySym:
01720         case XLookupBoth:
01721             break;
01722         }
01723     }
01724     else {
01725         // plain old lookup
01726         char dummy[1];
01727         XLookupString(event, dummy, 0, &keysym, NULL);
01728     }
01729 
01730     // convert key
01731     return CXWindowsUtil::mapKeySymToKeyID(keysym);
01732 }
01733 
01734 ButtonID
01735 CXWindowsScreen::mapButtonFromX(const XButtonEvent* event) const
01736 {
01737     unsigned int button = event->button;
01738 
01739     // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right)
01740     if (button >= 1 && button <= 3) {
01741         return static_cast<ButtonID>(button);
01742     }
01743 
01744     // buttons 4 and 5 are ignored here.  they're used for the wheel.
01745     // buttons 6, 7, etc and up map to 4, 5, etc.
01746     else if (button >= 6) {
01747         return static_cast<ButtonID>(button - 2);
01748     }
01749 
01750     // unknown button
01751     else {
01752         return kButtonNone;
01753     }
01754 }
01755 
01756 unsigned int
01757 CXWindowsScreen::mapButtonToX(ButtonID id) const
01758 {
01759     // map button -1 to button 4 (+wheel)
01760     if (id == static_cast<ButtonID>(-1)) {
01761         id = 4;
01762     }
01763 
01764     // map button -2 to button 5 (-wheel)
01765     else if (id == static_cast<ButtonID>(-2)) {
01766         id = 5;
01767     }
01768 
01769     // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons
01770     // 4 and 5 used to simulate the mouse wheel.
01771     else if (id >= 4) {
01772         id += 2;
01773     }
01774 
01775     // check button is in legal range
01776     if (id < 1 || id > m_buttons.size()) {
01777         // out of range
01778         return 0;
01779     }
01780 
01781     // map button
01782     return static_cast<unsigned int>(id);
01783 }
01784 
01785 void
01786 CXWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
01787 {
01788     assert(m_window != None);
01789 
01790     // send an event that we can recognize before the mouse warp
01791     XEvent eventBefore;
01792     eventBefore.type                = MotionNotify;
01793     eventBefore.xmotion.display     = m_display;
01794     eventBefore.xmotion.window      = m_window;
01795     eventBefore.xmotion.root        = m_root;
01796     eventBefore.xmotion.subwindow   = m_window;
01797     eventBefore.xmotion.time        = CurrentTime;
01798     eventBefore.xmotion.x           = x;
01799     eventBefore.xmotion.y           = y;
01800     eventBefore.xmotion.x_root      = x;
01801     eventBefore.xmotion.y_root      = y;
01802     eventBefore.xmotion.state       = 0;
01803     eventBefore.xmotion.is_hint     = NotifyNormal;
01804     eventBefore.xmotion.same_screen = True;
01805     XEvent eventAfter               = eventBefore;
01806     XSendEvent(m_display, m_window, False, 0, &eventBefore);
01807 
01808     // warp mouse
01809     XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
01810 
01811     // send an event that we can recognize after the mouse warp
01812     XSendEvent(m_display, m_window, False, 0, &eventAfter);
01813     XSync(m_display, False);
01814 
01815     LOG((CLOG_DEBUG2 "warped to %d,%d", x, y));
01816 }
01817 
01818 void
01819 CXWindowsScreen::updateButtons()
01820 {
01821     // query the button mapping
01822     UInt32 numButtons = XGetPointerMapping(m_display, NULL, 0);
01823     unsigned char* tmpButtons = new unsigned char[numButtons];
01824     XGetPointerMapping(m_display, tmpButtons, numButtons);
01825 
01826     // find the largest logical button id
01827     unsigned char maxButton = 0;
01828     for (UInt32 i = 0; i < numButtons; ++i) {
01829         if (tmpButtons[i] > maxButton) {
01830             maxButton = tmpButtons[i];
01831         }
01832     }
01833 
01834     // allocate button array
01835     m_buttons.resize(maxButton);
01836 
01837     // fill in button array values.  m_buttons[i] is the physical
01838     // button number for logical button i+1.
01839     for (UInt32 i = 0; i < numButtons; ++i) {
01840         m_buttons[i] = 0;
01841     }
01842     for (UInt32 i = 0; i < numButtons; ++i) {
01843         m_buttons[tmpButtons[i] - 1] = i + 1;
01844     }
01845 
01846     // clean up
01847     delete[] tmpButtons;
01848 }
01849 
01850 bool
01851 CXWindowsScreen::grabMouseAndKeyboard()
01852 {
01853     // grab the mouse and keyboard.  keep trying until we get them.
01854     // if we can't grab one after grabbing the other then ungrab
01855     // and wait before retrying.  give up after s_timeout seconds.
01856     static const double s_timeout = 1.0;
01857     int result;
01858     CStopwatch timer;
01859     do {
01860         // keyboard first
01861         do {
01862             result = XGrabKeyboard(m_display, m_window, True,
01863                                 GrabModeAsync, GrabModeAsync, CurrentTime);
01864             assert(result != GrabNotViewable);
01865             if (result != GrabSuccess) {
01866                 LOG((CLOG_DEBUG2 "waiting to grab keyboard"));
01867                 ARCH->sleep(0.05);
01868                 if (timer.getTime() >= s_timeout) {
01869                     LOG((CLOG_DEBUG2 "grab keyboard timed out"));
01870                     return false;
01871                 }
01872             }
01873         } while (result != GrabSuccess);
01874         LOG((CLOG_DEBUG2 "grabbed keyboard"));
01875 
01876         // now the mouse
01877         result = XGrabPointer(m_display, m_window, True, 0,
01878                                 GrabModeAsync, GrabModeAsync,
01879                                 m_window, None, CurrentTime);
01880         assert(result != GrabNotViewable);
01881         if (result != GrabSuccess) {
01882             // back off to avoid grab deadlock
01883             XUngrabKeyboard(m_display, CurrentTime);
01884             LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer"));
01885             ARCH->sleep(0.05);
01886             if (timer.getTime() >= s_timeout) {
01887                 LOG((CLOG_DEBUG2 "grab pointer timed out"));
01888                 return false;
01889             }
01890         }
01891     } while (result != GrabSuccess);
01892 
01893     LOG((CLOG_DEBUG1 "grabbed pointer and keyboard"));
01894     return true;
01895 }
01896 
01897 void
01898 CXWindowsScreen::refreshKeyboard(XEvent* event)
01899 {
01900     if (XPending(m_display) > 0) {
01901         XEvent tmpEvent;
01902         XPeekEvent(m_display, &tmpEvent);
01903         if (tmpEvent.type == MappingNotify) {
01904             // discard this event since another follows.
01905             // we tend to get a bunch of these in a row.
01906             return;
01907         }
01908     }
01909 
01910     // keyboard mapping changed
01911 #if HAVE_XKB_EXTENSION
01912     if (m_xkb && event->type == m_xkbEventBase) {
01913         XkbRefreshKeyboardMapping((XkbMapNotifyEvent*)event);
01914     }
01915     else
01916 #else
01917     {
01918         XRefreshKeyboardMapping(&event->xmapping);
01919     }
01920 #endif
01921     m_keyState->updateKeyMap();
01922     m_keyState->updateKeyState();
01923 }
01924 
01925 
01926 //
01927 // CXWindowsScreen::CHotKeyItem
01928 //
01929 
01930 CXWindowsScreen::CHotKeyItem::CHotKeyItem(int keycode, unsigned int mask) :
01931     m_keycode(keycode),
01932     m_mask(mask)
01933 {
01934     // do nothing
01935 }
01936 
01937 bool
01938 CXWindowsScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const
01939 {
01940     return (m_keycode < x.m_keycode ||
01941             (m_keycode == x.m_keycode && m_mask < x.m_mask));
01942 }

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