LCOV - code coverage report
Current view: top level - eq/glx - eventHandler.cpp (source / functions) Hit Total Coverage
Test: Equalizer Lines: 51 246 20.7 %
Date: 2017-12-16 05:07:20 Functions: 9 14 64.3 %

          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 : }

Generated by: LCOV version 1.11