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