+ 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.
This commit is contained in:
Konstantin Isakov 2009-04-22 15:29:28 +00:00
parent 5bc60a13b6
commit 23abdb9b44
2 changed files with 104 additions and 7 deletions

View file

@ -52,15 +52,29 @@ HotkeyWrapper::~HotkeyWrapper()
void HotkeyWrapper::waitKey2() void HotkeyWrapper::waitKey2()
{ {
state2 = false; state2 = false;
#ifdef Q_WS_X11
if ( keyToUngrab != grabbedKeys.end() )
{
ungrabKey( keyToUngrab );
keyToUngrab = grabbedKeys.end();
}
#endif
} }
bool HotkeyWrapper::checkState(quint32 vk, quint32 mod) bool HotkeyWrapper::checkState(quint32 vk, quint32 mod)
{ {
if (state2) { // wait for 2nd key if ( state2 )
state2 = false; { // wait for 2nd key
if (state2waiter.key2 == vk && state2waiter.modifier == mod) {
emit hotkeyActivated( state2waiter.handle ); waitKey2(); // Cancel the 2nd-key wait stage
return true;
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; state2 = true;
state2waiter = hs; state2waiter = hs;
QTimer::singleShot(500, this, SLOT(waitKey2())); 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; return true;
} }
} }
@ -240,6 +268,8 @@ bool QHotkeyApplication::winEventFilter ( MSG * message, long * result )
void HotkeyWrapper::init() void HotkeyWrapper::init()
{ {
keyToUngrab = grabbedKeys.end();
// We use RECORD extension instead of XGrabKey. That's because XGrabKey // We use RECORD extension instead of XGrabKey. That's because XGrabKey
// prevents other clients from getting their input if it's grabbed. // prevents other clients from getting their input if it's grabbed.
@ -254,6 +284,9 @@ void HotkeyWrapper::init()
lAltCode = XKeysymToKeycode( display, XK_Alt_L ); lAltCode = XKeysymToKeycode( display, XK_Alt_L );
rAltCode = XKeysymToKeycode( display, XK_Alt_R ); rAltCode = XKeysymToKeycode( display, XK_Alt_R );
cCode = XKeysymToKeycode( display, XK_c );
insertCode = XKeysymToKeycode( display, XK_Insert );
currentModifiers = 0; currentModifiers = 0;
// This one will be used to read the recorded content // 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) if (modifier & Qt::AltModifier)
mod |= Mod1Mask; 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; 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) quint32 HotkeyWrapper::nativeKey(int key)
{ {
QString keySymName; QString keySymName;
@ -408,6 +479,9 @@ void HotkeyWrapper::unregister()
XFree( recordRange ); XFree( recordRange );
XCloseDisplay( dataDisplay ); XCloseDisplay( dataDisplay );
while( grabbedKeys.size() )
ungrabKey( grabbedKeys.begin() );
(static_cast<QHotkeyApplication*>(qApp))->unregisterWrapper(this); (static_cast<QHotkeyApplication*>(qApp))->unregisterWrapper(this);
} }

View file

@ -5,6 +5,8 @@
#ifdef Q_WS_X11 #ifdef Q_WS_X11
#include <set>
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/extensions/record.h> #include <X11/extensions/record.h>
#include <QX11Info> #include <QX11Info>
@ -84,7 +86,8 @@ private:
void run(); // QThread void run(); // QThread
// We do one-time init of those, translating keysyms to keycodes // 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; quint32 currentModifiers;
@ -93,6 +96,26 @@ private:
XRecordContext recordContext; XRecordContext recordContext;
XRecordClientSpec recordClientSpec; 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: signals:
/// Emitted from the thread /// Emitted from the thread