From 23abdb9b44c8f15e27a99684ff2e89a882110a22 Mon Sep 17 00:00:00 2001 From: Konstantin Isakov Date: Wed, 22 Apr 2009 15:29:28 +0000 Subject: [PATCH] + Shield the hotkeys from being intercepted by other apps using XGrabKey(), unless the hotkey is a clipboard copy key on its first occurence in a combo. --- src/hotkeywrapper.cc | 86 ++++++++++++++++++++++++++++++++++++++++---- src/hotkeywrapper.hh | 25 ++++++++++++- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/hotkeywrapper.cc b/src/hotkeywrapper.cc index d392a68b..804ec994 100644 --- a/src/hotkeywrapper.cc +++ b/src/hotkeywrapper.cc @@ -52,15 +52,29 @@ HotkeyWrapper::~HotkeyWrapper() void HotkeyWrapper::waitKey2() { state2 = false; + +#ifdef Q_WS_X11 + + if ( keyToUngrab != grabbedKeys.end() ) + { + ungrabKey( keyToUngrab ); + keyToUngrab = grabbedKeys.end(); + } + +#endif } bool HotkeyWrapper::checkState(quint32 vk, quint32 mod) { - if (state2) { // wait for 2nd key - state2 = false; - if (state2waiter.key2 == vk && state2waiter.modifier == mod) { - emit hotkeyActivated( state2waiter.handle ); - return true; + if ( state2 ) + { // wait for 2nd key + + waitKey2(); // Cancel the 2nd-key wait stage + + if (state2waiter.key2 == vk && state2waiter.modifier == mod) + { + emit hotkeyActivated( state2waiter.handle ); + return true; } } @@ -77,6 +91,20 @@ bool HotkeyWrapper::checkState(quint32 vk, quint32 mod) state2 = true; state2waiter = hs; QTimer::singleShot(500, this, SLOT(waitKey2())); + + #ifdef Q_WS_X11 + + // Grab the second key, unless it's grabbed already + // Note that we only grab the clipboard key only if + // the sequence didn't begin with it + + if ( ( isCopyToClipboardKey( hs.key, hs.modifier ) || + !isCopyToClipboardKey( hs.key2, hs.modifier ) ) && + !isKeyGrabbed( hs.key2, hs.modifier ) ) + keyToUngrab = grabKey( hs.key2, hs.modifier ); + + #endif + return true; } } @@ -240,6 +268,8 @@ bool QHotkeyApplication::winEventFilter ( MSG * message, long * result ) void HotkeyWrapper::init() { + keyToUngrab = grabbedKeys.end(); + // We use RECORD extension instead of XGrabKey. That's because XGrabKey // prevents other clients from getting their input if it's grabbed. @@ -254,6 +284,9 @@ void HotkeyWrapper::init() lAltCode = XKeysymToKeycode( display, XK_Alt_L ); rAltCode = XKeysymToKeycode( display, XK_Alt_R ); + cCode = XKeysymToKeycode( display, XK_c ); + insertCode = XKeysymToKeycode( display, XK_Insert ); + currentModifiers = 0; // This one will be used to read the recorded content @@ -372,11 +405,49 @@ bool HotkeyWrapper::setGlobalKey( int key, int key2, if (modifier & Qt::AltModifier) mod |= Mod1Mask; - hotkeys.append( HotkeyStruct( vk, vk2, mod, handle ) ); + hotkeys.append( HotkeyStruct( vk, vk2, mod, handle, 0 ) ); + + if ( !isCopyToClipboardKey( vk, mod ) ) + grabKey( vk, mod ); // Make sure it doesn't get caught by other apps return true; } +bool HotkeyWrapper::isCopyToClipboardKey( quint32 keyCode, quint32 modifiers ) const +{ + return modifiers == ControlMask && + ( keyCode == cCode || keyCode == insertCode ); +} + +bool HotkeyWrapper::isKeyGrabbed( quint32 keyCode, quint32 modifiers ) const +{ + GrabbedKeys::const_iterator i = grabbedKeys.find( std::make_pair( keyCode, modifiers ) ); + + return i != grabbedKeys.end(); +} + +HotkeyWrapper::GrabbedKeys::iterator HotkeyWrapper::grabKey( quint32 keyCode, + quint32 modifiers ) +{ + std::pair< GrabbedKeys::iterator, bool > result = + grabbedKeys.insert( std::make_pair( keyCode, modifiers ) ); + + if ( result.second ) + { + XGrabKey( QX11Info::display(), keyCode, modifiers, QX11Info::appRootWindow(), + True, GrabModeAsync, GrabModeAsync ); + } + + return result.first; +} + +void HotkeyWrapper::ungrabKey( GrabbedKeys::iterator i ) +{ + XUngrabKey( QX11Info::display(), i->first, i->second, QX11Info::appRootWindow() ); + + grabbedKeys.erase( i ); +} + quint32 HotkeyWrapper::nativeKey(int key) { QString keySymName; @@ -408,6 +479,9 @@ void HotkeyWrapper::unregister() XFree( recordRange ); XCloseDisplay( dataDisplay ); + while( grabbedKeys.size() ) + ungrabKey( grabbedKeys.begin() ); + (static_cast(qApp))->unregisterWrapper(this); } diff --git a/src/hotkeywrapper.hh b/src/hotkeywrapper.hh index 4229fa97..36daa54c 100644 --- a/src/hotkeywrapper.hh +++ b/src/hotkeywrapper.hh @@ -5,6 +5,8 @@ #ifdef Q_WS_X11 +#include + #include #include #include @@ -84,7 +86,8 @@ private: void run(); // QThread // We do one-time init of those, translating keysyms to keycodes - KeyCode lShiftCode, rShiftCode, lCtrlCode, rCtrlCode, lAltCode, rAltCode; + KeyCode lShiftCode, rShiftCode, lCtrlCode, rCtrlCode, lAltCode, rAltCode, + cCode, insertCode; quint32 currentModifiers; @@ -93,6 +96,26 @@ private: XRecordContext recordContext; XRecordClientSpec recordClientSpec; + /// Holds all the keys currently grabbed. + /// The first value is keycode, the second is modifiers + typedef std::set< std::pair< quint32, quint32 > > GrabbedKeys; + GrabbedKeys grabbedKeys; + + GrabbedKeys::iterator keyToUngrab; // Used for second stage grabs + + /// Returns true if the given key is usually used to copy from clipboard, + /// false otherwise. + bool isCopyToClipboardKey( quint32 keyCode, quint32 modifiers ) const; + /// Returns true if the given key is grabbed, false otherwise + bool isKeyGrabbed( quint32 keyCode, quint32 modifiers ) const; + /// Grabs the given key, recording the fact in grabbedKeys. If the key's + /// already grabbed, does nothing. + /// Returns the key's iterator in grabbedKeys. + GrabbedKeys::iterator grabKey( quint32 keyCode, quint32 modifiers ); + /// Ungrabs the given key. erasing it from grabbedKeys. The key's provided + /// as an interator inside the grabbedKeys set. + void ungrabKey( GrabbedKeys::iterator ); + signals: /// Emitted from the thread