kglobalaccel_x11.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# kglobalaccel_x11.cpp

    00001 #include <qwindowdefs.h>
    00002 #ifdef Q_WS_X11
    00003
    00004 #include "kglobalaccel_x11.h"
    00005 #include "kglobalaccel.h"
    00006 #include "kkeyserver_x11.h"
    00007
    00008 #include <qpopupmenu.h>
    00009 #include <qregexp.h>
    00010 #include <qwidget.h>
    00011 #include <kapplication.h>
    00012 #include <kdebug.h>
    00013 #include <kkeynative.h>
    00014
    00015 #include <X11/X.h>
    00016 #include <X11/Xlib.h>
    00017 #include <X11/keysym.h>
    00018
    00019 #ifdef KeyPress
    00020 // defined by X11 headers
    00021 const int XKeyPress = KeyPress;
    00022 const int XKeyRelease = KeyRelease;
    00023 #undef KeyPress
    00024 #endif
    00025
    00026 static bool g_bGrabFailed;
    00027
    00028 extern "C" {
    00029   static int XGrabErrorHandler( Display *, XErrorEvent *e ) {
    00030         if ( e->error_code != BadAccess ) {
    00031             kdWarning() << "grabKey: got X error " << e->type << " instead of BadAccess\n";
    00032         }
    00033         g_bGrabFailed = true;
    00034         return 0;
    00035   }
    00036 }
    00037
    00038 // g_keyModMaskXAccel
    00039 //      mask of modifiers which can be used in shortcuts
    00040 //      (meta, alt, ctrl, shift)
    00041 // g_keyModMaskXOnOrOff
    00042 //      mask of modifiers where we don't care whether they are on or off
    00043 //      (caps lock, num lock, scroll lock)
    00044 static uint g_keyModMaskXAccel = 0;
    00045 static uint g_keyModMaskXOnOrOff = 0;
    00046
    00047 static void calculateGrabMasks()
    00048 {
    00049         g_keyModMaskXAccel = KKeyServer::accelModMaskX();
    00050         g_keyModMaskXOnOrOff =
    00051                         KKeyServer::modXLock() |
    00052                         KKeyServer::modXNumLock() |
    00053                         KKeyServer::modXScrollLock();
    00054         //kdDebug() << "g_keyModMaskXAccel = " << g_keyModMaskXAccel
    00055         //      << "g_keyModMaskXOnOrOff = " << g_keyModMaskXOnOrOff << endl;
    00056 }
    00057
    00058 //----------------------------------------------------
    00059
    00060 KGlobalAccelPrivate::KGlobalAccelPrivate()
    00061 : KAccelBase( KAccelBase::NATIVE_KEYS )
    00062 {
    00063         m_sConfigGroup = "Global Shortcuts";
    00064         kapp->installX11EventFilter( this );
    00065 }
    00066
    00067 KGlobalAccelPrivate::~KGlobalAccelPrivate()
    00068 {
    00069         // TODO: Need to release all grabbed keys if the main window is not shutting down.
    00070         //for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
    00071         //      const CodeMod& codemod = it.key();
    00072         //}
    00073 }
    00074
    00075 void KGlobalAccelPrivate::setEnabled( bool bEnable )
    00076 {
    00077         m_bEnabled = bEnable;
    00078         //updateConnections();
    00079 }
    00080
    00081 bool KGlobalAccelPrivate::emitSignal( Signal )
    00082 {
    00083         return false;
    00084 }
    00085
    00086 bool KGlobalAccelPrivate::connectKey( KAccelAction& action, const KKeyServer::Key& key )
    00087         { return grabKey( key, true, &action ); }
    00088 bool KGlobalAccelPrivate::connectKey( const KKeyServer::Key& key )
    00089         { return grabKey( key, true, 0 ); }
    00090 bool KGlobalAccelPrivate::disconnectKey( KAccelAction& action, const KKeyServer::Key& key )
    00091         { return grabKey( key, false, &action ); }
    00092 bool KGlobalAccelPrivate::disconnectKey( const KKeyServer::Key& key )
    00093         { return grabKey( key, false, 0 ); }
    00094
    00095 bool KGlobalAccelPrivate::grabKey( const KKeyServer::Key& key, bool bGrab, KAccelAction* pAction )
    00096 {
    00097         if( !key.code() ) {
    00098                 kdWarning(125) << "KGlobalAccelPrivate::grabKey( " << key.key().toStringInternal() << ", " << bGrab << ", \"" << (pAction ? pAction->name().latin1() : "(null)") << "\" ): Tried to grab key with null code." << endl;
    00099                 return false;
    00100         }
    00101
    00102         // Make sure that grab masks have been initialized.
    00103         if( g_keyModMaskXOnOrOff == 0 )
    00104                 calculateGrabMasks();
    00105
    00106         uchar keyCodeX = key.code();
    00107         uint keyModX = key.mod() & g_keyModMaskXAccel; // Get rid of any non-relevant bits in mod
    00108
    00109 #ifndef __osf__
    00110 // this crashes under Tru64 so .....
    00111         kdDebug(125) << QString( "grabKey( key: '%1', bGrab: %2 ): keyCodeX: %3 keyModX: %4\n" )
    00112                 .arg( key.key().toStringInternal() ).arg( bGrab )
    00113                 .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 );
    00114 #endif
    00115         if( !keyCodeX )
    00116                 return false;
    00117
    00118         // We want to catch only our own errors
    00119         g_bGrabFailed = false;
    00120         XSync( qt_xdisplay(), 0 );
    00121         XErrorHandler savedErrorHandler = XSetErrorHandler( XGrabErrorHandler );
    00122
    00123         // We'll have to grab 8 key modifier combinations in order to cover all
    00124         //  combinations of CapsLock, NumLock, ScrollLock.
    00125         // Does anyone with more X-savvy know how to set a mask on qt_xrootwin so that
    00126         //  the irrelevant bits are always ignored and we can just make one XGrabKey
    00127         //  call per accelerator? -- ellis
    00128 #ifndef NDEBUG
    00129         QString sDebug = QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16);
    00130 #endif
    00131         uint keyModMaskX = ~g_keyModMaskXOnOrOff;
    00132         for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) {
    00133                 if( (irrelevantBitsMask & keyModMaskX) == 0 ) {
    00134 #ifndef NDEBUG
    00135                         sDebug += QString("0x%3, ").arg(irrelevantBitsMask, 0, 16);
    00136 #endif
    00137                         if( bGrab ) {
    00138                                 XGrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask,
    00139                                         qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
    00140
    00141                                 // If grab failed, then ungrab any previously successful grabs.
    00142                                 if( g_bGrabFailed ) {
    00143                                         kdDebug(125) << "grab failed!\n";
    00144                                         for( uint m = 0; m < irrelevantBitsMask; m++ ) {
    00145                                                 if( m & keyModMaskX == 0 )
    00146                                                         XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | m, qt_xrootwin() );
    00147                                         }
    00148                                         break;
    00149                                 }
    00150                         } else
    00151                                 XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin() );
    00152                 }
    00153         }
    00154 #ifndef NDEBUG
    00155         kdDebug(125) << sDebug << endl;
    00156 #endif
    00157
    00158         XSync( qt_xdisplay(), 0 );
    00159         XSetErrorHandler( savedErrorHandler );
    00160
    00161         if( !g_bGrabFailed ) {
    00162                 CodeMod codemod;
    00163                 codemod.code = keyCodeX;
    00164                 codemod.mod = keyModX;
    00165                 if( key.mod() & KKeyServer::MODE_SWITCH )
    00166                         codemod.mod |= KKeyServer::MODE_SWITCH;
    00167
    00168                 if( bGrab )
    00169                         m_rgCodeModToAction.insert( codemod, pAction );
    00170                 else
    00171                         m_rgCodeModToAction.remove( codemod );
    00172         }
    00173         return !g_bGrabFailed;
    00174 }
    00175
    00176 bool KGlobalAccelPrivate::x11Event( XEvent* pEvent )
    00177 {
    00178         //kdDebug(125) << "x11EventFilter( type = " << pEvent->type << " )" << endl;
    00179         switch( pEvent->type ) {
    00180          case MappingNotify:
    00181                 x11MappingNotify();
    00182                 return true;
    00183          case XKeyPress:
    00184                 if( x11KeyPress( pEvent ) )
    00185                         return true;
    00186          default:
    00187                 return QWidget::x11Event( pEvent );
    00188         }
    00189 }
    00190
    00191 void KGlobalAccelPrivate::x11MappingNotify()
    00192 {
    00193         kdDebug(125) << "KGlobalAccelPrivate::x11MappingNotify()" << endl;
    00194         if( m_bEnabled ) {
    00195                 // Maybe the X modifier map has been changed.
    00196                 KKeyServer::initializeMods();
    00197                 calculateGrabMasks();
    00198                 // Do new XGrabKey()s.
    00199                 updateConnections();
    00200         }
    00201 }
    00202
    00203 bool KGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent )
    00204 {
    00205         // do not change this line unless you really really know what you are doing (Matthias)
    00206         if ( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget() )
    00207                 XUngrabKeyboard( qt_xdisplay(), pEvent->xkey.time );
    00208
    00209         if( !m_bEnabled )
    00210                 return false;
    00211
    00212         CodeMod codemod;
    00213         codemod.code = pEvent->xkey.keycode;
    00214         codemod.mod = pEvent->xkey.state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH);
    00215
    00216         // If numlock is active and a keypad key is pressed, XOR the SHIFT state.
    00217         //  e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left.
    00218         if( pEvent->xkey.state & KKeyServer::modXNumLock() ) {
    00219                 // TODO: what's the xor operator in c++?
    00220                 uint sym = XKeycodeToKeysym( qt_xdisplay(), codemod.code, 0 );
    00221                 // If this is a keypad key,
    00222                 if( sym >= XK_KP_Space && sym <= XK_KP_9 ) {
    00223                         switch( sym ) {
    00224                                 // Leave the following keys unaltered
    00225                                 // FIXME: The proper solution is to see which keysyms don't change when shifted.
    00226                                 case XK_KP_Multiply:
    00227                                 case XK_KP_Add:
    00228                                 case XK_KP_Subtract:
    00229                                 case XK_KP_Divide:
    00230                                         break;
    00231                                 default:
    00232                                         if( codemod.mod & KKeyServer::modXShift() )
    00233                                                 codemod.mod &= ~KKeyServer::modXShift();
    00234                                         else
    00235                                                 codemod.mod |= KKeyServer::modXShift();
    00236                         }
    00237                 }
    00238         }
    00239
    00240         KKeyNative keyNative( pEvent );
    00241         KKey key = keyNative;
    00242
    00243         kdDebug(125) << "x11KeyPress: seek " << key.toStringInternal()
    00244                 << QString( " keyCodeX: %1 state: %2 keyModX: %3" )
    00245                         .arg( codemod.code, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ).arg( codemod.mod, 0, 16 ) << endl;
    00246
    00247         // Search for which accelerator activated this event:
    00248         if( !m_rgCodeModToAction.contains( codemod ) ) {
    00249 #ifndef NDEBUG
    00250                 for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
    00251                         KAccelAction* pAction = *it;
    00252                         kdDebug(125) << "\tcode: " << QString::number(it.key().code, 16) << " mod: " << QString::number(it.key().mod, 16)
    00253                                 << (pAction ? QString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : QString::null)
    00254                                 << endl;
    00255                 }
    00256 #endif
    00257                 return false;
    00258         }
    00259         KAccelAction* pAction = m_rgCodeModToAction[codemod];
    00260
    00261         if( !pAction ) {
    00262                 QPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) );
    00263                 connect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)) );
    00264                 pMenu->exec( QPoint( 0, 0 ) );
    00265                 disconnect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
    00266                 delete pMenu;
    00267         } else if( !pAction->objSlotPtr() || !pAction->isEnabled() )
    00268                 return false;
    00269         else
    00270                 activate( pAction, KKeySequence(key) );
    00271
    00272         return true;
    00273 }
    00274
    00275 void KGlobalAccelPrivate::activate( KAccelAction* pAction, const KKeySequence& seq )
    00276 {
    00277         kdDebug(125) << "KGlobalAccelPrivate::activate( \"" << pAction->name() << "\" ) " << endl;
    00278
    00279         QRegExp rexPassIndex( "([ ]*int[ ]*)" );
    00280         QRegExp rexPassInfo( " QString" );
    00281         QRegExp rexIndex( " ([0-9]+)$" );
    00282
    00283         // If the slot to be called accepts an integer index
    00284         //  and an index is present at the end of the action's name,
    00285         //  then send the slot the given index #.
    00286         if( rexPassIndex.search( pAction->methodSlotPtr() ) >= 0 && rexIndex.search( pAction->name() ) >= 0 ) {
    00287                 int n = rexIndex.cap(1).toInt();
    00288                 kdDebug(125) << "Calling " << pAction->methodSlotPtr() << " int = " << n << endl;
    00289                 connect( this, SIGNAL(activated(int)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00290                 emit activated( n );
    00291                 disconnect( this, SIGNAL(activated(int)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00292         } else if( rexPassInfo.search( pAction->methodSlotPtr() ) ) {
    00293                 connect( this, SIGNAL(activated(const QString&, const QString&, const KKeySequence&)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00294                 emit activated( pAction->name(), pAction->label(), seq );
    00295                 disconnect( this, SIGNAL(activated(const QString&, const QString&, const KKeySequence&)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00296         } else {
    00297                 connect( this, SIGNAL(activated()), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00298                 emit activated();
    00299                 disconnect( this, SIGNAL(activated()), pAction->objSlotPtr(), pAction->methodSlotPtr() );
    00300         }
    00301 }
    00302
    00303 void KGlobalAccelPrivate::slotActivated( int iAction )
    00304 {
    00305         KAccelAction* pAction = actions().actionPtr( iAction );
    00306         if( pAction )
    00307                 activate( pAction, KKeySequence() );
    00308 }
    00309
    00310 #include "kglobalaccel_x11.moc"
    00311
    00312 #endif // !Q_WS_X11

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:31 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001