kdecore Library API Documentation

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 #ifndef __osf__
00109 // this crashes under Tru64 so .....
00110     kdDebug(125) << QString( "grabKey( key: '%1', bGrab: %2 ): keyCodeX: %3 keyModX: %4\n" )
00111         .arg( key.key().toStringInternal() ).arg( bGrab )
00112         .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 );
00113 #endif
00114     if( !keyCodeX )
00115         return false;
00116 
00117     // We want to catch only our own errors
00118     g_bGrabFailed = false;
00119     XSync( qt_xdisplay(), 0 );
00120     XErrorHandler savedErrorHandler = XSetErrorHandler( XGrabErrorHandler );
00121 
00122     // We'll have to grab 8 key modifier combinations in order to cover all
00123     //  combinations of CapsLock, NumLock, ScrollLock.
00124     // Does anyone with more X-savvy know how to set a mask on qt_xrootwin so that
00125     //  the irrelevant bits are always ignored and we can just make one XGrabKey
00126     //  call per accelerator? -- ellis
00127 #ifndef NDEBUG
00128     QString sDebug = QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16);
00129 #endif
00130     uint keyModMaskX = ~g_keyModMaskXOnOrOff;
00131     for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) {
00132         if( (irrelevantBitsMask & keyModMaskX) == 0 ) {
00133 #ifndef NDEBUG
00134             sDebug += QString("0x%3, ").arg(irrelevantBitsMask, 0, 16);
00135 #endif
00136             if( bGrab ) {
00137                 if(keyCodeX==115)
00138                     XGrabKey( qt_xdisplay(), keyCodeX, irrelevantBitsMask, qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
00139                 else
00140                     XGrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
00141 
00142                 // If grab failed, then ungrab any previously successful grabs.
00143                 if( g_bGrabFailed ) {
00144                     kdDebug(125) << "grab failed!\n";
00145                     for( uint m = 0; m < irrelevantBitsMask; m++ ) 
00146                     {
00147                         if( m & keyModMaskX == 0 )
00148                         {
00149                             if(keyCodeX==115)
00150                                 XUngrabKey( qt_xdisplay(), keyCodeX, m, qt_xrootwin() );
00151                             else
00152                                 XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | m, qt_xrootwin() );
00153                         }
00154                     }
00155                     break;
00156                 }
00157             } else
00158             {
00159                 if(keyCodeX==115)
00160                     XUngrabKey( qt_xdisplay(), keyCodeX,irrelevantBitsMask, qt_xrootwin() );
00161                 else
00162                     XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin() );
00163             }
00164         }
00165     }
00166 #ifndef NDEBUG
00167     kdDebug(125) << sDebug << endl;
00168 #endif
00169 
00170     XSync( qt_xdisplay(), 0 );
00171     XSetErrorHandler( savedErrorHandler );
00172 
00173     if( !g_bGrabFailed ) {
00174         CodeMod codemod;
00175         codemod.code = keyCodeX;
00176         codemod.mod = keyModX;
00177         if( key.mod() & KKeyServer::MODE_SWITCH )
00178             codemod.mod |= KKeyServer::MODE_SWITCH;
00179         if(keyCodeX==115)
00180             codemod.mod=0;
00181         if( bGrab )
00182             m_rgCodeModToAction.insert( codemod, pAction );
00183         else
00184             m_rgCodeModToAction.remove( codemod );
00185     }
00186     return !g_bGrabFailed;
00187 }
00188 
00189 bool KGlobalAccelPrivate::x11Event( XEvent* pEvent )
00190 {
00191     //kdDebug(125) << "x11EventFilter( type = " << pEvent->type << " )" << endl;
00192     switch( pEvent->type ) {
00193      case MappingNotify:
00194         x11MappingNotify();
00195         return true;
00196      case XKeyPress:
00197         if( x11KeyPress( pEvent ) )
00198             return true;
00199      default:
00200         return QWidget::x11Event( pEvent );
00201     }
00202 }
00203 
00204 void KGlobalAccelPrivate::x11MappingNotify()
00205 {
00206     kdDebug(125) << "KGlobalAccelPrivate::x11MappingNotify()" << endl;
00207     if( m_bEnabled ) {
00208         // Maybe the X modifier map has been changed.
00209         KKeyServer::initializeMods();
00210         calculateGrabMasks();
00211         // Do new XGrabKey()s.
00212         updateConnections();
00213     }
00214 }
00215 
00216 bool KGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent )
00217 {
00218     // do not change this line unless you really really know what you are doing (Matthias)
00219     if ( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget() )
00220         XUngrabKeyboard( qt_xdisplay(), pEvent->xkey.time );
00221 
00222     if( !m_bEnabled )
00223         return false;
00224 
00225     CodeMod codemod;
00226     codemod.code = pEvent->xkey.keycode;
00227     codemod.mod = pEvent->xkey.state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH);
00228     
00229     // If numlock is active and a keypad key is pressed, XOR the SHIFT state.
00230     //  e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left.
00231     if( pEvent->xkey.state & KKeyServer::modXNumLock() ) {
00232         // TODO: what's the xor operator in c++?
00233         uint sym = XKeycodeToKeysym( qt_xdisplay(), codemod.code, 0 );
00234         if( sym >= XK_KP_Space && sym <= XK_KP_9 ) {
00235             if( codemod.mod & KKeyServer::modXShift() )
00236                 codemod.mod &= ~KKeyServer::modXShift();
00237             else
00238                 codemod.mod |= KKeyServer::modXShift();
00239         }
00240     }
00241 
00242     KKeyNative keyNative( pEvent );
00243     KKey key = keyNative;
00244     kdDebug(125) << "x11KeyPress: seek " << key.toStringInternal()
00245         << QString( " keyCodeX: %1 state: %2 keyModX: %3" )
00246             .arg( codemod.code, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ).arg( codemod.mod, 0, 16 ) << endl;
00247 
00248     // Search for which accelerator activated this event:
00249     if(codemod.code==115 /* left win */)
00250     {
00251         codemod.mod=0;
00252         key.init("Win");
00253     }
00254     
00255     if(!m_rgCodeModToAction.contains( codemod ) ) {
00256 #ifndef NDEBUG
00257         for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
00258             KAccelAction* pAction = *it;
00259             kdDebug(125) << "\tcode: " << QString::number(it.key().code, 16) << " mod: " << QString::number(it.key().mod, 16)
00260                 << (pAction ? QString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : QString::null)
00261                 << endl;
00262         }
00263 #endif
00264         return false;
00265     }
00266     KAccelAction* pAction = m_rgCodeModToAction[codemod];
00267 
00268     if( !pAction ) {
00269         QPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) );
00270         connect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)) );
00271         pMenu->exec( QPoint( 0, 0 ) );
00272         disconnect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
00273         delete pMenu;
00274     } else if( !pAction->objSlotPtr() || !pAction->isEnabled() )
00275         return false;
00276     else
00277         activate( pAction, KKeySequence(key) );
00278 
00279     return true;
00280 }
00281 
00282 void KGlobalAccelPrivate::activate( KAccelAction* pAction, const KKeySequence& seq )
00283 {
00284     kdDebug(125) << "KGlobalAccelPrivate::activate( \"" << pAction->name() << "\" ) " << endl;
00285 
00286     QRegExp rexPassIndex( "([ ]*int[ ]*)" );
00287     QRegExp rexPassInfo( " QString" );
00288     QRegExp rexIndex( " ([0-9]+)$" );
00289 
00290     // If the slot to be called accepts an integer index
00291     //  and an index is present at the end of the action's name,
00292     //  then send the slot the given index #.
00293     if( rexPassIndex.search( pAction->methodSlotPtr() ) >= 0 && rexIndex.search( pAction->name() ) >= 0 ) {
00294         int n = rexIndex.cap(1).toInt();
00295         kdDebug(125) << "Calling " << pAction->methodSlotPtr() << " int = " << n << endl;
00296         connect( this, SIGNAL(activated(int)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00297         emit activated( n );
00298         disconnect( this, SIGNAL(activated(int)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00299     } else if( rexPassInfo.search( pAction->methodSlotPtr() ) ) {
00300         connect( this, SIGNAL(activated(const QString&, const QString&, const KKeySequence&)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00301         emit activated( pAction->name(), pAction->label(), seq );
00302         disconnect( this, SIGNAL(activated(const QString&, const QString&, const KKeySequence&)), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00303     } else {
00304         connect( this, SIGNAL(activated()), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00305         emit activated();
00306         disconnect( this, SIGNAL(activated()), pAction->objSlotPtr(), pAction->methodSlotPtr() );
00307     }
00308 }
00309 
00310 void KGlobalAccelPrivate::slotActivated( int iAction )
00311 {
00312     KAccelAction* pAction = actions().actionPtr( iAction );
00313     if( pAction )
00314         activate( pAction, KKeySequence() );
00315 }
00316 
00317 #include "kglobalaccel_x11.moc"
00318 
00319 #endif // !Q_WS_X11
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.0.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Mon Oct 24 13:08:17 2011 by doxygen 1.3.5 written by Dimitri van Heesch, © 1997-2001