Line data Source code
1 :
2 : /* Copyright (c) 2006-2014, Stefan Eilemann <eile@equalizergraphics.com>
3 : * 2014, Daniel Nachbaur <danielnachbaur@gmail.com>
4 : * 2011, 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 "window.h"
23 : #include "windowEvent.h"
24 : #include "../config.h"
25 : #include "../event.h"
26 : #include "../global.h"
27 : #include "../log.h"
28 : #include "../os.h"
29 : #include "../pipe.h"
30 : #include "../window.h"
31 :
32 : #include <lunchbox/lockable.h>
33 : #include <lunchbox/perThread.h>
34 : #include <lunchbox/scopedMutex.h>
35 : #include <lunchbox/spinLock.h>
36 :
37 : #include <X11/keysym.h>
38 : #include <X11/XKBlib.h>
39 :
40 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
41 : # include <spnav.h>
42 : #endif
43 :
44 : namespace eq
45 : {
46 : namespace glx
47 : {
48 : namespace
49 : {
50 : typedef std::vector< EventHandler* > EventHandlers;
51 12 : static lunchbox::PerThread< EventHandlers > _eventHandlers;
52 :
53 : struct MagellanData
54 : {
55 12 : MagellanData() : display( 0 ), used( 0 ) {}
56 :
57 : Display* display;
58 : size_t used;
59 : };
60 :
61 12 : static lunchbox::Lockable< MagellanData, lunchbox::SpinLock > _magellan;
62 : }
63 :
64 15 : EventHandler::EventHandler( WindowIF* window )
65 : : _window( window )
66 15 : , _magellanUsed( false )
67 : {
68 15 : LBASSERT( window );
69 :
70 15 : if( !_eventHandlers )
71 15 : _eventHandlers = new EventHandlers;
72 15 : _eventHandlers->push_back( this );
73 :
74 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
75 15 : Display* display = window->getXDisplay();
76 15 : LBASSERT( display );
77 15 : lunchbox::ScopedFastWrite mutex( _magellan );
78 15 : if( !_magellan->display )
79 : {
80 15 : if( spnav_x11_open( display, window->getXDrawable( )) == -1 )
81 : {
82 45 : LBWARN << "Failed to connect to the space navigator daemon"
83 45 : << std::endl;
84 15 : return;
85 : }
86 0 : _magellan->display = display;
87 : }
88 0 : else if( _magellan->display != display )
89 : {
90 0 : LBINFO << "Multi-display space mouse support incomplete" << std::endl;
91 0 : return;
92 : }
93 0 : else if( spnav_x11_window( window->getXDrawable( )) == -1 )
94 : {
95 0 : LBWARN << "Failed to register window with the space navigator daemon"
96 0 : << std::endl;
97 0 : return;
98 : }
99 :
100 0 : ++_magellan->used;
101 0 : _magellanUsed = true;
102 : #endif
103 : }
104 :
105 45 : EventHandler::~EventHandler()
106 : {
107 15 : if( _magellanUsed )
108 : {
109 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
110 0 : lunchbox::ScopedFastWrite mutex( _magellan );
111 0 : if( --_magellan->used == 0 )
112 : {
113 0 : if( spnav_close() == -1 )
114 : {
115 0 : LBWARN
116 0 : << "Couldn't close connection to the space navigator daemon"
117 0 : << std::endl;
118 : }
119 : }
120 : #endif
121 0 : _magellanUsed = false;
122 : }
123 :
124 15 : EventHandlers::iterator i = lunchbox::find( *_eventHandlers, this );
125 15 : LBASSERT( i != _eventHandlers->end( ));
126 15 : _eventHandlers->erase( i );
127 15 : if( _eventHandlers->empty( ))
128 : {
129 15 : delete _eventHandlers.get();
130 15 : _eventHandlers = 0;
131 : }
132 30 : }
133 :
134 5616 : void EventHandler::dispatch()
135 : {
136 5616 : if( !_eventHandlers )
137 5808 : return;
138 :
139 32543 : for( EventHandlers::const_iterator i = _eventHandlers->begin();
140 21696 : i != _eventHandlers->end(); ++i )
141 : {
142 5424 : (*i)->_dispatch();
143 : }
144 : }
145 :
146 5424 : void EventHandler::_dispatch()
147 : {
148 5424 : Display* display = _window->getXDisplay();
149 5424 : LBASSERT( display );
150 5424 : if( !display )
151 5424 : return;
152 :
153 10858 : while( XPending( display ))
154 : {
155 10 : WindowEvent event;
156 10 : XEvent& xEvent = event.xEvent;
157 :
158 10 : XNextEvent( display, &xEvent );
159 :
160 60 : for( EventHandlers::const_iterator i = _eventHandlers->begin();
161 40 : i != _eventHandlers->end(); ++i )
162 : {
163 10 : EventHandler* handler = *i;
164 10 : handler->_processEvent( event );
165 : }
166 10 : }
167 : }
168 :
169 : namespace
170 : {
171 6 : void _getWindowSize( Display* display, XID drawable, ResizeEvent& event )
172 : {
173 : // Get window coordinates from X11, the event data is relative to window
174 : // parent, but we report pvp relative to root window.
175 : XWindowAttributes windowAttrs;
176 6 : XGetWindowAttributes( display, drawable, &windowAttrs );
177 :
178 : XID child;
179 : XTranslateCoordinates( display, drawable,
180 : RootWindowOfScreen( windowAttrs.screen ),
181 : windowAttrs.x, windowAttrs.y,
182 6 : &event.x, &event.y, &child );
183 :
184 6 : event.w = windowAttrs.width;
185 6 : event.h = windowAttrs.height;
186 6 : }
187 : }
188 :
189 10 : void EventHandler::_processEvent( WindowEvent& event )
190 : {
191 10 : LB_TS_THREAD( _thread );
192 :
193 10 : XEvent& xEvent = event.xEvent;
194 10 : XID drawable = xEvent.xany.window;
195 :
196 10 : if( _window->getXDrawable() != drawable )
197 0 : return;
198 :
199 10 : switch( xEvent.type )
200 : {
201 : case Expose:
202 0 : if( xEvent.xexpose.count ) // Only report last expose event
203 0 : return;
204 :
205 0 : event.type = Event::WINDOW_EXPOSE;
206 0 : break;
207 :
208 : case ConfigureNotify:
209 6 : event.type = Event::WINDOW_RESIZE;
210 6 : _getWindowSize( xEvent.xany.display, drawable, event.resize );
211 6 : break;
212 :
213 : case UnmapNotify:
214 0 : event.type = Event::WINDOW_HIDE;
215 0 : _getWindowSize( xEvent.xany.display, drawable, event.resize );
216 0 : break;
217 :
218 : case MapNotify:
219 0 : event.type = Event::WINDOW_SHOW;
220 0 : _getWindowSize( xEvent.xany.display, drawable, event.resize );
221 0 : break;
222 :
223 : case ClientMessage:
224 : {
225 : #ifdef EQUALIZER_USE_MAGELLAN_GLX
226 : spnav_event spev;
227 :
228 : /* spacenav event */
229 0 : if( spnav_x11_event( &xEvent, &spev ))
230 : {
231 0 : switch( spev.type )
232 : {
233 : case SPNAV_EVENT_MOTION:
234 0 : event.type = Event::MAGELLAN_AXIS;
235 0 : event.magellan.xAxis = spev.motion.x;
236 0 : event.magellan.yAxis = spev.motion.y;
237 0 : event.magellan.zAxis = -spev.motion.z;
238 0 : event.magellan.xRotation = -spev.motion.rx;
239 0 : event.magellan.yRotation = -spev.motion.ry;
240 0 : event.magellan.zRotation = spev.motion.rz;
241 0 : break;
242 :
243 : case SPNAV_EVENT_BUTTON:
244 0 : event.type = Event::MAGELLAN_BUTTON;
245 0 : event.magellan.buttons = spev.button.press;
246 0 : event.magellan.button = spev.button.bnum;
247 0 : break;
248 :
249 : default:
250 0 : LBUNIMPLEMENTED;
251 0 : return;
252 : }
253 :
254 : /* remove any other queued motion events */
255 : //? spnav_remove_events( SPNAV_EVENT_MOTION );
256 0 : break;
257 : }
258 : #endif
259 :
260 : Atom deleteAtom = XInternAtom( xEvent.xany.display,
261 0 : "WM_DELETE_WINDOW", False );
262 :
263 0 : if( static_cast<Atom>( xEvent.xclient.data.l[0] ) != deleteAtom )
264 0 : return; // not a delete message, ignore.
265 : }
266 : // else: delete message, fall through
267 : case DestroyNotify:
268 0 : event.type = Event::WINDOW_CLOSE;
269 0 : break;
270 :
271 : case MotionNotify:
272 0 : event.type = Event::WINDOW_POINTER_MOTION;
273 0 : event.pointerMotion.x = xEvent.xmotion.x;
274 0 : event.pointerMotion.y = xEvent.xmotion.y;
275 0 : event.pointerMotion.buttons = _getButtonState( xEvent );
276 0 : event.pointerMotion.button = PTR_BUTTON_NONE;
277 :
278 0 : _computePointerDelta( event );
279 0 : break;
280 :
281 : case ButtonPress:
282 0 : event.type = Event::WINDOW_POINTER_BUTTON_PRESS;
283 0 : event.pointerButtonPress.x = xEvent.xbutton.x;
284 0 : event.pointerButtonPress.y = xEvent.xbutton.y;
285 0 : event.pointerButtonPress.buttons = _getButtonState( xEvent );
286 0 : event.pointerButtonPress.button = _getButtonAction( xEvent );
287 :
288 : // Translate wheel events
289 0 : switch( event.pointerButtonPress.button )
290 : {
291 0 : case PTR_BUTTON4: event.pointerWheel.xAxis = 1; break;
292 0 : case PTR_BUTTON5: event.pointerWheel.xAxis = -1; break;
293 0 : case PTR_BUTTON6: event.pointerWheel.yAxis = 1; break;
294 0 : case PTR_BUTTON7: event.pointerWheel.yAxis = -1; break;
295 : }
296 0 : switch( event.pointerButtonPress.button )
297 : {
298 : case PTR_BUTTON4:
299 : case PTR_BUTTON5:
300 : case PTR_BUTTON6:
301 : case PTR_BUTTON7:
302 0 : event.type = Event::WINDOW_POINTER_WHEEL;
303 0 : event.pointerWheel.button = PTR_BUTTON_NONE;
304 : }
305 :
306 0 : _computePointerDelta( event );
307 0 : break;
308 :
309 : case ButtonRelease:
310 0 : event.type = Event::WINDOW_POINTER_BUTTON_RELEASE;
311 0 : event.pointerButtonRelease.x = xEvent.xbutton.x;
312 0 : event.pointerButtonRelease.y = xEvent.xbutton.y;
313 0 : event.pointerButtonRelease.buttons = _getButtonState( xEvent );
314 0 : event.pointerButtonRelease.button = _getButtonAction( xEvent);
315 :
316 0 : _computePointerDelta( event );
317 0 : break;
318 :
319 : case KeyPress:
320 0 : event.type = Event::KEY_PRESS;
321 0 : event.keyPress.key = _getKey( xEvent );
322 0 : break;
323 :
324 : case KeyRelease:
325 0 : event.type = Event::KEY_RELEASE;
326 0 : event.keyPress.key = _getKey( xEvent );
327 0 : break;
328 :
329 : case ReparentNotify:
330 : case VisibilityNotify:
331 4 : event.type = Event::UNKNOWN;
332 4 : LBVERB << "Ignored X event, type " << xEvent.type << std::endl;
333 4 : break;
334 :
335 : default:
336 0 : event.type = Event::UNKNOWN;
337 0 : LBWARN << "Unhandled X event, type " << xEvent.type << std::endl;
338 0 : break;
339 : }
340 :
341 10 : _window->processEvent( event );
342 : }
343 :
344 0 : uint32_t EventHandler::_getButtonState( XEvent& event )
345 : {
346 0 : const int xState = event.xbutton.state;
347 0 : uint32_t state = 0;
348 :
349 0 : if( xState & Button1Mask ) state |= PTR_BUTTON1;
350 0 : if( xState & Button2Mask ) state |= PTR_BUTTON2;
351 0 : if( xState & Button3Mask ) state |= PTR_BUTTON3;
352 0 : if( xState & Button4Mask ) state |= PTR_BUTTON4;
353 0 : if( xState & Button5Mask ) state |= PTR_BUTTON5;
354 :
355 0 : switch( event.type )
356 : { // state is state before event
357 : case ButtonPress:
358 0 : state |= _getButtonAction( event );
359 0 : break;
360 :
361 : case ButtonRelease:
362 0 : state &= ~_getButtonAction( event );
363 0 : break;
364 :
365 : default:
366 0 : break;
367 : }
368 0 : return state;
369 : }
370 :
371 0 : uint32_t EventHandler::_getButtonAction( XEvent& event )
372 : {
373 0 : switch( event.xbutton.button )
374 : {
375 0 : case Button1: return PTR_BUTTON1;
376 0 : case Button2: return PTR_BUTTON2;
377 0 : case Button3: return PTR_BUTTON3;
378 0 : case Button4: return PTR_BUTTON4;
379 0 : case Button5: return PTR_BUTTON5;
380 0 : case 6: return PTR_BUTTON6;
381 0 : case 7: return PTR_BUTTON7;
382 0 : default: return PTR_BUTTON_NONE;
383 : }
384 : }
385 :
386 :
387 0 : uint32_t EventHandler::_getKey( XEvent& event )
388 : {
389 0 : int index = 0;
390 0 : if( event.xkey.state & ShiftMask )
391 0 : index = 1;
392 :
393 : const KeySym key = XkbKeycodeToKeysym( event.xany.display,
394 0 : event.xkey.keycode, 0, index );
395 0 : switch( key )
396 : {
397 0 : case XK_Escape: return KC_ESCAPE;
398 0 : case XK_BackSpace: return KC_BACKSPACE;
399 0 : case XK_Return: return KC_RETURN;
400 0 : case XK_Tab: return KC_TAB;
401 0 : case XK_Home: return KC_HOME;
402 0 : case XK_Left: return KC_LEFT;
403 0 : case XK_Up: return KC_UP;
404 0 : case XK_Right: return KC_RIGHT;
405 0 : case XK_Down: return KC_DOWN;
406 0 : case XK_Page_Up: return KC_PAGE_UP;
407 0 : case XK_Page_Down: return KC_PAGE_DOWN;
408 0 : case XK_End: return KC_END;
409 0 : case XK_F1: return KC_F1;
410 0 : case XK_F2: return KC_F2;
411 0 : case XK_F3: return KC_F3;
412 0 : case XK_F4: return KC_F4;
413 0 : case XK_F5: return KC_F5;
414 0 : case XK_F6: return KC_F6;
415 0 : case XK_F7: return KC_F7;
416 0 : case XK_F8: return KC_F8;
417 0 : case XK_F9: return KC_F9;
418 0 : case XK_F10: return KC_F10;
419 0 : case XK_F11: return KC_F11;
420 0 : case XK_F12: return KC_F12;
421 0 : case XK_F13: return KC_F13;
422 0 : case XK_F14: return KC_F14;
423 0 : case XK_F15: return KC_F15;
424 0 : case XK_F16: return KC_F16;
425 0 : case XK_F17: return KC_F17;
426 0 : case XK_F18: return KC_F18;
427 0 : case XK_F19: return KC_F19;
428 0 : case XK_F20: return KC_F20;
429 0 : case XK_Shift_L: return KC_SHIFT_L;
430 0 : case XK_Shift_R: return KC_SHIFT_R;
431 0 : case XK_Control_L: return KC_CONTROL_L;
432 0 : case XK_Control_R: return KC_CONTROL_R;
433 0 : case XK_Alt_L: return KC_ALT_L;
434 0 : case XK_Alt_R: return KC_ALT_R;
435 :
436 : default:
437 0 : if( (key >= XK_space && key <= XK_asciitilde ) ||
438 0 : (key >= XK_nobreakspace && key <= XK_ydiaeresis))
439 : {
440 0 : return key;
441 : }
442 0 : LBWARN << "Unrecognized X11 key code " << key << std::endl;
443 0 : return KC_VOID;
444 : }
445 : }
446 :
447 :
448 : }
449 36 : }
|