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
00038
00039
00040
00041
00042
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
00054
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
00069
00070
00071
00072 }
00073
00074 void KGlobalAccelPrivate::setEnabled( bool bEnable )
00075 {
00076 m_bEnabled = bEnable;
00077
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
00102 if( g_keyModMaskXOnOrOff == 0 )
00103 calculateGrabMasks();
00104
00105 uchar keyCodeX = key.code();
00106 uint keyModX = key.mod() & g_keyModMaskXAccel;
00107
00108 if( key.sym() == XK_Sys_Req ) {
00109 keyModX |= KKeyServer::modXAlt();
00110 keyCodeX = 111;
00111 }
00112
00113 #ifndef __osf__
00114
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
00126
00127
00128
00129
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 );
00154 #endif
00155
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
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
00201 KKeyServer::initializeMods();
00202 calculateGrabMasks();
00203
00204 updateConnections();
00205 }
00206 }
00207
00208 bool KGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent )
00209 {
00210
00211 if ( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget() ) {
00212 XUngrabKeyboard( qt_xdisplay(), pEvent->xkey.time );
00213 XFlush( qt_xdisplay());
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
00224
00225 if( pEvent->xkey.state & KKeyServer::modXNumLock() ) {
00226
00227 uint sym = XKeycodeToKeysym( qt_xdisplay(), codemod.code, 0 );
00228
00229 if( sym >= XK_KP_Space && sym <= XK_KP_9 ) {
00230 switch( sym ) {
00231
00232
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
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
00296
00297
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