kdecore Library API Documentation

kglobalaccel_x11.cpp

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