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