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., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "config.h" 00021 00022 #include <tqwindowdefs.h> 00023 #ifdef Q_WS_X11 00024 00025 #include "kglobalaccel_x11.h" 00026 #include "kglobalaccel.h" 00027 #include "kkeyserver_x11.h" 00028 00029 #include <tqpopupmenu.h> 00030 #include <tqregexp.h> 00031 #include <tqwidget.h> 00032 #include <tqmetaobject.h> 00033 #include <tqucomextra_p.h> 00034 #include <tdeapplication.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/XKBlib.h> 00045 #include <X11/keysym.h> 00046 #include <fixx11h.h> 00047 00048 extern "C" { 00049 static int XGrabErrorHandler( Display *, XErrorEvent *e ) { 00050 if ( e->error_code != BadAccess ) { 00051 kdWarning() << "grabKey: got X error " << e->type << " instead of BadAccess\n"; 00052 } 00053 return 1; 00054 } 00055 } 00056 00057 // g_keyModMaskXAccel 00058 // mask of modifiers which can be used in shortcuts 00059 // (meta, alt, ctrl, shift) 00060 // g_keyModMaskXOnOrOff 00061 // mask of modifiers where we don't care whether they are on or off 00062 // (caps lock, num lock, scroll lock) 00063 static uint g_keyModMaskXAccel = 0; 00064 static uint g_keyModMaskXOnOrOff = 0; 00065 00066 static void calculateGrabMasks() 00067 { 00068 g_keyModMaskXAccel = KKeyServer::accelModMaskX(); 00069 g_keyModMaskXOnOrOff = 00070 KKeyServer::modXLock() | 00071 KKeyServer::modXNumLock() | 00072 KKeyServer::modXScrollLock() | 00073 KKeyServer::modXModeSwitch(); 00074 //kdDebug() << "g_keyModMaskXAccel = " << g_keyModMaskXAccel 00075 // << "g_keyModMaskXOnOrOff = " << g_keyModMaskXOnOrOff << endl; 00076 } 00077 00078 //---------------------------------------------------- 00079 00080 static TQValueList< TDEGlobalAccelPrivate* >* all_accels = 0; 00081 00082 TDEGlobalAccelPrivate::TDEGlobalAccelPrivate() 00083 : TDEAccelBase( TDEAccelBase::NATIVE_KEYS ) 00084 , m_blocked( false ) 00085 , m_blockingDisabled( false ) 00086 , m_suspended( false ) 00087 { 00088 if( all_accels == NULL ) 00089 all_accels = new TQValueList< TDEGlobalAccelPrivate* >; 00090 all_accels->append( this ); 00091 m_sConfigGroup = "Global Shortcuts"; 00092 kapp->installX11EventFilter( this ); 00093 connect(kapp, TQT_SIGNAL(coreFakeKeyPress(unsigned int)), this, TQT_SLOT(fakeKeyPressed(unsigned int))); 00094 } 00095 00096 TDEGlobalAccelPrivate::~TDEGlobalAccelPrivate() 00097 { 00098 // TODO: Need to release all grabbed keys if the main window is not shutting down. 00099 //for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) { 00100 // const CodeMod& codemod = it.key(); 00101 //} 00102 all_accels->remove( this ); 00103 if( all_accels->count() == 0 ) { 00104 delete all_accels; 00105 all_accels = NULL; 00106 } 00107 } 00108 00109 void TDEGlobalAccelPrivate::setEnabled( bool bEnable ) 00110 { 00111 m_bEnabled = bEnable; 00112 updateConnections(); 00113 } 00114 00115 void TDEGlobalAccelPrivate::blockShortcuts( bool block ) 00116 { 00117 if( all_accels == NULL ) 00118 return; 00119 for( TQValueList< TDEGlobalAccelPrivate* >::ConstIterator it = all_accels->begin(); 00120 it != all_accels->end(); 00121 ++it ) { 00122 if( (*it)->m_blockingDisabled ) 00123 continue; 00124 (*it)->m_blocked = block; 00125 (*it)->updateConnections(); 00126 } 00127 } 00128 00129 void TDEGlobalAccelPrivate::disableBlocking( bool block ) 00130 { 00131 m_blockingDisabled = block; 00132 } 00133 00134 bool TDEGlobalAccelPrivate::isEnabledInternal() const 00135 { 00136 return TDEAccelBase::isEnabled() && !m_blocked; 00137 } 00138 00139 // see #117169 - the bug is hard to reproduce, probably somewhere in X, testcase would be probably 00140 // difficult to make, and so on - just don't release the grabs and only ignore the events instead 00141 void TDEGlobalAccelPrivate::suspend( bool s ) 00142 { 00143 m_suspended = s; 00144 } 00145 00146 bool TDEGlobalAccelPrivate::emitSignal( Signal ) 00147 { 00148 return false; 00149 } 00150 00151 bool TDEGlobalAccelPrivate::connectKey( TDEAccelAction& action, const KKeyServer::Key& key ) 00152 { return grabKey( key, true, &action ); } 00153 bool TDEGlobalAccelPrivate::connectKey( const KKeyServer::Key& key ) 00154 { return grabKey( key, true, 0 ); } 00155 bool TDEGlobalAccelPrivate::disconnectKey( TDEAccelAction& action, const KKeyServer::Key& key ) 00156 { return grabKey( key, false, &action ); } 00157 bool TDEGlobalAccelPrivate::disconnectKey( const KKeyServer::Key& key ) 00158 { return grabKey( key, false, 0 ); } 00159 00160 bool TDEGlobalAccelPrivate::grabKey( const KKeyServer::Key& key, bool bGrab, TDEAccelAction* pAction ) 00161 { 00162 if( !key.code() ) { 00163 kdWarning(125) << "TDEGlobalAccelPrivate::grabKey( " << key.key().toStringInternal() << ", " << bGrab << ", \"" << (pAction ? pAction->name().latin1() : "(null)") << "\" ): Tried to grab key with null code." << endl; 00164 return false; 00165 } 00166 00167 // Make sure that grab masks have been initialized. 00168 if( g_keyModMaskXOnOrOff == 0 ) { 00169 calculateGrabMasks(); 00170 } 00171 00172 uchar keyCodeX = key.code(); 00173 uint keyModX = key.mod() & g_keyModMaskXAccel; // Get rid of any non-relevant bits in mod 00174 // HACK: make Alt+Print work 00175 // only do this for the Xorg default keyboard keycodes, 00176 // other mappings (e.g. evdev) don't need or want it 00177 if( key.sym() == XK_Sys_Req && XkbKeycodeToKeysym( tqt_xdisplay(), 111, 0, 0 ) == XK_Print ) { 00178 keyModX |= KKeyServer::modXAlt(); 00179 keyCodeX = 111; 00180 } 00181 // If the MODE_SWITCH modifier was set in the original key, and was truncated in g_keyModMaskXAccel, XGrabKey will grab the wrong key 00182 // See Bug 1676 00183 if ((key.mod() & KKeyServer::MODE_SWITCH) && (!(g_keyModMaskXAccel & KKeyServer::MODE_SWITCH))) { 00184 // FIXME 00185 // Is there any way to make AltGr-based character sequences work with XGrabKey? 00186 kdWarning(125) << "TDEGlobalAccelPrivate::grabKey( " << key.key().toStringInternal() << ", " << bGrab << ", \"" << (pAction ? pAction->name().latin1() : "(null)") << "\" ): Tried to grab key requiring ISO_Level3_Shift (AltGr) sequence." << endl; 00187 return false; 00188 } 00189 00190 #ifndef __osf__ 00191 // this crashes under Tru64 so ..... 00192 kdDebug(125) << TQString(TQString( "grabKey( key: '%1', bGrab: %2 ): keyCodeX: %3 keyModX: %4\n" ) 00193 .arg( key.key().toStringInternal() ).arg( bGrab ) 00194 .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 )); 00195 #endif 00196 if( !keyCodeX ) { 00197 return false; 00198 } 00199 00200 #ifdef Q_WS_X11 00201 KXErrorHandler handler( XGrabErrorHandler ); 00202 #endif 00203 // We'll have to grab 8 key modifier combinations in order to cover all 00204 // combinations of CapsLock, NumLock, ScrollLock. 00205 // Does anyone with more X-savvy know how to set a mask on tqt_xrootwin so that 00206 // the irrelevant bits are always ignored and we can just make one XGrabKey 00207 // call per accelerator? -- ellis 00208 #ifndef NDEBUG 00209 TQString sDebug = TQString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16); 00210 #endif 00211 uint keyModMaskX = ~g_keyModMaskXOnOrOff; 00212 for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) { 00213 if( (irrelevantBitsMask & keyModMaskX) == 0 ) { 00214 #ifndef NDEBUG 00215 sDebug += TQString("0x%3, ").arg(irrelevantBitsMask, 0, 16); 00216 #endif 00217 if( bGrab ) 00218 XGrabKey( tqt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, 00219 tqt_xrootwin(), True, GrabModeAsync, GrabModeSync ); 00220 else 00221 XUngrabKey( tqt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, tqt_xrootwin() ); 00222 } 00223 } 00224 #ifndef NDEBUG 00225 kdDebug(125) << sDebug << endl; 00226 #endif 00227 00228 bool failed = false; 00229 if( bGrab ) { 00230 #ifdef Q_WS_X11 00231 failed = handler.error( true ); // sync now 00232 #endif 00233 // If grab failed, then ungrab any grabs that could possibly succeed 00234 if( failed ) { 00235 kdDebug(125) << "grab failed!\n"; 00236 for( uint m = 0; m <= 0xff; m++ ) { 00237 if(( m & keyModMaskX ) == 0 ) 00238 XUngrabKey( tqt_xdisplay(), keyCodeX, keyModX | m, tqt_xrootwin() ); 00239 } 00240 } 00241 } 00242 if( !failed ) 00243 { 00244 CodeMod codemod; 00245 codemod.code = keyCodeX; 00246 codemod.mod = keyModX; 00247 if( key.mod() & KKeyServer::MODE_SWITCH ) 00248 codemod.mod |= KKeyServer::MODE_SWITCH; 00249 00250 if( bGrab ) 00251 m_rgCodeModToAction.insert( codemod, pAction ); 00252 else 00253 m_rgCodeModToAction.remove( codemod ); 00254 } 00255 return !failed; 00256 } 00257 00258 bool TDEGlobalAccelPrivate::x11Event( XEvent* pEvent ) 00259 { 00260 //kdDebug(125) << "x11EventFilter( type = " << pEvent->type << " )" << endl; 00261 switch( pEvent->type ) { 00262 case MappingNotify: 00263 XRefreshKeyboardMapping( &pEvent->xmapping ); 00264 x11MappingNotify(); 00265 return false; 00266 case XKeyPress: 00267 if( x11KeyPress( pEvent ) ) { 00268 return true; 00269 } 00270 default: 00271 return TQWidget::x11Event( pEvent ); 00272 } 00273 } 00274 00275 void TDEGlobalAccelPrivate::x11MappingNotify() 00276 { 00277 kdDebug(125) << "TDEGlobalAccelPrivate::x11MappingNotify()" << endl; 00278 // Maybe the X modifier map has been changed. 00279 KKeyServer::initializeMods(); 00280 calculateGrabMasks(); 00281 // Do new XGrabKey()s. 00282 updateConnections(); 00283 } 00284 00285 void TDEGlobalAccelPrivate::fakeKeyPressed(unsigned int keyCode) { 00286 CodeMod codemod; 00287 codemod.code = keyCode; 00288 codemod.mod = 0; 00289 00290 KKey key(keyCode, 0); 00291 00292 kdDebug(125) << "fakeKeyPressed: seek " << key.toStringInternal() 00293 << TQString(TQString( " keyCodeX: %1 keyCode: %2 keyModX: %3" ) 00294 .arg( codemod.code, 0, 16 ).arg( keyCode, 0, 16 ).arg( codemod.mod, 0, 16 )) << endl; 00295 00296 // Search for which accelerator activated this event: 00297 if( !m_rgCodeModToAction.contains( codemod ) ) { 00298 #ifndef NDEBUG 00299 for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) { 00300 TDEAccelAction* pAction = *it; 00301 kdDebug(125) << "\tcode: " << TQString::number(it.key().code, 16) << " mod: " << TQString::number(it.key().mod, 16) 00302 << (pAction ? TQString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : TQString()) 00303 << endl; 00304 } 00305 #endif 00306 return; 00307 } 00308 00309 TDEAccelAction* pAction = m_rgCodeModToAction[codemod]; 00310 00311 if( !pAction ) { 00312 static bool recursion_block = false; 00313 if( !recursion_block ) { 00314 recursion_block = true; 00315 TQPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) ); 00316 connect( pMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated(int)) ); 00317 pMenu->exec( TQPoint( 0, 0 ) ); 00318 disconnect( pMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated(int))); 00319 delete pMenu; 00320 recursion_block = false; 00321 } 00322 } else if( !pAction->objSlotPtr() || !pAction->isEnabled() ) 00323 return; 00324 else 00325 activate( pAction, KKeySequence(key) ); 00326 } 00327 00328 bool TDEGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent ) 00329 { 00330 // do not change this line unless you really really know what you are doing (Matthias) 00331 if ( !TQWidget::keyboardGrabber() && !TQApplication::activePopupWidget() ) { 00332 XUngrabKeyboard( tqt_xdisplay(), pEvent->xkey.time ); 00333 XFlush( tqt_xdisplay()); // avoid X(?) bug 00334 } 00335 00336 if( !isEnabledInternal() || m_suspended ) { 00337 return false; 00338 } 00339 00340 CodeMod codemod; 00341 codemod.code = pEvent->xkey.keycode; 00342 codemod.mod = pEvent->xkey.state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH); 00343 00344 // If numlock is active and a keypad key is pressed, XOR the SHIFT state. 00345 // e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left. 00346 if( pEvent->xkey.state & KKeyServer::modXNumLock() ) { 00347 // TODO: what's the xor operator in c++? 00348 uint sym = XkbKeycodeToKeysym( tqt_xdisplay(), codemod.code, 0, 0 ); 00349 // If this is a keypad key, 00350 if( sym >= XK_KP_Space && sym <= XK_KP_9 ) { 00351 switch( sym ) { 00352 // Leave the following keys unaltered 00353 // FIXME: The proper solution is to see which keysyms don't change when shifted. 00354 case XK_KP_Multiply: 00355 case XK_KP_Add: 00356 case XK_KP_Subtract: 00357 case XK_KP_Divide: 00358 break; 00359 default: 00360 if( codemod.mod & KKeyServer::modXShift() ) 00361 codemod.mod &= ~KKeyServer::modXShift(); 00362 else 00363 codemod.mod |= KKeyServer::modXShift(); 00364 } 00365 } 00366 } 00367 00368 KKeyNative keyNative( pEvent ); 00369 KKey key = keyNative; 00370 00371 kdDebug(125) << "x11KeyPress: seek " << key.toStringInternal() 00372 << TQString(TQString( " keyCodeX: %1 state: %2 keyModX: %3" ) 00373 .arg( codemod.code, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ).arg( codemod.mod, 0, 16 )) << endl; 00374 00375 // Search for which accelerator activated this event: 00376 if( !m_rgCodeModToAction.contains( codemod ) ) { 00377 #ifndef NDEBUG 00378 for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) { 00379 TDEAccelAction* pAction = *it; 00380 kdDebug(125) << "\tcode: " << TQString::number(it.key().code, 16) << " mod: " << TQString::number(it.key().mod, 16) 00381 << (pAction ? TQString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : TQString()) 00382 << endl; 00383 } 00384 #endif 00385 return false; 00386 } 00387 00388 TDEAccelAction* pAction = m_rgCodeModToAction[codemod]; 00389 00390 if( !pAction ) { 00391 static bool recursion_block = false; 00392 if( !recursion_block ) { 00393 recursion_block = true; 00394 TQPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) ); 00395 connect( pMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated(int)) ); 00396 pMenu->exec( TQPoint( 0, 0 ) ); 00397 disconnect( pMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated(int))); 00398 delete pMenu; 00399 recursion_block = false; 00400 } 00401 } else if( !pAction->objSlotPtr() || !pAction->isEnabled() ) 00402 return false; 00403 else 00404 activate( pAction, KKeySequence(key) ); 00405 00406 return true; 00407 } 00408 00409 void TDEGlobalAccelPrivate::activate( TDEAccelAction* pAction, const KKeySequence& seq ) 00410 { 00411 kdDebug(125) << "TDEGlobalAccelPrivate::activate( \"" << pAction->name() << "\" ) " << endl; 00412 00413 TQRegExp rexPassIndex( "([ ]*int[ ]*)" ); 00414 TQRegExp rexPassInfo( " TQString" ); 00415 TQRegExp rexIndex( " ([0-9]+)$" ); 00416 00417 // If the slot to be called accepts an integer index 00418 // and an index is present at the end of the action's name, 00419 // then send the slot the given index #. 00420 if( rexPassIndex.search( pAction->methodSlotPtr() ) >= 0 && rexIndex.search( pAction->name() ) >= 0 ) { 00421 int n = rexIndex.cap(1).toInt(); 00422 kdDebug(125) << "Calling " << pAction->methodSlotPtr() << " int = " << n << endl; 00423 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true ); 00424 if( slot_id >= 0 ) { 00425 TQUObject o[2]; 00426 static_TQUType_int.set(o+1,n); 00427 const_cast< TQObject* >( pAction->objSlotPtr())->tqt_invoke( slot_id, o ); 00428 } 00429 } else if( rexPassInfo.search( pAction->methodSlotPtr() ) ) { 00430 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true ); 00431 if( slot_id >= 0 ) { 00432 TQUObject o[4]; 00433 static_TQUType_TQString.set(o+1,pAction->name()); 00434 static_TQUType_TQString.set(o+2,pAction->label()); 00435 static_TQUType_ptr.set(o+3,&seq); 00436 const_cast< TQObject* >( pAction->objSlotPtr())->tqt_invoke( slot_id, o ); 00437 } 00438 } else { 00439 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true ); 00440 if( slot_id >= 0 ) 00441 const_cast< TQObject* >( pAction->objSlotPtr())->tqt_invoke( slot_id, 0 ); 00442 } 00443 } 00444 00445 void TDEGlobalAccelPrivate::slotActivated( int iAction ) 00446 { 00447 TDEAccelAction* pAction = TDEAccelBase::actions().actionPtr( iAction ); 00448 if( pAction ) 00449 activate( pAction, KKeySequence() ); 00450 } 00451 00452 #include "kglobalaccel_x11.moc" 00453 00454 #endif // !Q_WS_X11