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.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 17 11:26:07 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003