LCOV - code coverage report
Current view: top level - eq/client/glx - eventHandler.cpp (source / functions) Hit Total Coverage
Test: lcov2.info Lines: 66 228 28.9 %
Date: 2014-06-18 Functions: 10 13 76.9 %

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

Generated by: LCOV version 1.10