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

tdecore

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

tdecore

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

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.6.3
This website is maintained by Timothy Pearson.