Line data Source code
1 :
2 : /* Copyright (c) 2006-2017, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * Cedric Stalder <cedric.stalder@gmail.com>
5 : *
6 : * This library is free software; you can redistribute it and/or modify it under
7 : * the terms of the GNU Lesser General Public License version 2.1 as published
8 : * by the Free Software Foundation.
9 : *
10 : * This library is distributed in the hope that it will be useful, but WITHOUT
11 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
13 : * details.
14 : *
15 : * You should have received a copy of the GNU Lesser General Public License
16 : * along with this library; if not, write to the Free Software Foundation, Inc.,
17 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include "eventHandler.h"
21 :
22 : #include "../config.h"
23 : #include "../global.h"
24 : #include "../log.h"
25 : #include "../os.h"
26 : #include "../pipe.h"
27 : #include "../window.h"
28 : #include "window.h"
29 :
30 : #include <eq/fabric/axisEvent.h>
31 : #include <eq/fabric/buttonEvent.h>
32 : #include <eq/fabric/keyEvent.h>
33 : #include <eq/fabric/sizeEvent.h>
34 : #include <lunchbox/algorithm.h>
35 : #include <lunchbox/lockable.h>
36 : #include <lunchbox/perThread.h>
37 : #include <lunchbox/scopedMutex.h>
38 : #include <lunchbox/spinLock.h>
39 :
40 : #include <X11/XKBlib.h>
41 : #include <X11/keysym.h>
42 :
43 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
44 : #include <spnav.h>
45 : #endif
46 :
47 : namespace eq
48 : {
49 : namespace glx
50 : {
51 : namespace
52 : {
53 : typedef std::vector<EventHandler*> EventHandlers;
54 10 : static lunchbox::PerThread<EventHandlers> _eventHandlers;
55 :
56 : struct MagellanData
57 : {
58 10 : MagellanData()
59 10 : : display(0)
60 10 : , used(0)
61 : {
62 10 : }
63 :
64 : Display* display;
65 : size_t used;
66 : };
67 :
68 10 : static lunchbox::Lockable<MagellanData, lunchbox::SpinLock> _magellan;
69 :
70 0 : uint32_t _getButtonAction(const XEvent& event)
71 : {
72 0 : switch (event.xbutton.button)
73 : {
74 : case Button1:
75 0 : return PTR_BUTTON1;
76 : case Button2:
77 0 : return PTR_BUTTON2;
78 : case Button3:
79 0 : return PTR_BUTTON3;
80 : case Button4:
81 0 : return PTR_BUTTON4;
82 : case Button5:
83 0 : return PTR_BUTTON5;
84 : case 6:
85 0 : return PTR_BUTTON6;
86 : case 7:
87 0 : return PTR_BUTTON7;
88 : default:
89 0 : return PTR_BUTTON_NONE;
90 : }
91 : }
92 :
93 0 : uint32_t _getButtonState(const XEvent& event)
94 : {
95 0 : const int xState = event.xbutton.state;
96 0 : uint32_t state = 0;
97 :
98 0 : if (xState & Button1Mask)
99 0 : state |= PTR_BUTTON1;
100 0 : if (xState & Button2Mask)
101 0 : state |= PTR_BUTTON2;
102 0 : if (xState & Button3Mask)
103 0 : state |= PTR_BUTTON3;
104 0 : if (xState & Button4Mask)
105 0 : state |= PTR_BUTTON4;
106 0 : if (xState & Button5Mask)
107 0 : state |= PTR_BUTTON5;
108 :
109 0 : switch (event.type)
110 : { // state is state before event
111 : case ButtonPress:
112 0 : state |= _getButtonAction(event);
113 0 : break;
114 :
115 : case ButtonRelease:
116 0 : state &= ~_getButtonAction(event);
117 0 : break;
118 :
119 : default:
120 0 : break;
121 : }
122 0 : return state;
123 : }
124 :
125 0 : uint32_t _getKey(const XEvent& event)
126 : {
127 0 : int index = 0;
128 0 : if (event.xkey.state & ShiftMask)
129 0 : index = 1;
130 :
131 : const KeySym key =
132 0 : XkbKeycodeToKeysym(event.xany.display, event.xkey.keycode, 0, index);
133 0 : switch (key)
134 : {
135 : case XK_Escape:
136 0 : return KC_ESCAPE;
137 : case XK_BackSpace:
138 0 : return KC_BACKSPACE;
139 : case XK_Return:
140 0 : return KC_RETURN;
141 : case XK_Tab:
142 0 : return KC_TAB;
143 : case XK_Home:
144 0 : return KC_HOME;
145 : case XK_Left:
146 0 : return KC_LEFT;
147 : case XK_Up:
148 0 : return KC_UP;
149 : case XK_Right:
150 0 : return KC_RIGHT;
151 : case XK_Down:
152 0 : return KC_DOWN;
153 : case XK_Page_Up:
154 0 : return KC_PAGE_UP;
155 : case XK_Page_Down:
156 0 : return KC_PAGE_DOWN;
157 : case XK_End:
158 0 : return KC_END;
159 : case XK_F1:
160 0 : return KC_F1;
161 : case XK_F2:
162 0 : return KC_F2;
163 : case XK_F3:
164 0 : return KC_F3;
165 : case XK_F4:
166 0 : return KC_F4;
167 : case XK_F5:
168 0 : return KC_F5;
169 : case XK_F6:
170 0 : return KC_F6;
171 : case XK_F7:
172 0 : return KC_F7;
173 : case XK_F8:
174 0 : return KC_F8;
175 : case XK_F9:
176 0 : return KC_F9;
177 : case XK_F10:
178 0 : return KC_F10;
179 : case XK_F11:
180 0 : return KC_F11;
181 : case XK_F12:
182 0 : return KC_F12;
183 : case XK_F13:
184 0 : return KC_F13;
185 : case XK_F14:
186 0 : return KC_F14;
187 : case XK_F15:
188 0 : return KC_F15;
189 : case XK_F16:
190 0 : return KC_F16;
191 : case XK_F17:
192 0 : return KC_F17;
193 : case XK_F18:
194 0 : return KC_F18;
195 : case XK_F19:
196 0 : return KC_F19;
197 : case XK_F20:
198 0 : return KC_F20;
199 : case XK_Shift_L:
200 0 : return KC_SHIFT_L;
201 : case XK_Shift_R:
202 0 : return KC_SHIFT_R;
203 : case XK_Control_L:
204 0 : return KC_CONTROL_L;
205 : case XK_Control_R:
206 0 : return KC_CONTROL_R;
207 : case XK_Alt_L:
208 0 : return KC_ALT_L;
209 : case XK_Alt_R:
210 0 : return KC_ALT_R;
211 :
212 : default:
213 0 : if ((key >= XK_space && key <= XK_asciitilde) ||
214 0 : (key >= XK_nobreakspace && key <= XK_ydiaeresis))
215 : {
216 0 : return key;
217 : }
218 0 : LBWARN << "Unrecognized X11 key code " << key << std::endl;
219 0 : return KC_VOID;
220 : }
221 : }
222 :
223 0 : KeyModifier _getKeyModifiers(const unsigned int state)
224 : {
225 0 : KeyModifier result = KeyModifier::none;
226 0 : if (state & Mod1Mask)
227 0 : result |= KeyModifier::alt;
228 0 : if (state & ControlMask)
229 0 : result |= KeyModifier::control;
230 0 : if (state & ShiftMask)
231 0 : result |= KeyModifier::shift;
232 0 : return result;
233 : }
234 : }
235 :
236 2 : EventHandler::EventHandler(WindowIF* window)
237 : : _window(window)
238 2 : , _magellanUsed(false)
239 : {
240 2 : LBASSERT(window);
241 :
242 2 : if (!_eventHandlers)
243 2 : _eventHandlers = new EventHandlers;
244 2 : _eventHandlers->push_back(this);
245 :
246 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
247 2 : Display* display = window->getXDisplay();
248 2 : LBASSERT(display);
249 2 : lunchbox::ScopedFastWrite mutex(_magellan);
250 2 : if (!_magellan->display)
251 : {
252 2 : if (spnav_x11_open(display, window->getXDrawable()) == -1)
253 : {
254 6 : LBDEBUG << "Cannot connect to the space navigator daemon"
255 6 : << std::endl;
256 2 : return;
257 : }
258 0 : _magellan->display = display;
259 : }
260 0 : else if (_magellan->display != display)
261 : {
262 0 : LBINFO << "Multi-display space mouse support incomplete" << std::endl;
263 0 : return;
264 : }
265 0 : else if (spnav_x11_window(window->getXDrawable()) == -1)
266 : {
267 0 : LBWARN << "Failed to register window with the space navigator daemon"
268 0 : << std::endl;
269 0 : return;
270 : }
271 :
272 0 : ++_magellan->used;
273 0 : _magellanUsed = true;
274 : #endif
275 : }
276 :
277 6 : EventHandler::~EventHandler()
278 : {
279 2 : if (_magellanUsed)
280 : {
281 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
282 0 : lunchbox::ScopedFastWrite mutex(_magellan);
283 0 : if (--_magellan->used == 0)
284 : {
285 0 : if (spnav_close() == -1)
286 : {
287 0 : LBWARN
288 0 : << "Couldn't close connection to the space navigator daemon"
289 0 : << std::endl;
290 : }
291 : }
292 : #endif
293 0 : _magellanUsed = false;
294 : }
295 :
296 2 : EventHandlers::iterator i = lunchbox::find(*_eventHandlers, this);
297 2 : LBASSERT(i != _eventHandlers->end());
298 2 : _eventHandlers->erase(i);
299 2 : if (_eventHandlers->empty())
300 : {
301 2 : delete _eventHandlers.get();
302 2 : _eventHandlers = 0;
303 : }
304 4 : }
305 :
306 13944 : void EventHandler::dispatch()
307 : {
308 13944 : if (!_eventHandlers)
309 28 : return;
310 :
311 27832 : for (EventHandler* handler : *_eventHandlers)
312 13916 : handler->_dispatch();
313 : }
314 :
315 13916 : void EventHandler::_dispatch()
316 : {
317 13916 : Display* display = _window->getXDisplay();
318 13916 : LBASSERT(display);
319 13916 : if (!display)
320 0 : return;
321 :
322 13920 : while (XPending(display))
323 : {
324 : XEvent event;
325 2 : XNextEvent(display, &event);
326 :
327 4 : for (EventHandler* handler : *_eventHandlers)
328 2 : handler->_processEvent(event);
329 : }
330 : }
331 :
332 : namespace
333 : {
334 0 : void _getWindowSize(Display* display, XID drawable, SizeEvent& event)
335 : {
336 : // Get window coordinates from X11, the event data is relative to window
337 : // parent, but we report pvp relative to root window.
338 : XWindowAttributes windowAttrs;
339 0 : XGetWindowAttributes(display, drawable, &windowAttrs);
340 :
341 : XID child;
342 0 : XTranslateCoordinates(display, drawable,
343 0 : RootWindowOfScreen(windowAttrs.screen), windowAttrs.x,
344 0 : windowAttrs.y, &event.x, &event.y, &child);
345 :
346 0 : event.w = windowAttrs.width;
347 0 : event.h = windowAttrs.height;
348 0 : }
349 : }
350 :
351 2 : bool EventHandler::_processEvent(const XEvent& event)
352 : {
353 2 : LB_TS_THREAD(_thread);
354 :
355 2 : XID drawable = event.xany.window;
356 2 : if (_window->getXDrawable() != drawable)
357 0 : return false;
358 :
359 2 : switch (event.type)
360 : {
361 : case Expose:
362 1 : if (event.xexpose.count) // Only report last expose event
363 0 : return true;
364 :
365 1 : return _window->processEvent(EVENT_WINDOW_EXPOSE, event);
366 :
367 : case ConfigureNotify:
368 : {
369 0 : SizeEvent sizeEvent;
370 0 : _getWindowSize(event.xany.display, drawable, sizeEvent);
371 0 : return _window->processEvent(EVENT_WINDOW_RESIZE, event, sizeEvent);
372 : }
373 :
374 : case UnmapNotify:
375 : {
376 0 : SizeEvent sizeEvent;
377 0 : _getWindowSize(event.xany.display, drawable, sizeEvent);
378 0 : return _window->processEvent(EVENT_WINDOW_HIDE, event, sizeEvent);
379 : }
380 :
381 : case MapNotify:
382 : {
383 0 : SizeEvent sizeEvent;
384 0 : _getWindowSize(event.xany.display, drawable, sizeEvent);
385 0 : return _window->processEvent(EVENT_WINDOW_SHOW, event, sizeEvent);
386 : }
387 :
388 : case ClientMessage:
389 : {
390 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
391 : spnav_event spev;
392 :
393 0 : if (spnav_x11_event(&event, &spev)) // spacenav event
394 : {
395 0 : switch (spev.type)
396 : {
397 : case SPNAV_EVENT_MOTION:
398 : {
399 0 : AxisEvent axisEvent;
400 0 : axisEvent.xAxis = spev.motion.x;
401 0 : axisEvent.yAxis = spev.motion.y;
402 0 : axisEvent.zAxis = -spev.motion.z;
403 0 : axisEvent.xRotation = -spev.motion.rx;
404 0 : axisEvent.yRotation = -spev.motion.ry;
405 0 : axisEvent.zRotation = spev.motion.rz;
406 0 : return _window->processEvent(event, axisEvent);
407 : }
408 :
409 : case SPNAV_EVENT_BUTTON:
410 : {
411 0 : ButtonEvent buttonEvent;
412 0 : buttonEvent.buttons = spev.button.press;
413 0 : buttonEvent.button = spev.button.bnum;
414 0 : return _window->processEvent(event, buttonEvent);
415 : }
416 :
417 : default:
418 0 : LBUNIMPLEMENTED;
419 0 : return false;
420 : }
421 :
422 : break;
423 : }
424 : #endif
425 :
426 : Atom deleteAtom =
427 0 : XInternAtom(event.xany.display, "WM_DELETE_WINDOW", False);
428 :
429 0 : if (static_cast<Atom>(event.xclient.data.l[0]) != deleteAtom)
430 0 : return false; // not a delete message, ignore.
431 : }
432 : // else: delete message, fall through
433 :
434 : case DestroyNotify:
435 0 : return _window->processEvent(EVENT_WINDOW_CLOSE, event);
436 :
437 : case MotionNotify:
438 : {
439 0 : PointerEvent pointerEvent;
440 0 : pointerEvent.x = event.xmotion.x;
441 0 : pointerEvent.y = event.xmotion.y;
442 0 : pointerEvent.buttons = _getButtonState(event);
443 0 : pointerEvent.button = PTR_BUTTON_NONE;
444 0 : pointerEvent.modifiers = _getKeyModifiers(event.xbutton.state);
445 0 : _computePointerDelta(EVENT_WINDOW_POINTER_MOTION, pointerEvent);
446 :
447 0 : return _window->processEvent(EVENT_WINDOW_POINTER_MOTION, event,
448 0 : pointerEvent);
449 : }
450 :
451 : case ButtonPress:
452 : {
453 0 : PointerEvent pointerEvent;
454 0 : pointerEvent.x = event.xbutton.x;
455 0 : pointerEvent.y = event.xbutton.y;
456 0 : pointerEvent.buttons = _getButtonState(event);
457 0 : pointerEvent.button = _getButtonAction(event);
458 0 : pointerEvent.modifiers = _getKeyModifiers(event.xbutton.state);
459 :
460 0 : switch (pointerEvent.button) // Translate wheel events
461 : {
462 : case PTR_BUTTON4:
463 0 : pointerEvent.yAxis = 1;
464 0 : break;
465 : case PTR_BUTTON5:
466 0 : pointerEvent.yAxis = -1;
467 0 : break;
468 : case PTR_BUTTON6:
469 0 : pointerEvent.xAxis = 1;
470 0 : break;
471 : case PTR_BUTTON7:
472 0 : pointerEvent.xAxis = -1;
473 0 : break;
474 : }
475 :
476 0 : switch (pointerEvent.button)
477 : {
478 : case PTR_BUTTON4:
479 : case PTR_BUTTON5:
480 : case PTR_BUTTON6:
481 : case PTR_BUTTON7:
482 0 : pointerEvent.button = PTR_BUTTON_NONE;
483 0 : _computePointerDelta(EVENT_WINDOW_POINTER_WHEEL, pointerEvent);
484 0 : return _window->processEvent(EVENT_WINDOW_POINTER_WHEEL, event,
485 0 : pointerEvent);
486 : }
487 :
488 0 : _computePointerDelta(EVENT_WINDOW_POINTER_BUTTON_PRESS, pointerEvent);
489 0 : return _window->processEvent(EVENT_WINDOW_POINTER_BUTTON_PRESS, event,
490 0 : pointerEvent);
491 : }
492 :
493 : case ButtonRelease:
494 : {
495 0 : PointerEvent pointerEvent;
496 0 : pointerEvent.x = event.xbutton.x;
497 0 : pointerEvent.y = event.xbutton.y;
498 0 : pointerEvent.buttons = _getButtonState(event);
499 0 : pointerEvent.button = _getButtonAction(event);
500 0 : pointerEvent.modifiers = _getKeyModifiers(event.xbutton.state);
501 :
502 0 : _computePointerDelta(EVENT_WINDOW_POINTER_BUTTON_RELEASE, pointerEvent);
503 0 : return _window->processEvent(EVENT_WINDOW_POINTER_BUTTON_RELEASE, event,
504 0 : pointerEvent);
505 : }
506 :
507 : case KeyPress:
508 : {
509 0 : KeyEvent keyEvent;
510 0 : keyEvent.key = _getKey(event);
511 0 : keyEvent.modifiers = _getKeyModifiers(event.xkey.state);
512 0 : return _window->processEvent(EVENT_KEY_PRESS, event, keyEvent);
513 : }
514 :
515 : case KeyRelease:
516 : {
517 0 : KeyEvent keyEvent;
518 0 : keyEvent.key = _getKey(event);
519 0 : keyEvent.modifiers = _getKeyModifiers(event.xkey.state);
520 0 : return _window->processEvent(EVENT_KEY_RELEASE, event, keyEvent);
521 : }
522 :
523 : default:
524 0 : LBWARN << "Unhandled X event, type " << event.type << std::endl;
525 : // no break;
526 : case ReparentNotify:
527 : case VisibilityNotify:
528 1 : return _window->processEvent(EVENT_UNKNOWN, event);
529 : }
530 : }
531 : }
532 30 : }
|