• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

kglobalaccel_x11.cpp

Go to the documentation of this file.
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 <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 static QValueList< KGlobalAccelPrivate* >* all_accels = 0;
00080 
00081 KGlobalAccelPrivate::KGlobalAccelPrivate()
00082 : KAccelBase( KAccelBase::NATIVE_KEYS )
00083 , m_blocked( false )
00084 , m_blockingDisabled( false )
00085 , m_suspended( false )
00086 {
00087         if( all_accels == NULL )
00088             all_accels = new QValueList< KGlobalAccelPrivate* >;
00089         all_accels->append( this );
00090     m_sConfigGroup = "Global Shortcuts";
00091     kapp->installX11EventFilter( this );
00092 }
00093 
00094 KGlobalAccelPrivate::~KGlobalAccelPrivate()
00095 {
00096     // TODO: Need to release all grabbed keys if the main window is not shutting down.
00097     //for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
00098     //  const CodeMod& codemod = it.key();
00099     //}
00100         all_accels->remove( this );
00101         if( all_accels->count() == 0 ) {
00102             delete all_accels;
00103             all_accels = NULL;
00104         }
00105 }
00106 
00107 void KGlobalAccelPrivate::setEnabled( bool bEnable )
00108 {
00109     m_bEnabled = bEnable;
00110     updateConnections();
00111 }
00112 
00113 void KGlobalAccelPrivate::blockShortcuts( bool block )
00114 {
00115         if( all_accels == NULL )
00116             return;
00117         for( QValueList< KGlobalAccelPrivate* >::ConstIterator it = all_accels->begin();
00118              it != all_accels->end();
00119              ++it ) {
00120             if( (*it)->m_blockingDisabled )
00121                 continue;
00122             (*it)->m_blocked = block;
00123             (*it)->updateConnections();
00124         }
00125 }
00126 
00127 void KGlobalAccelPrivate::disableBlocking( bool block )
00128 {
00129         m_blockingDisabled = block;
00130 }
00131 
00132 bool KGlobalAccelPrivate::isEnabledInternal() const
00133 {
00134         return KAccelBase::isEnabled() && !m_blocked;
00135 }
00136 
00137 // see #117169 - the bug is hard to reproduce, probably somewhere in X, testcase would be probably
00138 // difficult to make, and so on - just don't release the grabs and only ignore the events instead
00139 void KGlobalAccelPrivate::suspend( bool s )
00140 {
00141     m_suspended = s;
00142 }
00143 
00144 bool KGlobalAccelPrivate::emitSignal( Signal )
00145 {
00146     return false;
00147 }
00148 
00149 bool KGlobalAccelPrivate::connectKey( KAccelAction& action, const KKeyServer::Key& key )
00150     { return grabKey( key, true, &action ); }
00151 bool KGlobalAccelPrivate::connectKey( const KKeyServer::Key& key )
00152     { return grabKey( key, true, 0 ); }
00153 bool KGlobalAccelPrivate::disconnectKey( KAccelAction& action, const KKeyServer::Key& key )
00154     { return grabKey( key, false, &action ); }
00155 bool KGlobalAccelPrivate::disconnectKey( const KKeyServer::Key& key )
00156     { return grabKey( key, false, 0 ); }
00157 
00158 bool KGlobalAccelPrivate::grabKey( const KKeyServer::Key& key, bool bGrab, KAccelAction* pAction )
00159 {
00160     if( !key.code() ) {
00161         kdWarning(125) << "KGlobalAccelPrivate::grabKey( " << key.key().toStringInternal() << ", " << bGrab << ", \"" << (pAction ? pAction->name().latin1() : "(null)") << "\" ): Tried to grab key with null code." << endl;
00162         return false;
00163     }
00164 
00165     // Make sure that grab masks have been initialized.
00166     if( g_keyModMaskXOnOrOff == 0 )
00167         calculateGrabMasks();
00168 
00169     uchar keyCodeX = key.code();
00170     uint keyModX = key.mod() & g_keyModMaskXAccel; // Get rid of any non-relevant bits in mod
00171     // HACK: make Alt+Print work
00172     // only do this for the Xorg default keyboard keycodes,
00173     // other mappings (e.g. evdev) don't need or want it
00174     if( key.sym() == XK_Sys_Req && XKeycodeToKeysym( qt_xdisplay(), 111, 0 ) == XK_Print ) {
00175         keyModX |= KKeyServer::modXAlt();
00176         keyCodeX = 111;
00177     }
00178 
00179 #ifndef __osf__
00180 // this crashes under Tru64 so .....
00181     kdDebug(125) << QString( "grabKey( key: '%1', bGrab: %2 ): keyCodeX: %3 keyModX: %4\n" )
00182         .arg( key.key().toStringInternal() ).arg( bGrab )
00183         .arg( keyCodeX, 0, 16 ).arg( keyModX, 0, 16 );
00184 #endif
00185     if( !keyCodeX )
00186         return false;
00187 
00188 #ifdef Q_WS_X11
00189         KXErrorHandler handler( XGrabErrorHandler );
00190 #endif
00191     // We'll have to grab 8 key modifier combinations in order to cover all
00192     //  combinations of CapsLock, NumLock, ScrollLock.
00193     // Does anyone with more X-savvy know how to set a mask on qt_xrootwin so that
00194     //  the irrelevant bits are always ignored and we can just make one XGrabKey
00195     //  call per accelerator? -- ellis
00196 #ifndef NDEBUG
00197     QString sDebug = QString("\tcode: 0x%1 state: 0x%2 | ").arg(keyCodeX,0,16).arg(keyModX,0,16);
00198 #endif
00199     uint keyModMaskX = ~g_keyModMaskXOnOrOff;
00200     for( uint irrelevantBitsMask = 0; irrelevantBitsMask <= 0xff; irrelevantBitsMask++ ) {
00201         if( (irrelevantBitsMask & keyModMaskX) == 0 ) {
00202 #ifndef NDEBUG
00203             sDebug += QString("0x%3, ").arg(irrelevantBitsMask, 0, 16);
00204 #endif
00205             if( bGrab )
00206                 XGrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask,
00207                     qt_xrootwin(), True, GrabModeAsync, GrabModeSync );
00208             else
00209                 XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | irrelevantBitsMask, qt_xrootwin() );
00210         }
00211     }
00212 #ifndef NDEBUG
00213     kdDebug(125) << sDebug << endl;
00214 #endif
00215 
00216         bool failed = false;
00217         if( bGrab ) {
00218 #ifdef Q_WS_X11
00219             failed = handler.error( true ); // sync now
00220 #endif
00221             // If grab failed, then ungrab any grabs that could possibly succeed
00222         if( failed ) {
00223             kdDebug(125) << "grab failed!\n";
00224             for( uint m = 0; m <= 0xff; m++ ) {
00225                 if(( m & keyModMaskX ) == 0 )
00226                     XUngrabKey( qt_xdisplay(), keyCodeX, keyModX | m, qt_xrootwin() );
00227                 }
00228                 }
00229     }
00230         if( !failed )
00231         {
00232         CodeMod codemod;
00233         codemod.code = keyCodeX;
00234         codemod.mod = keyModX;
00235         if( key.mod() & KKeyServer::MODE_SWITCH )
00236             codemod.mod |= KKeyServer::MODE_SWITCH;
00237 
00238         if( bGrab )
00239             m_rgCodeModToAction.insert( codemod, pAction );
00240         else
00241             m_rgCodeModToAction.remove( codemod );
00242     }
00243     return !failed;
00244 }
00245 
00246 bool KGlobalAccelPrivate::x11Event( XEvent* pEvent )
00247 {
00248     //kdDebug(125) << "x11EventFilter( type = " << pEvent->type << " )" << endl;
00249     switch( pEvent->type ) {
00250      case MappingNotify:
00251             XRefreshKeyboardMapping( &pEvent->xmapping );
00252         x11MappingNotify();
00253         return false;
00254      case XKeyPress:
00255         if( x11KeyPress( pEvent ) )
00256             return true;
00257      default:
00258         return QWidget::x11Event( pEvent );
00259     }
00260 }
00261 
00262 void KGlobalAccelPrivate::x11MappingNotify()
00263 {
00264     kdDebug(125) << "KGlobalAccelPrivate::x11MappingNotify()" << endl;
00265     // Maybe the X modifier map has been changed.
00266     KKeyServer::initializeMods();
00267     calculateGrabMasks();
00268     // Do new XGrabKey()s.
00269     updateConnections();
00270 }
00271 
00272 bool KGlobalAccelPrivate::x11KeyPress( const XEvent *pEvent )
00273 {
00274     // do not change this line unless you really really know what you are doing (Matthias)
00275     if ( !QWidget::keyboardGrabber() && !QApplication::activePopupWidget() ) {
00276         XUngrabKeyboard( qt_xdisplay(), pEvent->xkey.time );
00277                 XFlush( qt_xdisplay()); // avoid X(?) bug
00278         }
00279 
00280     if( !isEnabledInternal() || m_suspended )
00281         return false;
00282 
00283     CodeMod codemod;
00284     codemod.code = pEvent->xkey.keycode;
00285     codemod.mod = pEvent->xkey.state & (g_keyModMaskXAccel | KKeyServer::MODE_SWITCH);
00286 
00287     // If numlock is active and a keypad key is pressed, XOR the SHIFT state.
00288     //  e.g., KP_4 => Shift+KP_Left, and Shift+KP_4 => KP_Left.
00289     if( pEvent->xkey.state & KKeyServer::modXNumLock() ) {
00290         // TODO: what's the xor operator in c++?
00291         uint sym = XKeycodeToKeysym( qt_xdisplay(), codemod.code, 0 );
00292         // If this is a keypad key,
00293         if( sym >= XK_KP_Space && sym <= XK_KP_9 ) {
00294             switch( sym ) {
00295                 // Leave the following keys unaltered
00296                 // FIXME: The proper solution is to see which keysyms don't change when shifted.
00297                 case XK_KP_Multiply:
00298                 case XK_KP_Add:
00299                 case XK_KP_Subtract:
00300                 case XK_KP_Divide:
00301                     break;
00302                 default:
00303                     if( codemod.mod & KKeyServer::modXShift() )
00304                         codemod.mod &= ~KKeyServer::modXShift();
00305                     else
00306                         codemod.mod |= KKeyServer::modXShift();
00307             }
00308         }
00309     }
00310 
00311     KKeyNative keyNative( pEvent );
00312     KKey key = keyNative;
00313 
00314     kdDebug(125) << "x11KeyPress: seek " << key.toStringInternal()
00315         << QString( " keyCodeX: %1 state: %2 keyModX: %3" )
00316             .arg( codemod.code, 0, 16 ).arg( pEvent->xkey.state, 0, 16 ).arg( codemod.mod, 0, 16 ) << endl;
00317 
00318     // Search for which accelerator activated this event:
00319     if( !m_rgCodeModToAction.contains( codemod ) ) {
00320 #ifndef NDEBUG
00321         for( CodeModMap::ConstIterator it = m_rgCodeModToAction.begin(); it != m_rgCodeModToAction.end(); ++it ) {
00322             KAccelAction* pAction = *it;
00323             kdDebug(125) << "\tcode: " << QString::number(it.key().code, 16) << " mod: " << QString::number(it.key().mod, 16)
00324                 << (pAction ? QString(" name: \"%1\" shortcut: %2").arg(pAction->name()).arg(pAction->shortcut().toStringInternal()) : QString::null)
00325                 << endl;
00326         }
00327 #endif
00328         return false;
00329     }
00330         
00331     KAccelAction* pAction = m_rgCodeModToAction[codemod];
00332 
00333     if( !pAction ) {
00334                 static bool recursion_block = false;
00335                 if( !recursion_block ) {
00336                         recursion_block = true;
00337                 QPopupMenu* pMenu = createPopupMenu( 0, KKeySequence(key) );
00338                 connect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)) );
00339                 pMenu->exec( QPoint( 0, 0 ) );
00340                 disconnect( pMenu, SIGNAL(activated(int)), this, SLOT(slotActivated(int)));
00341                 delete pMenu;
00342                         recursion_block = false;
00343                 }
00344     } else if( !pAction->objSlotPtr() || !pAction->isEnabled() )
00345         return false;
00346     else
00347         activate( pAction, KKeySequence(key) );
00348 
00349     return true;
00350 }
00351 
00352 void KGlobalAccelPrivate::activate( KAccelAction* pAction, const KKeySequence& seq )
00353 {
00354     kdDebug(125) << "KGlobalAccelPrivate::activate( \"" << pAction->name() << "\" ) " << endl;
00355 
00356     QRegExp rexPassIndex( "([ ]*int[ ]*)" );
00357     QRegExp rexPassInfo( " QString" );
00358     QRegExp rexIndex( " ([0-9]+)$" );
00359 
00360     // If the slot to be called accepts an integer index
00361     //  and an index is present at the end of the action's name,
00362     //  then send the slot the given index #.
00363     if( rexPassIndex.search( pAction->methodSlotPtr() ) >= 0 && rexIndex.search( pAction->name() ) >= 0 ) {
00364         int n = rexIndex.cap(1).toInt();
00365         kdDebug(125) << "Calling " << pAction->methodSlotPtr() << " int = " << n << endl;
00366                 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00367                 if( slot_id >= 0 ) {
00368                     QUObject o[2];
00369                     static_QUType_int.set(o+1,n);
00370                     const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, o );
00371                 }
00372     } else if( rexPassInfo.search( pAction->methodSlotPtr() ) ) {
00373                 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00374                 if( slot_id >= 0 ) {
00375                     QUObject o[4];
00376                     static_QUType_QString.set(o+1,pAction->name());
00377                     static_QUType_QString.set(o+2,pAction->label());
00378                     static_QUType_ptr.set(o+3,&seq);
00379                     const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, o );
00380                 }
00381     } else {
00382                 int slot_id = pAction->objSlotPtr()->metaObject()->findSlot( normalizeSignalSlot( pAction->methodSlotPtr() ).data() + 1, true );
00383                 if( slot_id >= 0 )
00384                     const_cast< QObject* >( pAction->objSlotPtr())->qt_invoke( slot_id, 0 );
00385     }
00386 }
00387 
00388 void KGlobalAccelPrivate::slotActivated( int iAction )
00389 {
00390     KAccelAction* pAction = actions().actionPtr( iAction );
00391     if( pAction )
00392         activate( pAction, KKeySequence() );
00393 }
00394 
00395 #include "kglobalaccel_x11.moc"
00396 
00397 #endif // !Q_WS_X11

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal