PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/platform/XWindowsScreen.cpp

https://gitlab.com/thomasphillips3/synergy
C++ | 1849 lines | 1325 code | 203 blank | 321 comment | 329 complexity | e4b96c5c7c52985f805ed52ae56e79ba MD5 | raw file
  1. /*
  2. * synergy -- mouse and keyboard sharing utility
  3. * Copyright (C) 2012 Synergy Si Ltd.
  4. * Copyright (C) 2002 Chris Schoeneman
  5. *
  6. * This package is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * found in the file LICENSE that should have accompanied this file.
  9. *
  10. * This package is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "platform/XWindowsScreen.h"
  19. #include "platform/XWindowsClipboard.h"
  20. #include "platform/XWindowsEventQueueBuffer.h"
  21. #include "platform/XWindowsKeyState.h"
  22. #include "platform/XWindowsScreenSaver.h"
  23. #include "platform/XWindowsUtil.h"
  24. #include "synergy/Clipboard.h"
  25. #include "synergy/KeyMap.h"
  26. #include "synergy/XScreen.h"
  27. #include "arch/XArch.h"
  28. #include "arch/Arch.h"
  29. #include "base/Log.h"
  30. #include "base/Stopwatch.h"
  31. #include "base/String.h"
  32. #include "base/IEventQueue.h"
  33. #include "base/TMethodEventJob.h"
  34. #include <cstring>
  35. #include <cstdlib>
  36. #if X_DISPLAY_MISSING
  37. # error X11 is required to build synergy
  38. #else
  39. # include <X11/X.h>
  40. # include <X11/Xutil.h>
  41. # define XK_MISCELLANY
  42. # define XK_XKB_KEYS
  43. # include <X11/keysymdef.h>
  44. # if HAVE_X11_EXTENSIONS_DPMS_H
  45. extern "C" {
  46. # include <X11/extensions/dpms.h>
  47. }
  48. # endif
  49. # if HAVE_X11_EXTENSIONS_XTEST_H
  50. # include <X11/extensions/XTest.h>
  51. # else
  52. # error The XTest extension is required to build synergy
  53. # endif
  54. # if HAVE_X11_EXTENSIONS_XINERAMA_H
  55. // Xinerama.h may lack extern "C" for inclusion by C++
  56. extern "C" {
  57. # include <X11/extensions/Xinerama.h>
  58. }
  59. # endif
  60. # if HAVE_X11_EXTENSIONS_XRANDR_H
  61. # include <X11/extensions/Xrandr.h>
  62. # endif
  63. # if HAVE_XKB_EXTENSION
  64. # include <X11/XKBlib.h>
  65. # endif
  66. # ifdef HAVE_XI2
  67. # include <X11/extensions/XInput2.h>
  68. # endif
  69. #endif
  70. static int xi_opcode;
  71. //
  72. // XWindowsScreen
  73. //
  74. // NOTE -- the X display is shared among several objects but is owned
  75. // by the XWindowsScreen. Xlib is not reentrant so we must ensure
  76. // that no two objects can simultaneously call Xlib with the display.
  77. // this is easy since we only make X11 calls from the main thread.
  78. // we must also ensure that these objects do not use the display in
  79. // their destructors or, if they do, we can tell them not to. This
  80. // is to handle unexpected disconnection of the X display, when any
  81. // call on the display is invalid. In that situation we discard the
  82. // display and the X11 event queue buffer, ignore any calls that try
  83. // to use the display, and wait to be destroyed.
  84. XWindowsScreen* XWindowsScreen::s_screen = NULL;
  85. XWindowsScreen::XWindowsScreen(
  86. const char* displayName,
  87. bool isPrimary,
  88. bool disableXInitThreads,
  89. int mouseScrollDelta,
  90. IEventQueue* events) :
  91. m_isPrimary(isPrimary),
  92. m_mouseScrollDelta(mouseScrollDelta),
  93. m_display(NULL),
  94. m_root(None),
  95. m_window(None),
  96. m_isOnScreen(m_isPrimary),
  97. m_x(0), m_y(0),
  98. m_w(0), m_h(0),
  99. m_xCenter(0), m_yCenter(0),
  100. m_xCursor(0), m_yCursor(0),
  101. m_keyState(NULL),
  102. m_lastFocus(None),
  103. m_lastFocusRevert(RevertToNone),
  104. m_im(NULL),
  105. m_ic(NULL),
  106. m_lastKeycode(0),
  107. m_sequenceNumber(0),
  108. m_screensaver(NULL),
  109. m_screensaverNotify(false),
  110. m_xtestIsXineramaUnaware(true),
  111. m_preserveFocus(false),
  112. m_xkb(false),
  113. m_xi2detected(false),
  114. m_xrandr(false),
  115. m_events(events),
  116. PlatformScreen(events)
  117. {
  118. assert(s_screen == NULL);
  119. if (mouseScrollDelta==0) m_mouseScrollDelta=120;
  120. s_screen = this;
  121. if (!disableXInitThreads) {
  122. // initializes Xlib support for concurrent threads.
  123. if (XInitThreads() == 0)
  124. throw XArch("XInitThreads() returned zero");
  125. } else {
  126. LOG((CLOG_DEBUG "skipping XInitThreads()"));
  127. }
  128. // set the X I/O error handler so we catch the display disconnecting
  129. XSetIOErrorHandler(&XWindowsScreen::ioErrorHandler);
  130. try {
  131. m_display = openDisplay(displayName);
  132. m_root = DefaultRootWindow(m_display);
  133. saveShape();
  134. m_window = openWindow();
  135. m_screensaver = new XWindowsScreenSaver(m_display,
  136. m_window, getEventTarget(), events);
  137. m_keyState = new XWindowsKeyState(m_display, m_xkb, events, m_keyMap);
  138. LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : ""));
  139. LOG((CLOG_DEBUG "window is 0x%08x", m_window));
  140. }
  141. catch (...) {
  142. if (m_display != NULL) {
  143. XCloseDisplay(m_display);
  144. }
  145. throw;
  146. }
  147. // primary/secondary screen only initialization
  148. if (m_isPrimary) {
  149. // start watching for events on other windows
  150. selectEvents(m_root);
  151. m_xi2detected = detectXI2();
  152. if (m_xi2detected) {
  153. #ifdef HAVE_XI2
  154. selectXIRawMotion();
  155. #endif
  156. } else
  157. {
  158. // start watching for events on other windows
  159. selectEvents(m_root);
  160. }
  161. // prepare to use input methods
  162. openIM();
  163. }
  164. else {
  165. // become impervious to server grabs
  166. XTestGrabControl(m_display, True);
  167. }
  168. // initialize the clipboards
  169. for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
  170. m_clipboard[id] = new XWindowsClipboard(m_display, m_window, id);
  171. }
  172. // install event handlers
  173. m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
  174. new TMethodEventJob<XWindowsScreen>(this,
  175. &XWindowsScreen::handleSystemEvent));
  176. // install the platform event queue
  177. m_events->adoptBuffer(new XWindowsEventQueueBuffer(
  178. m_display, m_window, m_events));
  179. }
  180. XWindowsScreen::~XWindowsScreen()
  181. {
  182. assert(s_screen != NULL);
  183. assert(m_display != NULL);
  184. m_events->adoptBuffer(NULL);
  185. m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
  186. for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
  187. delete m_clipboard[id];
  188. }
  189. delete m_keyState;
  190. delete m_screensaver;
  191. m_keyState = NULL;
  192. m_screensaver = NULL;
  193. if (m_display != NULL) {
  194. // FIXME -- is it safe to clean up the IC and IM without a display?
  195. if (m_ic != NULL) {
  196. XDestroyIC(m_ic);
  197. }
  198. if (m_im != NULL) {
  199. XCloseIM(m_im);
  200. }
  201. XDestroyWindow(m_display, m_window);
  202. XCloseDisplay(m_display);
  203. }
  204. XSetIOErrorHandler(NULL);
  205. s_screen = NULL;
  206. }
  207. void
  208. XWindowsScreen::enable()
  209. {
  210. if (!m_isPrimary) {
  211. // get the keyboard control state
  212. XKeyboardState keyControl;
  213. XGetKeyboardControl(m_display, &keyControl);
  214. m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
  215. m_keyState->setAutoRepeat(keyControl);
  216. // move hider window under the cursor center
  217. XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
  218. // raise and show the window
  219. // FIXME -- take focus?
  220. XMapRaised(m_display, m_window);
  221. // warp the mouse to the cursor center
  222. fakeMouseMove(m_xCenter, m_yCenter);
  223. }
  224. }
  225. void
  226. XWindowsScreen::disable()
  227. {
  228. // release input context focus
  229. if (m_ic != NULL) {
  230. XUnsetICFocus(m_ic);
  231. }
  232. // unmap the hider/grab window. this also ungrabs the mouse and
  233. // keyboard if they're grabbed.
  234. XUnmapWindow(m_display, m_window);
  235. // restore auto-repeat state
  236. if (!m_isPrimary && m_autoRepeat) {
  237. //XAutoRepeatOn(m_display);
  238. }
  239. }
  240. void
  241. XWindowsScreen::enter()
  242. {
  243. screensaver(false);
  244. // release input context focus
  245. if (m_ic != NULL) {
  246. XUnsetICFocus(m_ic);
  247. }
  248. // set the input focus to what it had been when we took it
  249. if (m_lastFocus != None) {
  250. // the window may not exist anymore so ignore errors
  251. XWindowsUtil::ErrorLock lock(m_display);
  252. XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime);
  253. }
  254. #if HAVE_X11_EXTENSIONS_DPMS_H
  255. // Force the DPMS to turn screen back on since we don't
  256. // actually cause physical hardware input to trigger it
  257. int dummy;
  258. CARD16 powerlevel;
  259. BOOL enabled;
  260. if (DPMSQueryExtension(m_display, &dummy, &dummy) &&
  261. DPMSCapable(m_display) &&
  262. DPMSInfo(m_display, &powerlevel, &enabled))
  263. {
  264. if (enabled && powerlevel != DPMSModeOn)
  265. DPMSForceLevel(m_display, DPMSModeOn);
  266. }
  267. #endif
  268. // unmap the hider/grab window. this also ungrabs the mouse and
  269. // keyboard if they're grabbed.
  270. XUnmapWindow(m_display, m_window);
  271. /* maybe call this if entering for the screensaver
  272. // set keyboard focus to root window. the screensaver should then
  273. // pick up key events for when the user enters a password to unlock.
  274. XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime);
  275. */
  276. if (!m_isPrimary) {
  277. // get the keyboard control state
  278. XKeyboardState keyControl;
  279. XGetKeyboardControl(m_display, &keyControl);
  280. m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
  281. m_keyState->setAutoRepeat(keyControl);
  282. // turn off auto-repeat. we do this so fake key press events don't
  283. // cause the local server to generate their own auto-repeats of
  284. // those keys.
  285. //XAutoRepeatOff(m_display);
  286. }
  287. // now on screen
  288. m_isOnScreen = true;
  289. }
  290. bool
  291. XWindowsScreen::leave()
  292. {
  293. if (!m_isPrimary) {
  294. // restore the previous keyboard auto-repeat state. if the user
  295. // changed the auto-repeat configuration while on the client then
  296. // that state is lost. that's because we can't get notified by
  297. // the X server when the auto-repeat configuration is changed so
  298. // we can't track the desired configuration.
  299. if (m_autoRepeat) {
  300. //XAutoRepeatOn(m_display);
  301. }
  302. // move hider window under the cursor center
  303. XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
  304. }
  305. // raise and show the window
  306. XMapRaised(m_display, m_window);
  307. // grab the mouse and keyboard, if primary and possible
  308. if (m_isPrimary && !grabMouseAndKeyboard()) {
  309. XUnmapWindow(m_display, m_window);
  310. return false;
  311. }
  312. // save current focus
  313. XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert);
  314. // take focus
  315. if (m_isPrimary || !m_preserveFocus) {
  316. XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
  317. }
  318. // now warp the mouse. we warp after showing the window so we're
  319. // guaranteed to get the mouse leave event and to prevent the
  320. // keyboard focus from changing under point-to-focus policies.
  321. if (m_isPrimary) {
  322. warpCursor(m_xCenter, m_yCenter);
  323. }
  324. else {
  325. fakeMouseMove(m_xCenter, m_yCenter);
  326. }
  327. // set input context focus to our window
  328. if (m_ic != NULL) {
  329. XmbResetIC(m_ic);
  330. XSetICFocus(m_ic);
  331. m_filtered.clear();
  332. }
  333. // now off screen
  334. m_isOnScreen = false;
  335. return true;
  336. }
  337. bool
  338. XWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard)
  339. {
  340. // fail if we don't have the requested clipboard
  341. if (m_clipboard[id] == NULL) {
  342. return false;
  343. }
  344. // get the actual time. ICCCM does not allow CurrentTime.
  345. Time timestamp = XWindowsUtil::getCurrentTime(
  346. m_display, m_clipboard[id]->getWindow());
  347. if (clipboard != NULL) {
  348. // save clipboard data
  349. return Clipboard::copy(m_clipboard[id], clipboard, timestamp);
  350. }
  351. else {
  352. // assert clipboard ownership
  353. if (!m_clipboard[id]->open(timestamp)) {
  354. return false;
  355. }
  356. m_clipboard[id]->empty();
  357. m_clipboard[id]->close();
  358. return true;
  359. }
  360. }
  361. void
  362. XWindowsScreen::checkClipboards()
  363. {
  364. // do nothing, we're always up to date
  365. }
  366. void
  367. XWindowsScreen::openScreensaver(bool notify)
  368. {
  369. m_screensaverNotify = notify;
  370. if (!m_screensaverNotify) {
  371. m_screensaver->disable();
  372. }
  373. }
  374. void
  375. XWindowsScreen::closeScreensaver()
  376. {
  377. if (!m_screensaverNotify) {
  378. m_screensaver->enable();
  379. }
  380. }
  381. void
  382. XWindowsScreen::screensaver(bool activate)
  383. {
  384. if (activate) {
  385. m_screensaver->activate();
  386. }
  387. else {
  388. m_screensaver->deactivate();
  389. }
  390. }
  391. void
  392. XWindowsScreen::resetOptions()
  393. {
  394. m_xtestIsXineramaUnaware = true;
  395. m_preserveFocus = false;
  396. }
  397. void
  398. XWindowsScreen::setOptions(const OptionsList& options)
  399. {
  400. for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
  401. if (options[i] == kOptionXTestXineramaUnaware) {
  402. m_xtestIsXineramaUnaware = (options[i + 1] != 0);
  403. LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false"));
  404. }
  405. else if (options[i] == kOptionScreenPreserveFocus) {
  406. m_preserveFocus = (options[i + 1] != 0);
  407. LOG((CLOG_DEBUG1 "Preserve Focus = %s", m_preserveFocus ? "true" : "false"));
  408. }
  409. }
  410. }
  411. void
  412. XWindowsScreen::setSequenceNumber(UInt32 seqNum)
  413. {
  414. m_sequenceNumber = seqNum;
  415. }
  416. bool
  417. XWindowsScreen::isPrimary() const
  418. {
  419. return m_isPrimary;
  420. }
  421. void*
  422. XWindowsScreen::getEventTarget() const
  423. {
  424. return const_cast<XWindowsScreen*>(this);
  425. }
  426. bool
  427. XWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const
  428. {
  429. assert(clipboard != NULL);
  430. // fail if we don't have the requested clipboard
  431. if (m_clipboard[id] == NULL) {
  432. return false;
  433. }
  434. // get the actual time. ICCCM does not allow CurrentTime.
  435. Time timestamp = XWindowsUtil::getCurrentTime(
  436. m_display, m_clipboard[id]->getWindow());
  437. // copy the clipboard
  438. return Clipboard::copy(clipboard, m_clipboard[id], timestamp);
  439. }
  440. void
  441. XWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
  442. {
  443. x = m_x;
  444. y = m_y;
  445. w = m_w;
  446. h = m_h;
  447. }
  448. void
  449. XWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
  450. {
  451. Window root, window;
  452. int mx, my, xWindow, yWindow;
  453. unsigned int mask;
  454. if (XQueryPointer(m_display, m_root, &root, &window,
  455. &mx, &my, &xWindow, &yWindow, &mask)) {
  456. x = mx;
  457. y = my;
  458. }
  459. else {
  460. x = m_xCenter;
  461. y = m_yCenter;
  462. }
  463. }
  464. void
  465. XWindowsScreen::reconfigure(UInt32)
  466. {
  467. // do nothing
  468. }
  469. void
  470. XWindowsScreen::warpCursor(SInt32 x, SInt32 y)
  471. {
  472. // warp mouse
  473. warpCursorNoFlush(x, y);
  474. // remove all input events before and including warp
  475. XEvent event;
  476. while (XCheckMaskEvent(m_display, PointerMotionMask |
  477. ButtonPressMask | ButtonReleaseMask |
  478. KeyPressMask | KeyReleaseMask |
  479. KeymapStateMask,
  480. &event)) {
  481. // do nothing
  482. }
  483. // save position as last position
  484. m_xCursor = x;
  485. m_yCursor = y;
  486. }
  487. UInt32
  488. XWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
  489. {
  490. // only allow certain modifiers
  491. if ((mask & ~(KeyModifierShift | KeyModifierControl |
  492. KeyModifierAlt | KeyModifierSuper)) != 0) {
  493. LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
  494. return 0;
  495. }
  496. // fail if no keys
  497. if (key == kKeyNone && mask == 0) {
  498. return 0;
  499. }
  500. // convert to X
  501. unsigned int modifiers;
  502. if (!m_keyState->mapModifiersToX(mask, modifiers)) {
  503. // can't map all modifiers
  504. LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
  505. return 0;
  506. }
  507. XWindowsKeyState::KeycodeList keycodes;
  508. m_keyState->mapKeyToKeycodes(key, keycodes);
  509. if (key != kKeyNone && keycodes.empty()) {
  510. // can't map key
  511. LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
  512. return 0;
  513. }
  514. // choose hotkey id
  515. UInt32 id;
  516. if (!m_oldHotKeyIDs.empty()) {
  517. id = m_oldHotKeyIDs.back();
  518. m_oldHotKeyIDs.pop_back();
  519. }
  520. else {
  521. id = m_hotKeys.size() + 1;
  522. }
  523. HotKeyList& hotKeys = m_hotKeys[id];
  524. // all modifier hotkey must be treated specially. for each modifier
  525. // we need to grab the modifier key in combination with all the other
  526. // requested modifiers.
  527. bool err = false;
  528. {
  529. XWindowsUtil::ErrorLock lock(m_display, &err);
  530. if (key == kKeyNone) {
  531. static const KeyModifierMask s_hotKeyModifiers[] = {
  532. KeyModifierShift,
  533. KeyModifierControl,
  534. KeyModifierAlt,
  535. KeyModifierMeta,
  536. KeyModifierSuper
  537. };
  538. XModifierKeymap* modKeymap = XGetModifierMapping(m_display);
  539. for (size_t j = 0; j < sizeof(s_hotKeyModifiers) /
  540. sizeof(s_hotKeyModifiers[0]) && !err; ++j) {
  541. // skip modifier if not in mask
  542. if ((mask & s_hotKeyModifiers[j]) == 0) {
  543. continue;
  544. }
  545. // skip with error if we can't map remaining modifiers
  546. unsigned int modifiers2;
  547. KeyModifierMask mask2 = (mask & ~s_hotKeyModifiers[j]);
  548. if (!m_keyState->mapModifiersToX(mask2, modifiers2)) {
  549. err = true;
  550. continue;
  551. }
  552. // compute modifier index for modifier. there should be
  553. // exactly one X modifier missing
  554. int index;
  555. switch (modifiers ^ modifiers2) {
  556. case ShiftMask:
  557. index = ShiftMapIndex;
  558. break;
  559. case LockMask:
  560. index = LockMapIndex;
  561. break;
  562. case ControlMask:
  563. index = ControlMapIndex;
  564. break;
  565. case Mod1Mask:
  566. index = Mod1MapIndex;
  567. break;
  568. case Mod2Mask:
  569. index = Mod2MapIndex;
  570. break;
  571. case Mod3Mask:
  572. index = Mod3MapIndex;
  573. break;
  574. case Mod4Mask:
  575. index = Mod4MapIndex;
  576. break;
  577. case Mod5Mask:
  578. index = Mod5MapIndex;
  579. break;
  580. default:
  581. err = true;
  582. continue;
  583. }
  584. // grab each key for the modifier
  585. const KeyCode* modifiermap =
  586. modKeymap->modifiermap + index * modKeymap->max_keypermod;
  587. for (int k = 0; k < modKeymap->max_keypermod && !err; ++k) {
  588. KeyCode code = modifiermap[k];
  589. if (modifiermap[k] != 0) {
  590. XGrabKey(m_display, code, modifiers2, m_root,
  591. False, GrabModeAsync, GrabModeAsync);
  592. if (!err) {
  593. hotKeys.push_back(std::make_pair(code, modifiers2));
  594. m_hotKeyToIDMap[HotKeyItem(code, modifiers2)] = id;
  595. }
  596. }
  597. }
  598. }
  599. XFreeModifiermap(modKeymap);
  600. }
  601. // a non-modifier key must be insensitive to CapsLock, NumLock and
  602. // ScrollLock, so we have to grab the key with every combination of
  603. // those.
  604. else {
  605. // collect available toggle modifiers
  606. unsigned int modifier;
  607. unsigned int toggleModifiers[3];
  608. size_t numToggleModifiers = 0;
  609. if (m_keyState->mapModifiersToX(KeyModifierCapsLock, modifier)) {
  610. toggleModifiers[numToggleModifiers++] = modifier;
  611. }
  612. if (m_keyState->mapModifiersToX(KeyModifierNumLock, modifier)) {
  613. toggleModifiers[numToggleModifiers++] = modifier;
  614. }
  615. if (m_keyState->mapModifiersToX(KeyModifierScrollLock, modifier)) {
  616. toggleModifiers[numToggleModifiers++] = modifier;
  617. }
  618. for (XWindowsKeyState::KeycodeList::iterator j = keycodes.begin();
  619. j != keycodes.end() && !err; ++j) {
  620. for (size_t i = 0; i < (1u << numToggleModifiers); ++i) {
  621. // add toggle modifiers for index i
  622. unsigned int tmpModifiers = modifiers;
  623. if ((i & 1) != 0) {
  624. tmpModifiers |= toggleModifiers[0];
  625. }
  626. if ((i & 2) != 0) {
  627. tmpModifiers |= toggleModifiers[1];
  628. }
  629. if ((i & 4) != 0) {
  630. tmpModifiers |= toggleModifiers[2];
  631. }
  632. // add grab
  633. XGrabKey(m_display, *j, tmpModifiers, m_root,
  634. False, GrabModeAsync, GrabModeAsync);
  635. if (!err) {
  636. hotKeys.push_back(std::make_pair(*j, tmpModifiers));
  637. m_hotKeyToIDMap[HotKeyItem(*j, tmpModifiers)] = id;
  638. }
  639. }
  640. }
  641. }
  642. }
  643. if (err) {
  644. // if any failed then unregister any we did get
  645. for (HotKeyList::iterator j = hotKeys.begin();
  646. j != hotKeys.end(); ++j) {
  647. XUngrabKey(m_display, j->first, j->second, m_root);
  648. m_hotKeyToIDMap.erase(HotKeyItem(j->first, j->second));
  649. }
  650. m_oldHotKeyIDs.push_back(id);
  651. m_hotKeys.erase(id);
  652. LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask));
  653. return 0;
  654. }
  655. LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", synergy::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
  656. return id;
  657. }
  658. void
  659. XWindowsScreen::unregisterHotKey(UInt32 id)
  660. {
  661. // look up hotkey
  662. HotKeyMap::iterator i = m_hotKeys.find(id);
  663. if (i == m_hotKeys.end()) {
  664. return;
  665. }
  666. // unregister with OS
  667. bool err = false;
  668. {
  669. XWindowsUtil::ErrorLock lock(m_display, &err);
  670. HotKeyList& hotKeys = i->second;
  671. for (HotKeyList::iterator j = hotKeys.begin();
  672. j != hotKeys.end(); ++j) {
  673. XUngrabKey(m_display, j->first, j->second, m_root);
  674. m_hotKeyToIDMap.erase(HotKeyItem(j->first, j->second));
  675. }
  676. }
  677. if (err) {
  678. LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
  679. }
  680. else {
  681. LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
  682. }
  683. // discard hot key from map and record old id for reuse
  684. m_hotKeys.erase(i);
  685. m_oldHotKeyIDs.push_back(id);
  686. }
  687. void
  688. XWindowsScreen::fakeInputBegin()
  689. {
  690. // FIXME -- not implemented
  691. }
  692. void
  693. XWindowsScreen::fakeInputEnd()
  694. {
  695. // FIXME -- not implemented
  696. }
  697. SInt32
  698. XWindowsScreen::getJumpZoneSize() const
  699. {
  700. return 1;
  701. }
  702. bool
  703. XWindowsScreen::isAnyMouseButtonDown(UInt32& buttonID) const
  704. {
  705. // query the pointer to get the button state
  706. Window root, window;
  707. int xRoot, yRoot, xWindow, yWindow;
  708. unsigned int state;
  709. if (XQueryPointer(m_display, m_root, &root, &window,
  710. &xRoot, &yRoot, &xWindow, &yWindow, &state)) {
  711. return ((state & (Button1Mask | Button2Mask | Button3Mask |
  712. Button4Mask | Button5Mask)) != 0);
  713. }
  714. return false;
  715. }
  716. void
  717. XWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
  718. {
  719. x = m_xCenter;
  720. y = m_yCenter;
  721. }
  722. void
  723. XWindowsScreen::fakeMouseButton(ButtonID button, bool press)
  724. {
  725. const unsigned int xButton = mapButtonToX(button);
  726. if (xButton != 0) {
  727. XTestFakeButtonEvent(m_display, xButton,
  728. press ? True : False, CurrentTime);
  729. XFlush(m_display);
  730. }
  731. }
  732. void
  733. XWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y)
  734. {
  735. if (m_xinerama && m_xtestIsXineramaUnaware) {
  736. XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
  737. }
  738. else {
  739. XTestFakeMotionEvent(m_display, DefaultScreen(m_display),
  740. x, y, CurrentTime);
  741. }
  742. XFlush(m_display);
  743. }
  744. void
  745. XWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
  746. {
  747. // FIXME -- ignore xinerama for now
  748. if (false && m_xinerama && m_xtestIsXineramaUnaware) {
  749. // XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
  750. }
  751. else {
  752. XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime);
  753. }
  754. XFlush(m_display);
  755. }
  756. void
  757. XWindowsScreen::fakeMouseWheel(SInt32, SInt32 yDelta) const
  758. {
  759. // XXX -- support x-axis scrolling
  760. if (yDelta == 0) {
  761. return;
  762. }
  763. // choose button depending on rotation direction
  764. const unsigned int xButton = mapButtonToX(static_cast<ButtonID>(
  765. (yDelta >= 0) ? -1 : -2));
  766. if (xButton == 0) {
  767. // If we get here, then the XServer does not support the scroll
  768. // wheel buttons, so send PageUp/PageDown keystrokes instead.
  769. // Patch by Tom Chadwick.
  770. KeyCode keycode = 0;
  771. if (yDelta >= 0) {
  772. keycode = XKeysymToKeycode(m_display, XK_Page_Up);
  773. }
  774. else {
  775. keycode = XKeysymToKeycode(m_display, XK_Page_Down);
  776. }
  777. if (keycode != 0) {
  778. XTestFakeKeyEvent(m_display, keycode, True, CurrentTime);
  779. XTestFakeKeyEvent(m_display, keycode, False, CurrentTime);
  780. }
  781. return;
  782. }
  783. // now use absolute value of delta
  784. if (yDelta < 0) {
  785. yDelta = -yDelta;
  786. }
  787. if (yDelta < m_mouseScrollDelta) {
  788. LOG((CLOG_WARN "Wheel scroll delta (%d) smaller than threshold (%d)", yDelta, m_mouseScrollDelta));
  789. }
  790. // send as many clicks as necessary
  791. for (; yDelta >= m_mouseScrollDelta; yDelta -= m_mouseScrollDelta) {
  792. XTestFakeButtonEvent(m_display, xButton, True, CurrentTime);
  793. XTestFakeButtonEvent(m_display, xButton, False, CurrentTime);
  794. }
  795. XFlush(m_display);
  796. }
  797. Display*
  798. XWindowsScreen::openDisplay(const char* displayName)
  799. {
  800. // get the DISPLAY
  801. if (displayName == NULL) {
  802. displayName = getenv("DISPLAY");
  803. if (displayName == NULL) {
  804. displayName = ":0.0";
  805. }
  806. }
  807. // open the display
  808. LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName));
  809. Display* display = XOpenDisplay(displayName);
  810. if (display == NULL) {
  811. throw XScreenUnavailable(60.0);
  812. }
  813. // verify the availability of the XTest extension
  814. if (!m_isPrimary) {
  815. int majorOpcode, firstEvent, firstError;
  816. if (!XQueryExtension(display, XTestExtensionName,
  817. &majorOpcode, &firstEvent, &firstError)) {
  818. LOG((CLOG_ERR "XTEST extension not available"));
  819. XCloseDisplay(display);
  820. throw XScreenOpenFailure();
  821. }
  822. }
  823. #if HAVE_XKB_EXTENSION
  824. {
  825. m_xkb = false;
  826. int major = XkbMajorVersion, minor = XkbMinorVersion;
  827. if (XkbLibraryVersion(&major, &minor)) {
  828. int opcode, firstError;
  829. if (XkbQueryExtension(display, &opcode, &m_xkbEventBase,
  830. &firstError, &major, &minor)) {
  831. m_xkb = true;
  832. XkbSelectEvents(display, XkbUseCoreKbd,
  833. XkbMapNotifyMask, XkbMapNotifyMask);
  834. XkbSelectEventDetails(display, XkbUseCoreKbd,
  835. XkbStateNotifyMask,
  836. XkbGroupStateMask, XkbGroupStateMask);
  837. }
  838. }
  839. }
  840. #endif
  841. #if HAVE_X11_EXTENSIONS_XRANDR_H
  842. // query for XRandR extension
  843. int dummyError;
  844. m_xrandr = XRRQueryExtension(display, &m_xrandrEventBase, &dummyError);
  845. if (m_xrandr) {
  846. // enable XRRScreenChangeNotifyEvent
  847. XRRSelectInput(display, DefaultRootWindow(display), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
  848. }
  849. #endif
  850. return display;
  851. }
  852. void
  853. XWindowsScreen::saveShape()
  854. {
  855. // get shape of default screen
  856. m_x = 0;
  857. m_y = 0;
  858. m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display));
  859. m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display));
  860. #if HAVE_X11_EXTENSIONS_XRANDR_H
  861. if (m_xrandr){
  862. int numSizes;
  863. XRRScreenSize* xrrs;
  864. Rotation rotation;
  865. xrrs = XRRSizes(m_display, DefaultScreen(m_display), &numSizes);
  866. XRRRotations(m_display, DefaultScreen(m_display), &rotation);
  867. if (xrrs != NULL) {
  868. if (rotation & (RR_Rotate_90|RR_Rotate_270) ){
  869. m_w = xrrs->height;
  870. m_h = xrrs->width;
  871. }
  872. }
  873. }
  874. #endif
  875. // get center of default screen
  876. m_xCenter = m_x + (m_w >> 1);
  877. m_yCenter = m_y + (m_h >> 1);
  878. // check if xinerama is enabled and there is more than one screen.
  879. // get center of first Xinerama screen. Xinerama appears to have
  880. // a bug when XWarpPointer() is used in combination with
  881. // XGrabPointer(). in that case, the warp is successful but the
  882. // next pointer motion warps the pointer again, apparently to
  883. // constrain it to some unknown region, possibly the region from
  884. // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over
  885. // all physical screens. this warp only seems to happen if the
  886. // pointer wasn't in that region before the XWarpPointer(). the
  887. // second (unexpected) warp causes synergy to think the pointer
  888. // has been moved when it hasn't. to work around the problem,
  889. // we warp the pointer to the center of the first physical
  890. // screen instead of the logical screen.
  891. m_xinerama = false;
  892. #if HAVE_X11_EXTENSIONS_XINERAMA_H
  893. int eventBase, errorBase;
  894. if (XineramaQueryExtension(m_display, &eventBase, &errorBase) &&
  895. XineramaIsActive(m_display)) {
  896. int numScreens;
  897. XineramaScreenInfo* screens;
  898. screens = XineramaQueryScreens(m_display, &numScreens);
  899. if (screens != NULL) {
  900. if (numScreens > 1) {
  901. m_xinerama = true;
  902. m_xCenter = screens[0].x_org + (screens[0].width >> 1);
  903. m_yCenter = screens[0].y_org + (screens[0].height >> 1);
  904. }
  905. XFree(screens);
  906. }
  907. }
  908. #endif
  909. }
  910. Window
  911. XWindowsScreen::openWindow() const
  912. {
  913. // default window attributes. we don't want the window manager
  914. // messing with our window and we don't want the cursor to be
  915. // visible inside the window.
  916. XSetWindowAttributes attr;
  917. attr.do_not_propagate_mask = 0;
  918. attr.override_redirect = True;
  919. attr.cursor = createBlankCursor();
  920. // adjust attributes and get size and shape
  921. SInt32 x, y, w, h;
  922. if (m_isPrimary) {
  923. // grab window attributes. this window is used to capture user
  924. // input when the user is focused on another client. it covers
  925. // the whole screen.
  926. attr.event_mask = PointerMotionMask |
  927. ButtonPressMask | ButtonReleaseMask |
  928. KeyPressMask | KeyReleaseMask |
  929. KeymapStateMask | PropertyChangeMask;
  930. x = m_x;
  931. y = m_y;
  932. w = m_w;
  933. h = m_h;
  934. }
  935. else {
  936. // cursor hider window attributes. this window is used to hide the
  937. // cursor when it's not on the screen. the window is hidden as soon
  938. // as the cursor enters the screen or the display's real mouse is
  939. // moved. we'll reposition the window as necessary so its
  940. // position here doesn't matter. it only needs to be 1x1 because
  941. // it only needs to contain the cursor's hotspot.
  942. attr.event_mask = LeaveWindowMask;
  943. x = 0;
  944. y = 0;
  945. w = 1;
  946. h = 1;
  947. }
  948. // create and return the window
  949. Window window = XCreateWindow(m_display, m_root, x, y, w, h, 0, 0,
  950. InputOnly, CopyFromParent,
  951. CWDontPropagate | CWEventMask |
  952. CWOverrideRedirect | CWCursor,
  953. &attr);
  954. if (window == None) {
  955. throw XScreenOpenFailure();
  956. }
  957. return window;
  958. }
  959. void
  960. XWindowsScreen::openIM()
  961. {
  962. // open the input methods
  963. XIM im = XOpenIM(m_display, NULL, NULL, NULL);
  964. if (im == NULL) {
  965. LOG((CLOG_INFO "no support for IM"));
  966. return;
  967. }
  968. // find the appropriate style. synergy supports XIMPreeditNothing
  969. // only at the moment.
  970. XIMStyles* styles;
  971. if (XGetIMValues(im, XNQueryInputStyle, &styles, NULL) != NULL ||
  972. styles == NULL) {
  973. LOG((CLOG_WARN "cannot get IM styles"));
  974. XCloseIM(im);
  975. return;
  976. }
  977. XIMStyle style = 0;
  978. for (unsigned short i = 0; i < styles->count_styles; ++i) {
  979. style = styles->supported_styles[i];
  980. if ((style & XIMPreeditNothing) != 0) {
  981. if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) {
  982. break;
  983. }
  984. }
  985. }
  986. XFree(styles);
  987. if (style == 0) {
  988. LOG((CLOG_INFO "no supported IM styles"));
  989. XCloseIM(im);
  990. return;
  991. }
  992. // create an input context for the style and tell it about our window
  993. XIC ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, m_window, NULL);
  994. if (ic == NULL) {
  995. LOG((CLOG_WARN "cannot create IC"));
  996. XCloseIM(im);
  997. return;
  998. }
  999. // find out the events we must select for and do so
  1000. unsigned long mask;
  1001. if (XGetICValues(ic, XNFilterEvents, &mask, NULL) != NULL) {
  1002. LOG((CLOG_WARN "cannot get IC filter events"));
  1003. XDestroyIC(ic);
  1004. XCloseIM(im);
  1005. return;
  1006. }
  1007. // we have IM
  1008. m_im = im;
  1009. m_ic = ic;
  1010. m_lastKeycode = 0;
  1011. // select events on our window that IM requires
  1012. XWindowAttributes attr;
  1013. XGetWindowAttributes(m_display, m_window, &attr);
  1014. XSelectInput(m_display, m_window, attr.your_event_mask | mask);
  1015. }
  1016. void
  1017. XWindowsScreen::sendEvent(Event::Type type, void* data)
  1018. {
  1019. m_events->addEvent(Event(type, getEventTarget(), data));
  1020. }
  1021. void
  1022. XWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id)
  1023. {
  1024. ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
  1025. info->m_id = id;
  1026. info->m_sequenceNumber = m_sequenceNumber;
  1027. sendEvent(type, info);
  1028. }
  1029. IKeyState*
  1030. XWindowsScreen::getKeyState() const
  1031. {
  1032. return m_keyState;
  1033. }
  1034. Bool
  1035. XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
  1036. {
  1037. KeyEventFilter* filter = reinterpret_cast<KeyEventFilter*>(arg);
  1038. return (xevent->type == filter->m_event &&
  1039. xevent->xkey.window == filter->m_window &&
  1040. xevent->xkey.time == filter->m_time &&
  1041. xevent->xkey.keycode == filter->m_keycode) ? True : False;
  1042. }
  1043. void
  1044. XWindowsScreen::handleSystemEvent(const Event& event, void*)
  1045. {
  1046. XEvent* xevent = reinterpret_cast<XEvent*>(event.getData());
  1047. assert(xevent != NULL);
  1048. // update key state
  1049. bool isRepeat = false;
  1050. if (m_isPrimary) {
  1051. if (xevent->type == KeyRelease) {
  1052. // check if this is a key repeat by getting the next
  1053. // KeyPress event that has the same key and time as
  1054. // this release event, if any. first prepare the
  1055. // filter info.
  1056. KeyEventFilter filter;
  1057. filter.m_event = KeyPress;
  1058. filter.m_window = xevent->xkey.window;
  1059. filter.m_time = xevent->xkey.time;
  1060. filter.m_keycode = xevent->xkey.keycode;
  1061. XEvent xevent2;
  1062. isRepeat = (XCheckIfEvent(m_display, &xevent2,
  1063. &XWindowsScreen::findKeyEvent,
  1064. (XPointer)&filter) == True);
  1065. }
  1066. if (xevent->type == KeyPress || xevent->type == KeyRelease) {
  1067. if (xevent->xkey.window == m_root) {
  1068. // this is a hot key
  1069. onHotKey(xevent->xkey, isRepeat);
  1070. return;
  1071. }
  1072. else if (!m_isOnScreen) {
  1073. // this might be a hot key
  1074. if (onHotKey(xevent->xkey, isRepeat)) {
  1075. return;
  1076. }
  1077. }
  1078. bool down = (isRepeat || xevent->type == KeyPress);
  1079. KeyModifierMask state =
  1080. m_keyState->mapModifiersFromX(xevent->xkey.state);
  1081. m_keyState->onKey(xevent->xkey.keycode, down, state);
  1082. }
  1083. }
  1084. // let input methods try to handle event first
  1085. if (m_ic != NULL) {
  1086. // XFilterEvent() may eat the event and generate a new KeyPress
  1087. // event with a keycode of 0 because there isn't an actual key
  1088. // associated with the keysym. but the KeyRelease may pass
  1089. // through XFilterEvent() and keep its keycode. this means
  1090. // there's a mismatch between KeyPress and KeyRelease keycodes.
  1091. // since we use the keycode on the client to detect when a key
  1092. // is released this won't do. so we remember the keycode on
  1093. // the most recent KeyPress (and clear it on a matching
  1094. // KeyRelease) so we have a keycode for a synthesized KeyPress.
  1095. if (xevent->type == KeyPress && xevent->xkey.keycode != 0) {
  1096. m_lastKeycode = xevent->xkey.keycode;
  1097. }
  1098. else if (xevent->type == KeyRelease &&
  1099. xevent->xkey.keycode == m_lastKeycode) {
  1100. m_lastKeycode = 0;
  1101. }
  1102. // now filter the event
  1103. if (XFilterEvent(xevent, DefaultRootWindow(m_display))) {
  1104. if (xevent->type == KeyPress) {
  1105. // add filtered presses to the filtered list
  1106. m_filtered.insert(m_lastKeycode);
  1107. }
  1108. return;
  1109. }
  1110. // discard matching key releases for key presses that were
  1111. // filtered and remove them from our filtered list.
  1112. else if (xevent->type == KeyRelease &&
  1113. m_filtered.count(xevent->xkey.keycode) > 0) {
  1114. m_filtered.erase(xevent->xkey.keycode);
  1115. return;
  1116. }
  1117. }
  1118. // let screen saver have a go
  1119. if (m_screensaver->handleXEvent(xevent)) {
  1120. // screen saver handled it
  1121. return;
  1122. }
  1123. #ifdef HAVE_XI2
  1124. if (m_xi2detected) {
  1125. // Process RawMotion
  1126. XGenericEventCookie *cookie = (XGenericEventCookie*)&xevent->xcookie;
  1127. if (XGetEventData(m_display, cookie) &&
  1128. cookie->type == GenericEvent &&
  1129. cookie->extension == xi_opcode) {
  1130. if (cookie->evtype == XI_RawMotion) {
  1131. // Get current pointer's position
  1132. Window root, child;
  1133. XMotionEvent xmotion;
  1134. xmotion.type = MotionNotify;
  1135. xmotion.send_event = False; // Raw motion
  1136. xmotion.display = m_display;
  1137. xmotion.window = m_window;
  1138. /* xmotion's time, state and is_hint are not used */
  1139. unsigned int msk;
  1140. xmotion.same_screen = XQueryPointer(
  1141. m_display, m_root, &xmotion.root, &xmotion.subwindow,
  1142. &xmotion.x_root,
  1143. &xmotion.y_root,
  1144. &xmotion.x,
  1145. &xmotion.y,
  1146. &msk);
  1147. onMouseMove(xmotion);
  1148. XFreeEventData(m_display, cookie);
  1149. return;
  1150. }
  1151. XFreeEventData(m_display, cookie);
  1152. }
  1153. }
  1154. #endif
  1155. // handle the event ourself
  1156. switch (xevent->type) {
  1157. case CreateNotify:
  1158. if (m_isPrimary) {
  1159. // select events on new window
  1160. selectEvents(xevent->xcreatewindow.window);
  1161. }
  1162. break;
  1163. case MappingNotify:
  1164. refreshKeyboard(xevent);
  1165. break;
  1166. case LeaveNotify:
  1167. if (!m_isPrimary) {
  1168. // mouse moved out of hider window somehow. hide the window.
  1169. XUnmapWindow(m_display, m_window);
  1170. }
  1171. break;
  1172. case SelectionClear:
  1173. {
  1174. // we just lost the selection. that means someone else
  1175. // grabbed the selection so this screen is now the
  1176. // selection owner. report that to the receiver.
  1177. ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
  1178. if (id != kClipboardEnd) {
  1179. m_clipboard[id]->lost(xevent->xselectionclear.time);
  1180. sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id);
  1181. return;
  1182. }
  1183. }
  1184. break;
  1185. case SelectionNotify:
  1186. // notification of selection transferred. we shouldn't
  1187. // get this here because we handle them in the selection
  1188. // retrieval methods. we'll just delete the property
  1189. // with the data (satisfying the usual ICCCM protocol).
  1190. if (xevent->xselection.property != None) {
  1191. XDeleteProperty(m_display,
  1192. xevent->xselection.requestor,
  1193. xevent->xselection.property);
  1194. }
  1195. break;
  1196. case SelectionRequest:
  1197. {
  1198. // somebody is asking for clipboard data
  1199. ClipboardID id = getClipboardID(
  1200. xevent->xselectionrequest.selection);
  1201. if (id != kClipboardEnd) {
  1202. m_clipboard[id]->addRequest(
  1203. xevent->xselectionrequest.owner,
  1204. xevent->xselectionrequest.requestor,
  1205. xevent->xselectionrequest.target,
  1206. xevent->xselectionrequest.time,
  1207. xevent->xselectionrequest.property);
  1208. return;
  1209. }
  1210. }
  1211. break;
  1212. case PropertyNotify:
  1213. // property delete may be part of a selection conversion
  1214. if (xevent->xproperty.state == PropertyDelete) {
  1215. processClipboardRequest(xevent->xproperty.window,
  1216. xevent->xproperty.time,
  1217. xevent->xproperty.atom);
  1218. }
  1219. break;
  1220. case DestroyNotify:
  1221. // looks like one of the windows that requested a clipboard
  1222. // transfer has gone bye-bye.
  1223. destroyClipboardRequest(xevent->xdestroywindow.window);
  1224. break;
  1225. case KeyPress:
  1226. if (m_isPrimary) {
  1227. onKeyPress(xevent->xkey);
  1228. }
  1229. return;
  1230. case KeyRelease:
  1231. if (m_isPrimary) {
  1232. onKeyRelease(xevent->xkey, isRepeat);
  1233. }
  1234. return;
  1235. case ButtonPress:
  1236. if (m_isPrimary) {
  1237. onMousePress(xevent->xbutton);
  1238. }
  1239. return;
  1240. case ButtonRelease:
  1241. if (m_isPrimary) {
  1242. onMouseRelease(xevent->xbutton);
  1243. }
  1244. return;
  1245. case MotionNotify:
  1246. if (m_isPrimary) {
  1247. onMouseMove(xevent->xmotion);
  1248. }
  1249. return;
  1250. default:
  1251. #if HAVE_XKB_EXTENSION
  1252. if (m_xkb && xevent->type == m_xkbEventBase) {
  1253. XkbEvent* xkbEvent = reinterpret_cast<XkbEvent*>(xevent);
  1254. switch (xkbEvent->any.xkb_type) {
  1255. case XkbMapNotify:
  1256. refreshKeyboard(xevent);
  1257. return;
  1258. case XkbStateNotify:
  1259. LOG((CLOG_INFO "group change: %d", xkbEvent->state.group));
  1260. m_keyState->setActiveGroup((SInt32)xkbEvent->state.group);
  1261. return;
  1262. }
  1263. }
  1264. #endif
  1265. #if HAVE_X11_EXTENSIONS_XRANDR_H
  1266. if (m_xrandr) {
  1267. if (xevent->type == m_xrandrEventBase + RRScreenChangeNotify
  1268. || xevent->type == m_xrandrEventBase + RRNotify
  1269. && reinterpret_cast<XRRNotifyEvent *>(xevent)->subtype == RRNotify_CrtcChange) {
  1270. LOG((CLOG_INFO "XRRScreenChangeNotifyEvent or RRNotify_CrtcChange received"));
  1271. // we're required to call back into XLib so XLib can update its internal state
  1272. XRRUpdateConfiguration(xevent);
  1273. // requery/recalculate the screen shape
  1274. saveShape();
  1275. // we need to resize m_window, otherwise we'll get a weird problem where moving
  1276. // off the server onto the client causes the pointer to warp to the
  1277. // center of the server (so you can't move the pointer off the server)
  1278. if (m_isPrimary) {
  1279. XMoveWindow(m_display, m_window, m_x, m_y);
  1280. XResizeWindow(m_display, m_window, m_w, m_h);
  1281. }
  1282. }
  1283. }
  1284. #endif
  1285. break;
  1286. }
  1287. }
  1288. void
  1289. XWindowsScreen::onKeyPress(XKeyEvent& xkey)
  1290. {
  1291. LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state));
  1292. const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
  1293. KeyID key = mapKeyFromX(&xkey);
  1294. if (key != kKeyNone) {
  1295. // check for ctrl+alt+del emulation
  1296. if ((key == kKeyPause || key == kKeyBreak) &&
  1297. (mask & (KeyModifierControl | KeyModifierAlt)) ==
  1298. (KeyModifierControl | KeyModifierAlt)) {
  1299. // pretend it's ctrl+alt+del
  1300. LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
  1301. key = kKeyDelete;
  1302. }
  1303. // get which button. see call to XFilterEvent() in onEvent()
  1304. // for more info.
  1305. bool isFake = false;
  1306. KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
  1307. if (keycode == 0) {
  1308. isFake = true;
  1309. keycode = static_cast<KeyButton>(m_lastKeycode);
  1310. if (keycode == 0) {
  1311. // no keycode
  1312. return;
  1313. }
  1314. }
  1315. // handle key
  1316. m_keyState->sendKeyEvent(getEventTarget(),
  1317. true, false, key, mask, 1, keycode);
  1318. // do fake release if this is a fake press
  1319. if (isFake) {
  1320. m_keyState->sendKeyEvent(getEventTarget(),
  1321. false, false, key, mask, 1, keycode);
  1322. }
  1323. }
  1324. }
  1325. void
  1326. XWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
  1327. {
  1328. const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
  1329. KeyID key = mapKeyFromX(&xkey);
  1330. if (key != kKeyNone) {
  1331. // check for ctrl+alt+del emulation
  1332. if ((key == kKeyPause || key == kKeyBreak) &&
  1333. (mask & (KeyModifierControl | KeyModifierAlt)) ==
  1334. (KeyModifierControl | KeyModifierAlt)) {
  1335. // pretend it's ctrl+alt+del and ignore autorepeat
  1336. LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
  1337. key = kKeyDelete;
  1338. isRepeat = false;
  1339. }
  1340. KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
  1341. if (!isRepeat) {
  1342. // no press event follows so it's a plain release
  1343. LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state));
  1344. m_keyState->sendKeyEvent(getEventTarget(),
  1345. false, false, key, mask, 1, keycode);
  1346. }
  1347. else {
  1348. // found a press event following so it's a repeat.
  1349. // we could attempt to count the already queued
  1350. // repeats but we'll just send a repeat of 1.
  1351. // note that we discard the press event.
  1352. LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
  1353. m_keyState->sendKeyEvent(getEventTarget(),
  1354. false, true, key, mask, 1, keycode);
  1355. }
  1356. }
  1357. }
  1358. bool
  1359. XWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat)
  1360. {
  1361. // find the hot key id
  1362. HotKeyToIDMap::const_iterator i =
  1363. m_hotKeyToIDMap.find(HotKeyItem(xkey.keycode, xkey.state));
  1364. if (i == m_hotKeyToIDMap.end()) {
  1365. return false;
  1366. }
  1367. // find what kind of event
  1368. Event::Type type;
  1369. if (xkey.type == KeyPress) {
  1370. type = m_events->forIPrimaryScreen().hotKeyDown();
  1371. }
  1372. else if (xkey.type == KeyRelease) {
  1373. type = m_events->forIPrimaryScreen().hotKeyUp();
  1374. }
  1375. else {
  1376. return false;
  1377. }
  1378. // generate event (ignore key repeats)
  1379. if (!isRepeat) {
  1380. m_events->addEvent(Event(type, getEventTarget(),
  1381. HotKeyInfo::alloc(i->second)));
  1382. }
  1383. return true;
  1384. }
  1385. void
  1386. XWindowsScreen::onMousePress(const XButtonEvent& xbutton)
  1387. {
  1388. LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
  1389. ButtonID button = mapButtonFromX(&xbutton);
  1390. KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
  1391. if (button != kButtonNone) {
  1392. sendEvent(m_events->forIPrimaryScreen().buttonDown(), ButtonInfo::alloc(button, mask));
  1393. }
  1394. }
  1395. void
  1396. XWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
  1397. {
  1398. LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
  1399. ButtonID button = mapButtonFromX(&xbutton);
  1400. KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
  1401. if (button != kButtonNone) {
  1402. sendEvent(m_events->forIPrimaryScreen().buttonUp(), ButtonInfo::alloc(button, mask));
  1403. }
  1404. else if (xbutton.button == 4) {
  1405. // wheel forward (away from user)
  1406. sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, 120));
  1407. }
  1408. else if (xbutton.button == 5) {
  1409. // wheel backward (toward user)
  1410. sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, -120));
  1411. }
  1412. // XXX -- support x-axis scrolling
  1413. }
  1414. void
  1415. XWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
  1416. {
  1417. LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xmotion.x_root, xmotion.y_root));
  1418. // compute motion delta (relative to the last known
  1419. // mouse position)
  1420. SInt32 x = xmotion.x_root - m_xCursor;
  1421. SInt32 y = xmotion.y_root - m_yCursor;
  1422. // save position to compute delta of next motion
  1423. m_xCursor = xmotion.x_root;
  1424. m_yCursor = xmotion.y_root;
  1425. if (xmotion.send_event) {
  1426. // we warped the mouse. discard events until we
  1427. // find the matching sent event. see
  1428. // warpCursorNoFlush() for where the events are
  1429. // sent. we discard the matching sent event and
  1430. // can be sure we've skipped the warp event.
  1431. XEvent xevent;
  1432. char cntr = 0;
  1433. do {
  1434. XMaskEvent(m_display, PointerMotionMask, &xevent);
  1435. if (cntr++ > 10) {
  1436. LOG((CLOG_WARN "too many discarded events! %d", cntr));
  1437. break;
  1438. }
  1439. } while (!xevent.xany.send_event);
  1440. cntr = 0;
  1441. }
  1442. else if (m_isOnScreen) {
  1443. // motion on primary screen
  1444. sendEvent(m_events->forIPrimaryScreen().motionOnPrimary(),
  1445. MotionInfo::alloc(m_xCursor, m_yCursor));
  1446. }
  1447. else {
  1448. // motion on secondary screen. warp mouse back to
  1449. // center.
  1450. //
  1451. // my lombard (powerbook g3) running linux and
  1452. // using the adbmouse driver has two problems:
  1453. // first, the driver only sends motions of +/-2
  1454. // pixels and, second, it seems to discard some
  1455. // physical input after a warp. the former isn't a
  1456. // big deal (we're just limited to every other
  1457. // pixel) but the latter is a PITA. to work around
  1458. // it we only warp when the mouse has moved more
  1459. // than s_size pixels from the center.
  1460. static const SInt32 s_size = 32;
  1461. if (xmotion.x_root - m_xCenter < -s_size ||
  1462. xmotion.x_root - m_xCenter > s_size ||
  1463. xmotion.y_root - m_yCenter < -s_size ||
  1464. xmotion.y_root - m_yCenter > s_size) {
  1465. warpCursorNoFlush(m_xCenter, m_yCenter);
  1466. }
  1467. // send event if mouse moved. do this after warping
  1468. // back to center in case the motion takes us onto
  1469. // the primary screen. if we sent the event first
  1470. // in that case then the warp would happen after
  1471. // warping to the primary screen's enter position,
  1472. // effectively overriding it.
  1473. if (x != 0 || y != 0) {
  1474. sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
  1475. }
  1476. }
  1477. }
  1478. Cursor
  1479. XWindowsScreen::createBlankCursor() const
  1480. {
  1481. // this seems just a bit more complicated than really necessary
  1482. // get the closet cursor size to 1x1
  1483. unsigned int w, h;
  1484. XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
  1485. // make bitmap data for cursor of closet size. since the cursor
  1486. // is blank we can use the same bitmap for shape and mask: all
  1487. // zeros.
  1488. const int size = ((w + 7) >> 3) * h;
  1489. char* data = new char[size];
  1490. memset(data, 0, size);
  1491. // make bitmap
  1492. Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
  1493. // need an arbitrary color for the cursor
  1494. XColor color;
  1495. color.pixel = 0;
  1496. color.red = color.green = color.blue = 0;
  1497. color.flags = DoRed | DoGreen | DoBlue;
  1498. // make cursor from bitmap
  1499. Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
  1500. &color, &color, 0, 0);
  1501. // don't need bitmap or the data anymore
  1502. delete[] data;
  1503. XFreePixmap(m_display, bitmap);
  1504. return cursor;
  1505. }
  1506. ClipboardID
  1507. XWindowsScreen::getClipboardID(Atom selection) const
  1508. {
  1509. for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
  1510. if (m_clipboard[id] != NULL &&
  1511. m_clipboard[id]->getSelection() == selection) {
  1512. return id;
  1513. }
  1514. }
  1515. return kClipboardEnd;
  1516. }
  1517. void
  1518. XWindowsScreen::processClipboardRequest(Window requestor,
  1519. Time time, Atom property)
  1520. {
  1521. // check every clipboard until one returns success
  1522. for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
  1523. if (m_clipboard[id] != NULL &&
  1524. m_clipboard[id]->processRequest(requestor, time, property)) {
  1525. break;
  1526. }
  1527. }
  1528. }
  1529. void
  1530. XWindowsScreen::destroyClipboardRequest(Window requestor)
  1531. {
  1532. // check every clipboard until one returns success
  1533. for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
  1534. if (m_clipboard[id] != NULL &&
  1535. m_clipboard[id]->destroyRequest(requestor)) {
  1536. break;
  1537. }
  1538. }
  1539. }
  1540. void
  1541. XWindowsScreen::onError()
  1542. {
  1543. // prevent further access to the X display
  1544. m_events->adoptBuffer(NULL);
  1545. m_screensaver->destroy();
  1546. m_screensaver = NULL;
  1547. m_display = NULL;
  1548. // notify of failure
  1549. sendEvent(m_events->forIScreen().error(), NULL);
  1550. // FIXME -- should ensure that we ignore operations that involve
  1551. // m_display from now on. however, Xlib will simply exit the
  1552. // application in response to the X I/O error so there's no
  1553. // point in trying to really handle the error. if we did want
  1554. // to handle the error, it'd probably be easiest to delegate to
  1555. // one of two objects. one object would take the implementation
  1556. // from this class. the other object would be stub methods that
  1557. // don't use X11. on error, we'd switch to the latter.
  1558. }
  1559. int
  1560. XWindowsScreen::ioErrorHandler(Display*)
  1561. {
  1562. // the display has disconnected, probably because X is shutting
  1563. // down. X forces us to exit at this point which is annoying.
  1564. // we'll pretend as if we won't exit so we try to make sure we
  1565. // don't access the display anymore.
  1566. LOG((CLOG_CRIT "X display has unexpectedly disconnected"));
  1567. s_screen->onError();
  1568. return 0;
  1569. }
  1570. void
  1571. XWindowsScreen::selectEvents(Window w) const
  1572. {
  1573. // ignore errors while we adjust event masks. windows could be
  1574. // destroyed at any time after the XQueryTree() in doSelectEvents()
  1575. // so we must ignore BadWindow errors.
  1576. XWindowsUtil::ErrorLock lock(m_display);
  1577. // adjust event masks
  1578. doSelectEvents(w);
  1579. }
  1580. void
  1581. XWindowsScreen::doSelectEvents(Window w) const
  1582. {
  1583. // we want to track the mouse everywhere on the display. to achieve
  1584. // that we select PointerMotionMask on every window. we also select
  1585. // SubstructureNotifyMask in order to get CreateNotify events so we
  1586. // select events on new windows too.
  1587. // we don't want to adjust our grab window
  1588. if (w == m_window) {
  1589. return;
  1590. }
  1591. // X11 has a design flaw. If *no* client selected PointerMotionMask for
  1592. // a window, motion events will be delivered to that window's parent.
  1593. // If *any* client, not necessarily the owner, selects PointerMotionMask
  1594. // on such a window, X will stop propagating motion events to its
  1595. // parent. This breaks applications that rely on event propagation
  1596. // behavior.
  1597. //
  1598. // Avoid selecting PointerMotionMask unless some other client selected
  1599. // it already.
  1600. long mask = SubstructureNotifyMask;
  1601. XWindowAttributes attr;
  1602. XGetWindowAttributes(m_display, w, &attr);
  1603. if ((attr.all_event_masks & PointerMotionMask) == PointerMotionMask) {
  1604. mask |= PointerMotionMask;
  1605. }
  1606. // select events of interest. do this before querying the tree so
  1607. // we'll get notifications of children created after the XQueryTree()
  1608. // so we won't miss them.
  1609. XSelectInput(m_display, w, mask);
  1610. // recurse on child windows
  1611. Window rw, pw, *cw;
  1612. unsigned int nc;
  1613. if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
  1614. for (unsigned int i = 0; i < nc; ++i) {
  1615. doSelectEvents(cw[i]);
  1616. }
  1617. XFree(cw);
  1618. }
  1619. }
  1620. KeyID
  1621. XWindowsScreen::mapKeyFromX(XKeyEvent* event) const
  1622. {
  1623. // convert to a keysym
  1624. KeySym keysym;
  1625. if (event->type == KeyPress && m_ic != NULL) {
  1626. // do multibyte lookup. can only call XmbLookupString with a
  1627. // key press event and a valid XIC so we checked those above.
  1628. char scratch[32];
  1629. int n = sizeof(scratch) / sizeof(scratch[0]);
  1630. char* buffer = scratch;
  1631. int status;
  1632. n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
  1633. if (status == XBufferOverflow) {
  1634. // not enough space. grow buffer and try again.
  1635. buffer = new char[n];
  1636. n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
  1637. delete[] buffer;
  1638. }
  1639. // see what we got. since we don't care about the string
  1640. // we'll just look for a keysym.
  1641. switch (status) {
  1642. default:
  1643. case XLookupNone:
  1644. case XLookupChars:
  1645. keysym = 0;
  1646. break;