• Main Page
  • Classes
  • Files
  • File List

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

Generated on Sat May 25 2013 00:00:04 for Synergy by  doxygen 1.7.1