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 KKeyServer::modXModeSwitch();
00054
00055
00056 }
00057
00058
00059
00060 KGlobalAccelPrivate::KGlobalAccelPrivate()
00061 : KAccelBase( KAccelBase::NATIVE_KEYS )
00062 {
00063 m_sConfigGroup = "Global Shortcuts";
00064 kapp->installX11EventFilter( this );
00065 }
00066
00067 KGlobalAccelPrivate::~KGlobalAccelPrivate()
00068 {
00069
00070
00071
00072
00073 }
00074
00075 void KGlobalAccelPrivate::setEnabled( bool bEnable )
00076 {
00077 m_bEnabled = bEnable;
00078
00079 }
00080
00081 bool KGlobalAccelPrivate::emitSignal( Signal )
00082 {
00083 return false;
00084 }
00085
00086 bool KGlobalAccelPrivate::connectKey( KAccelAction& action, const KKeyServer::Key& key )
00087 { return grabKey( key, true, &action ); }
00088 bool KGlobalAccelPrivate::connectKey( const KKeyServer::Key& key )
00089 { return grabKey( key, true, 0 ); }
00090 bool KGlobalAccelPrivate::disconnectKey( KAccelAction& action, const KKeyServer::Key& key )
00091 { return grabKey( key, false, &action ); }
00092 bool KGlobalAccelPrivate::disconnectKey( const KKeyServer::Key& key )
00093 { return grabKey( key, false, 0 ); }
00094
00095 bool KGlobalAccelPrivate::grabKey( const KKeyServer::Key& key, bool bGrab, KAccelAction* pAction )
00096 {
00097 if( !key.code() ) {
00098 kdWarning(125) << "KGlobalAccelPrivate::grabKey( " << key.key().toStringInternal() << ", " << bGrab << ", \"" << (pAction ? pAction->name().latin1() : "(null)") << "\" ): Tried to grab key with null code." << endl;
00099 return false;
00100 }
00101
00102
00103 if( g_keyModMaskXOnOrOff == 0 )
00104 calculateGrabMasks();
00105
00106 uchar keyCodeX = key.code();
00107 uint keyModX = key.mod() & g_keyModMaskXAccel;
00108
00109 if( key.sym() == XK_Sys_Req ) {
00110 keyModX |= KKeyServer::modXAlt();
00111 keyCodeX = 111;
00112 }
00113
00114 #ifndef __osf__
00115
00116 kdDebug(125) << QString( "grabKey( key: '%1', bGrab: %2 ): keyCodeX: %3 keyModX: %4\n" )
00117 .arg( key.key().toStringInternal() ).arg( bGrab )
00118 .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 );
00119 #endif
00120 if( !keyCodeX )
00121 return false;
00122
00123 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00124 KXErrorHandler handler( XGrabErrorHandler );
00125 #endif
00126
00127
00128
00129
00130
00131 #ifndef NDEBUG
00132 QString sDebug = QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16);
00133 #endif
00134 uint keyModMaskX = ~g_keyModMaskXOnOrOff;
00135 for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) {
00136 if( (irrelevantBitsMask & keyModMaskX) == 0 ) {
00137 #ifndef NDEBUG
00138 sDebug += QString("0x%3, ").arg(irrelevantBitsMask, 0, 16);
00139 #endif
00140 if( bGrab )
00141 XGrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask,
00142 qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
00143 else
00144 XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin() );
00145 }
00146 }
00147 #ifndef NDEBUG
00148 kdDebug(125) << sDebug << endl;
00149 #endif
00150
00151 bool failed = false;
00152 if( bGrab ) {
00153 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00154 failed = handler.error( true );
00155 #endif
00156
00157 if( failed ) {
00158 kdDebug(125) << "grab failed!\n";
00159 for( uint m = 0; m <= 0xff; m++ ) {
00160 if( m & keyModMaskX == 0 )
00161 XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | m, qt_xrootwin() );
00162 }
00163 }
00164 }
00165 if( !failed )
00166 {
00167 CodeMod codemod;
00168 codemod.code = keyCodeX;
00169 codemod.mod = keyModX;
00170 if( key.mod() & KKeyServer::MODE_SWITCH )
00171 codemod.mod |= KKeyServer::MODE_SWITCH;
00172
00173 if( bGrab )
00174 m_rgCodeModToAction.insert( codemod, pAction );
00175 else
00176 m_rgCodeModToAction.remove( codemod );
00177 }
00178 return !failed;
00179 }
00180
00181 bool KGlobalAccelPrivate::x11Event( XEvent* pEvent )
00182 {
00183
00184 switch( pEvent->type ) {
00185 case MappingNotify:
00186 XRefreshKeyboardMapping( &pEvent->xmapping );
00187 x11MappingNotify();
00188 return false;
00189 case XKeyPress:
00190 if( x11KeyPress( pEvent ) )
00191 return true;
00192 default:
00193 return QWidget::x11Event( pEvent );
00194 }
00195 }
00196
00197 void KGlobalAccelPrivate::x11MappingNotify()
00198 {
00199 kdDebug(125) << "KGlobalAccelPrivate::x11MappingNotify()" << endl;
00200 if( m_bEnabled ) {
00201
00202 KKeyServer::initializeMods();
00203 calculateGrabMasks();
00204
00205 updateConnections();
00206 }
00207 }
00208
00209 bool KGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent )
00210 {
00211
00212 if ( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget() ) {
00213 XUngrabKeyboard( qt_xdisplay(), pEvent->xkey.time );
00214 XFlush( qt_xdisplay());
00215 }
00216
00217 if( !m_bEnabled )
00218 return false;
00219
00220 CodeMod codemod;
00221 codemod.code = pEvent->xkey.keycode;
00222 codemod.mod = pEvent->xkey.state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH);
00223
00224
00225
00226 if( pEvent->xkey.state & KKeyServer::modXNumLock() ) {
00227
00228 uint sym = XKeycodeToKeysym( qt_xdisplay(), codemod.code, 0 );
00229
00230 if( sym >= XK_KP_Space && sym <= XK_KP_9 ) {
00231 switch( sym ) {
00232
00233
00234 case XK_KP_Multiply:
00235 case XK_KP_Add:
00236 case XK_KP_Subtract:
00237 case XK_KP_Divide:
00238 break;
00239 default:
00240 if( codemod.mod & KKeyServer::modXShift() )
00241 codemod.mod &= ~KKeyServer::modXShift();
00242 else
00243 codemod.mod |= KKeyServer::modXShift();
00244 }
00245 }
00246 }
00247
00248 KKeyNative keyNative( pEvent );
00249 KKey key = keyNative;
00250
00251 kdDebug(125) << "x11KeyPress: seek " << key.toStringInternal()
00252 << QString( " keyCodeX: %1 state: %2 keyModX: %3" )
00253 .arg( codemod.code, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ).arg( codemod.mod, 0, 16 ) << endl;
00254
00255
00256 if( !m_rgCodeModToAction.contains( codemod ) ) {
00257 #ifndef NDEBUG
00258 for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
00259 KAccelAction* pAction = *it;
00260 kdDebug(125) << "\tcode: " << QString::number(it.key().code, 16) << " mod: " << QString::number(it.key().mod, 16)
00261 << (pAction ? QString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : QString::null)
00262 << endl;
00263 }
00264 #endif
00265 return false;
00266 }
00267 KAccelAction* pAction = m_rgCodeModToAction[codemod];
00268
00269 if( !pAction ) {
00270 static bool recursion_block = false;
00271 if( !recursion_block ) {
00272 recursion_block = true;
00273 QPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) );
00274 connect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)) );
00275 pMenu->exec( QPoint( 0, 0 ) );
00276 disconnect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
00277 delete pMenu;
00278 recursion_block = false;
00279 }
00280 } else if( !pAction->objSlotPtr() || !pAction->isEnabled() )
00281 return false;
00282 else
00283 activate( pAction, KKeySequence(key) );
00284
00285 return true;
00286 }
00287
00288 void KGlobalAccelPrivate::activate( KAccelAction* pAction, const KKeySequence& seq )
00289 {
00290 kdDebug(125) << "KGlobalAccelPrivate::activate( \"" << pAction->name() << "\" ) " << endl;
00291
00292 QRegExp rexPassIndex( "([ ]*int[ ]*)" );
00293 QRegExp rexPassInfo( " QString" );
00294 QRegExp rexIndex( " ([0-9]+)$" );
00295
00296
00297
00298
00299 if( rexPassIndex.search( pAction->methodSlotPtr() ) >= 0 && rexIndex.search( pAction->name() ) >= 0 ) {
00300 int n = rexIndex.cap(1).toInt();
00301 kdDebug(125) << "Calling " << pAction->methodSlotPtr() << " int = " << n << endl;
00302 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00303 if( slot_id >= 0 ) {
00304 QUObject o[2];
00305 static_QUType_int.set(o+1,n);
00306 const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, o );
00307 }
00308 } else if( rexPassInfo.search( pAction->methodSlotPtr() ) ) {
00309 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00310 if( slot_id >= 0 ) {
00311 QUObject o[4];
00312 static_QUType_QString.set(o+1,pAction->name());
00313 static_QUType_QString.set(o+2,pAction->label());
00314 static_QUType_ptr.set(o+3,&seq);
00315 const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, o );
00316 }
00317 } else {
00318 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00319 if( slot_id >= 0 )
00320 const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, 0 );
00321 }
00322 }
00323
00324 void KGlobalAccelPrivate::slotActivated( int iAction )
00325 {
00326 KAccelAction* pAction = actions().actionPtr( iAction );
00327 if( pAction )
00328 activate( pAction, KKeySequence() );
00329 }
00330
00331 #include "kglobalaccel_x11.moc"
00332
00333 #endif // !Q_WS_X11