kdecore Library: kglobalaccel_x11.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:04 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003