• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kdecore
 

kdecore

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

kdecore

Skip menu "kdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kdecore by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |