00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
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
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
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
00108 if (XInitThreads() == 0)
00109 {
00110 throw XArch("XInitThreads() returned zero");
00111 }
00112
00113
00114
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
00136 if (m_isPrimary) {
00137
00138 selectEvents(m_root);
00139
00140
00141 openIM();
00142 }
00143 else {
00144
00145 XTestGrabControl(m_display, True);
00146 }
00147
00148
00149 for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
00150 m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id);
00151 }
00152
00153
00154 EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(),
00155 new TMethodEventJob<CXWindowsScreen>(this,
00156 &CXWindowsScreen::handleSystemEvent));
00157
00158
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
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
00197 XKeyboardState keyControl;
00198 XGetKeyboardControl(m_display, &keyControl);
00199 m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
00200 m_keyState->setAutoRepeat(keyControl);
00201
00202
00203 XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
00204
00205
00206
00207 XMapRaised(m_display, m_window);
00208
00209
00210 fakeMouseMove(m_xCenter, m_yCenter);
00211 }
00212 }
00213
00214 void
00215 CXWindowsScreen::disable()
00216 {
00217
00218 if (m_ic != NULL) {
00219 XUnsetICFocus(m_ic);
00220 }
00221
00222
00223
00224 XUnmapWindow(m_display, m_window);
00225
00226
00227 if (!m_isPrimary && m_autoRepeat) {
00228
00229 }
00230 }
00231
00232 void
00233 CXWindowsScreen::enter()
00234 {
00235 screensaver(false);
00236
00237
00238 if (m_ic != NULL) {
00239 XUnsetICFocus(m_ic);
00240 }
00241
00242
00243 if (m_lastFocus != None) {
00244
00245 CXWindowsUtil::CErrorLock lock(m_display);
00246 XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime);
00247 }
00248
00249
00250
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
00263
00264 XUnmapWindow(m_display, m_window);
00265
00266
00267
00268
00269
00270
00271
00272 if (!m_isPrimary) {
00273
00274 XKeyboardState keyControl;
00275 XGetKeyboardControl(m_display, &keyControl);
00276 m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
00277 m_keyState->setAutoRepeat(keyControl);
00278
00279
00280
00281
00282
00283 }
00284
00285
00286 m_isOnScreen = true;
00287 }
00288
00289 bool
00290 CXWindowsScreen::leave()
00291 {
00292 if (!m_isPrimary) {
00293
00294
00295
00296
00297
00298 if (m_autoRepeat) {
00299
00300 }
00301
00302
00303 XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
00304 }
00305
00306
00307 XMapRaised(m_display, m_window);
00308
00309
00310 if (m_isPrimary && !grabMouseAndKeyboard()) {
00311 XUnmapWindow(m_display, m_window);
00312 return false;
00313 }
00314
00315
00316 XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert);
00317
00318
00319 if (m_isPrimary || !m_preserveFocus) {
00320 XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
00321 }
00322
00323
00324
00325
00326 if (m_isPrimary) {
00327 warpCursor(m_xCenter, m_yCenter);
00328 }
00329 else {
00330 fakeMouseMove(m_xCenter, m_yCenter);
00331 }
00332
00333
00334 if (m_ic != NULL) {
00335 XmbResetIC(m_ic);
00336 XSetICFocus(m_ic);
00337 m_filtered.clear();
00338 }
00339
00340
00341 m_isOnScreen = false;
00342
00343 return true;
00344 }
00345
00346 bool
00347 CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard)
00348 {
00349
00350 if (m_clipboard[id] == NULL) {
00351 return false;
00352 }
00353
00354
00355 Time timestamp = CXWindowsUtil::getCurrentTime(
00356 m_display, m_clipboard[id]->getWindow());
00357
00358 if (clipboard != NULL) {
00359
00360 return CClipboard::copy(m_clipboard[id], clipboard, timestamp);
00361 }
00362 else {
00363
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
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
00453 if (m_clipboard[id] == NULL) {
00454 return false;
00455 }
00456
00457
00458 Time timestamp = CXWindowsUtil::getCurrentTime(
00459 m_display, m_clipboard[id]->getWindow());
00460
00461
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
00495 }
00496
00497 void
00498 CXWindowsScreen::warpCursor(SInt32 x, SInt32 y)
00499 {
00500
00501 warpCursorNoFlush(x, y);
00502
00503
00504 XEvent event;
00505 while (XCheckMaskEvent(m_display, PointerMotionMask |
00506 ButtonPressMask | ButtonReleaseMask |
00507 KeyPressMask | KeyReleaseMask |
00508 KeymapStateMask,
00509 &event)) {
00510
00511 }
00512
00513
00514 m_xCursor = x;
00515 m_yCursor = y;
00516 }
00517
00518 UInt32
00519 CXWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
00520 {
00521
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
00529 if (key == kKeyNone && mask == 0) {
00530 return 0;
00531 }
00532
00533
00534 unsigned int modifiers;
00535 if (!m_keyState->mapModifiersToX(mask, modifiers)) {
00536
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
00544 LOG((CLOG_WARN "could not map hotkey id=%04x mask=%04x", key, mask));
00545 return 0;
00546 }
00547
00548
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
00560
00561
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
00578 if ((mask & s_hotKeyModifiers[j]) == 0) {
00579 continue;
00580 }
00581
00582
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
00591
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
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
00650
00651
00652 else {
00653
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
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
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
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
00717 HotKeyMap::iterator i = m_hotKeys.find(id);
00718 if (i == m_hotKeys.end()) {
00719 return;
00720 }
00721
00722
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
00741 m_hotKeys.erase(i);
00742 m_oldHotKeyIDs.push_back(id);
00743 }
00744
00745 void
00746 CXWindowsScreen::fakeInputBegin()
00747 {
00748
00749 }
00750
00751 void
00752 CXWindowsScreen::fakeInputEnd()
00753 {
00754
00755 }
00756
00757 SInt32
00758 CXWindowsScreen::getJumpZoneSize() const
00759 {
00760 return 1;
00761 }
00762
00763 bool
00764 CXWindowsScreen::isAnyMouseButtonDown() const
00765 {
00766
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
00814 if (false && m_xinerama && m_xtestIsXineramaUnaware) {
00815
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
00827 if (yDelta == 0) {
00828 return;
00829 }
00830
00831
00832 const unsigned int xButton = mapButtonToX(static_cast<ButtonID>(
00833 (yDelta >= 0) ? -1 : -2));
00834 if (xButton == 0) {
00835
00836
00837
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
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
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
00873 if (displayName == NULL) {
00874 displayName = getenv("DISPLAY");
00875 if (displayName == NULL) {
00876 displayName = ":0.0";
00877 }
00878 }
00879
00880
00881 LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName));
00882 Display* display = XOpenDisplay(displayName);
00883 if (display == NULL) {
00884 throw XScreenUnavailable(60.0);
00885 }
00886
00887
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
00924 m_x = 0;
00925 m_y = 0;
00926 m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display));
00927 m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display));
00928
00929
00930 m_xCenter = m_x + (m_w >> 1);
00931 m_yCenter = m_y + (m_h >> 1);
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
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
00970
00971
00972 XSetWindowAttributes attr;
00973 attr.do_not_propagate_mask = 0;
00974 attr.override_redirect = True;
00975 attr.cursor = createBlankCursor();
00976
00977
00978 SInt32 x, y, w, h;
00979 if (m_isPrimary) {
00980
00981
00982
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
00994
00995
00996
00997
00998
00999 attr.event_mask = LeaveWindowMask;
01000 x = 0;
01001 y = 0;
01002 w = 1;
01003 h = 1;
01004 }
01005
01006
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
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
01029
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
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
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
01071 m_im = im;
01072 m_ic = ic;
01073 m_lastKeycode = 0;
01074
01075
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
01119 bool isRepeat = false;
01120 if (m_isPrimary) {
01121 if (xevent->type == KeyRelease) {
01122
01123
01124
01125
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
01140 onHotKey(xevent->xkey, isRepeat);
01141 return;
01142 }
01143 else if (!m_isOnScreen) {
01144
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
01158 if (m_ic != NULL) {
01159
01160
01161
01162
01163
01164
01165
01166
01167
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
01177 if (XFilterEvent(xevent, None)) {
01178 if (xevent->type == KeyPress) {
01179
01180 m_filtered.insert(m_lastKeycode);
01181 }
01182 return;
01183 }
01184
01185
01186
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
01195 if (m_screensaver->handleXEvent(xevent)) {
01196
01197 return;
01198 }
01199
01200
01201 switch (xevent->type) {
01202 case CreateNotify:
01203 if (m_isPrimary) {
01204
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
01216 XUnmapWindow(m_display, m_window);
01217 }
01218 break;
01219
01220 case SelectionClear:
01221 {
01222
01223
01224
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
01237
01238
01239
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
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
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
01275
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
01337 if ((key == kKeyPause || key == kKeyBreak) &&
01338 (mask & (KeyModifierControl | KeyModifierAlt)) ==
01339 (KeyModifierControl | KeyModifierAlt)) {
01340
01341 LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
01342 key = kKeyDelete;
01343 }
01344
01345
01346
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
01354 return;
01355 }
01356 }
01357
01358
01359 m_keyState->sendKeyEvent(getEventTarget(),
01360 true, false, key, mask, 1, keycode);
01361
01362
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
01377 if ((key == kKeyPause || key == kKeyBreak) &&
01378 (mask & (KeyModifierControl | KeyModifierAlt)) ==
01379 (KeyModifierControl | KeyModifierAlt)) {
01380
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
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
01395
01396
01397
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
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
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
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
01457 sendEvent(getWheelEvent(), CWheelInfo::alloc(0, 120));
01458 }
01459 else if (xbutton.button == 5) {
01460
01461 sendEvent(getWheelEvent(), CWheelInfo::alloc(0, -120));
01462 }
01463
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
01472
01473 SInt32 x = xmotion.x_root - m_xCursor;
01474 SInt32 y = xmotion.y_root - m_yCursor;
01475
01476
01477 m_xCursor = xmotion.x_root;
01478 m_yCursor = xmotion.y_root;
01479
01480 if (xmotion.send_event) {
01481
01482
01483
01484
01485
01486 XEvent xevent;
01487 do {
01488 XMaskEvent(m_display, PointerMotionMask, &xevent);
01489 } while (!xevent.xany.send_event);
01490 }
01491 else if (m_isOnScreen) {
01492
01493 sendEvent(getMotionOnPrimaryEvent(),
01494 CMotionInfo::alloc(m_xCursor, m_yCursor));
01495 }
01496 else {
01497
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508
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
01518
01519
01520
01521
01522
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
01533
01534
01535 unsigned int w, h;
01536 XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
01537
01538
01539
01540
01541 const int size = ((w + 7) >> 3) * h;
01542 char* data = new char[size];
01543 memset(data, 0, size);
01544
01545
01546 Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
01547
01548
01549 XColor color;
01550 color.pixel = 0;
01551 color.red = color.green = color.blue = 0;
01552 color.flags = DoRed | DoGreen | DoBlue;
01553
01554
01555 Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
01556 &color, &color, 0, 0);
01557
01558
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
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
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
01606 EVENTQUEUE->adoptBuffer(NULL);
01607 m_screensaver->destroy();
01608 m_screensaver = NULL;
01609 m_display = NULL;
01610
01611
01612 sendEvent(getErrorEvent(), NULL);
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622 }
01623
01624 int
01625 CXWindowsScreen::ioErrorHandler(Display*)
01626 {
01627
01628
01629
01630
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
01640
01641
01642 CXWindowsUtil::CErrorLock lock(m_display);
01643
01644
01645 doSelectEvents(w);
01646 }
01647
01648 void
01649 CXWindowsScreen::doSelectEvents(Window w) const
01650 {
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670 if (w == m_window) {
01671 return;
01672 }
01673
01674
01675
01676
01677 XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask);
01678
01679
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
01694 KeySym keysym;
01695 if (event->type == KeyPress && m_ic != NULL) {
01696
01697
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
01705 buffer = new char[n];
01706 n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
01707 delete[] buffer;
01708 }
01709
01710
01711
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
01726 char dummy[1];
01727 XLookupString(event, dummy, 0, &keysym, NULL);
01728 }
01729
01730
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
01740 if (button >= 1 && button <= 3) {
01741 return static_cast<ButtonID>(button);
01742 }
01743
01744
01745
01746 else if (button >= 6) {
01747 return static_cast<ButtonID>(button - 2);
01748 }
01749
01750
01751 else {
01752 return kButtonNone;
01753 }
01754 }
01755
01756 unsigned int
01757 CXWindowsScreen::mapButtonToX(ButtonID id) const
01758 {
01759
01760 if (id == static_cast<ButtonID>(-1)) {
01761 id = 4;
01762 }
01763
01764
01765 else if (id == static_cast<ButtonID>(-2)) {
01766 id = 5;
01767 }
01768
01769
01770
01771 else if (id >= 4) {
01772 id += 2;
01773 }
01774
01775
01776 if (id < 1 || id > m_buttons.size()) {
01777
01778 return 0;
01779 }
01780
01781
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
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
01809 XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
01810
01811
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
01822 UInt32 numButtons = XGetPointerMapping(m_display, NULL, 0);
01823 unsigned char* tmpButtons = new unsigned char[numButtons];
01824 XGetPointerMapping(m_display, tmpButtons, numButtons);
01825
01826
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
01835 m_buttons.resize(maxButton);
01836
01837
01838
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
01847 delete[] tmpButtons;
01848 }
01849
01850 bool
01851 CXWindowsScreen::grabMouseAndKeyboard()
01852 {
01853
01854
01855
01856 static const double s_timeout = 1.0;
01857 int result;
01858 CStopwatch timer;
01859 do {
01860
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
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
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
01905
01906 return;
01907 }
01908 }
01909
01910
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
01928
01929
01930 CXWindowsScreen::CHotKeyItem::CHotKeyItem(int keycode, unsigned int mask) :
01931 m_keycode(keycode),
01932 m_mask(mask)
01933 {
01934
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 }