kdecore Library API Documentation

kglobalaccel_x11.cpp

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