Use a dedicated keycode for keys that require another modifier than shift.

This commit is contained in:
Albert Weichselbraun 2013-11-22 07:49:13 +01:00
parent 3fcfc348ed
commit a26119ea20
2 changed files with 110 additions and 319 deletions

View File

@ -42,14 +42,20 @@ AutoTypePlatformX11::AutoTypePlatformX11()
m_currentGlobalModifiers = 0; m_currentGlobalModifiers = 0;
m_keysymTable = Q_NULLPTR; m_keysymTable = Q_NULLPTR;
m_altMask = 0; m_specialCharacterKeycode = 0;
m_metaMask = 0; m_modifierMask = ControlMask | ShiftMask | Mod1Mask | Mod4Mask;
m_altgrMask = 0;
m_altgrKeysym = NoSymbol;
updateKeymap(); updateKeymap();
} }
/*
* Restore the KeyboardMapping to its original state.
*/
AutoTypePlatformX11::~AutoTypePlatformX11() {
AddKeysym(NoSymbol);
}
QStringList AutoTypePlatformX11::windowTitles() QStringList AutoTypePlatformX11::windowTitles()
{ {
return windowTitlesRecursive(m_rootWindow); return windowTitlesRecursive(m_rootWindow);
@ -125,10 +131,10 @@ uint AutoTypePlatformX11::qtToNativeModifiers(Qt::KeyboardModifiers modifiers)
nativeModifiers |= ControlMask; nativeModifiers |= ControlMask;
} }
if (modifiers & Qt::AltModifier) { if (modifiers & Qt::AltModifier) {
nativeModifiers |= m_altMask; nativeModifiers |= Mod1Mask;
} }
if (modifiers & Qt::MetaModifier) { if (modifiers & Qt::MetaModifier) {
nativeModifiers |= m_metaMask; nativeModifiers |= Mod4Mask;
} }
return nativeModifiers; return nativeModifiers;
@ -165,6 +171,7 @@ int AutoTypePlatformX11::platformEventFilter(void* event)
return 1; return 1;
} }
if (xevent->type == MappingNotify) { if (xevent->type == MappingNotify) {
XRefreshKeyboardMapping(reinterpret_cast<XMappingEvent*>(xevent));
updateKeymap(); updateKeymap();
} }
@ -387,24 +394,45 @@ KeySym AutoTypePlatformX11::keyToKeySym(Qt::Key key)
} }
} }
/*
* Update the keyboard and modifier mapping.
* We need the KeyboardMapping for AddKeysym.
* Modifier mapping is required for clearing the modifiers.
*/
void AutoTypePlatformX11::updateKeymap() void AutoTypePlatformX11::updateKeymap()
{ {
ReadKeymap(); int keycode, inx;
int mod_index, mod_key;
XModifierKeymap *modifiers;
if (!m_altgrMask) { XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
AddModifier(XK_Mode_switch); if (m_keysymTable != NULL) XFree(m_keysymTable);
} m_keysymTable = XGetKeyboardMapping(m_dpy,
if (!m_metaMask) { m_minKeycode, m_maxKeycode - m_minKeycode + 1,
m_metaMask = Mod4Mask; &m_keysymPerKeycode);
if (m_specialCharacterKeycode == 0) {
for (keycode = m_minKeycode; keycode <= m_maxKeycode; keycode++) {
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
if (m_keysymTable[inx] == NoSymbol) {
m_specialCharacterKeycode = keycode;
break;
}
}
} }
m_modifierMask = ControlMask | ShiftMask | m_altMask | m_metaMask; modifiers = XGetModifierMapping(m_dpy);
for (mod_index = 0; mod_index < 8; mod_index ++) {
// TODO: figure out why this breaks after the first global auto-type m_modifier_keycode[mod_index] = 0;
/*if (m_currentGlobalKey && m_currentGlobalModifiers) { for (mod_key = 0; mod_key < modifiers->max_keypermod; mod_key++) {
unregisterGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers); keycode = modifiers->modifiermap[mod_index * modifiers->max_keypermod + mod_key];
registerGlobalShortcut(m_currentGlobalKey, m_currentGlobalModifiers); if (keycode) {
}*/ m_modifier_keycode[mod_index] = keycode;
break;
}
}
}
XFreeModifiermap(modifiers);
} }
void AutoTypePlatformX11::startCatchXErrors() void AutoTypePlatformX11::startCatchXErrors()
@ -442,150 +470,19 @@ int AutoTypePlatformX11::x11ErrorHandler(Display* display, XErrorEvent* error)
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
/* /*
* Insert a specified keysym to unused position in the keymap table. * Insert a specified keysym on the dedicated position in the keymap
* This will be called to add required keysyms on-the-fly. * table.
* if the second parameter is TRUE, the keysym will be added to the
* non-shifted position - this may be required for modifier keys
* (e.g. Mode_switch) and some special keys (e.g. F20).
*/ */
int AutoTypePlatformX11::AddKeysym(KeySym keysym, bool top) int AutoTypePlatformX11::AddKeysym(KeySym keysym)
{ {
int keycode, pos, max_pos, inx, phase; int inx = (m_specialCharacterKeycode - m_minKeycode) * m_keysymPerKeycode;
m_keysymTable[inx] = keysym;
if (top) { XChangeKeyboardMapping(m_dpy, m_specialCharacterKeycode, m_keysymPerKeycode, &m_keysymTable[inx], 1);
max_pos = 0; XFlush(m_dpy);
} else { /* Xlib needs some time until the mapping is distributed to
max_pos = m_keysymPerKeycode - 1; all clients */
if (4 <= max_pos) max_pos = 3; usleep(10000);
if (2 <= max_pos && m_altgrKeysym != XK_Mode_switch) max_pos = 1; return m_specialCharacterKeycode;
}
for (phase = 0; phase < 2; phase++) {
for (keycode = m_maxKeycode; m_minKeycode <= keycode; keycode--) {
for (pos = max_pos; 0 <= pos; pos--) {
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
if ((phase != 0 || m_keysymTable[inx] == NoSymbol) && m_keysymTable[inx] < 0xFF00) {
/* In the first phase, to avoid modifing existing keys, */
/* add the keysym only to the keys which has no keysym in the first position. */
/* If no place fuond in the first phase, add the keysym for any keys except */
/* for modifier keys and other special keys */
if (m_keysymTable[inx + pos] == NoSymbol) {
m_keysymTable[inx + pos] = keysym;
XChangeKeyboardMapping(m_dpy, keycode, m_keysymPerKeycode, &m_keysymTable[inx], 1);
XFlush(m_dpy);
return keycode;
}
}
}
}
}
qWarning("Couldn't add \"%s\" to keymap", XKeysymToString(keysym));
return NoSymbol;
}
/*
* Add the specified key as a new modifier.
* This is used to use Mode_switch (AltGr) as a modifier.
*/
void AutoTypePlatformX11::AddModifier(KeySym keysym)
{
XModifierKeymap *modifiers;
int keycode, i, pos;
keycode = XKeysymToKeycode(m_dpy, keysym);
if (keycode == NoSymbol) keycode = AddKeysym(keysym, TRUE);
modifiers = XGetModifierMapping(m_dpy);
for (i = 7; 3 < i; i--) {
if (modifiers->modifiermap[i * modifiers->max_keypermod] == NoSymbol
|| ((m_keysymTable[(modifiers->modifiermap[i * modifiers->max_keypermod]
- m_minKeycode) * m_keysymPerKeycode]) == XK_ISO_Level3_Shift
&& keysym == XK_Mode_switch))
{
for (pos = 0; pos < modifiers->max_keypermod; pos++) {
if (modifiers->modifiermap[i * modifiers->max_keypermod + pos] == NoSymbol) {
modifiers->modifiermap[i * modifiers->max_keypermod + pos] = keycode;
XSetModifierMapping(m_dpy, modifiers);
return;
}
}
}
}
qWarning("Couldn't add \"%s\" as modifier", XKeysymToString(keysym));
}
/*
* Read keyboard mapping and modifier mapping.
* Keyboard mapping is used to know what keys are in shifted position.
* Modifier mapping is required because we should know Alt and Meta
* key are used as which modifier.
*/
void AutoTypePlatformX11::ReadKeymap()
{
int i;
int keycode, inx, pos;
KeySym keysym;
XModifierKeymap *modifiers;
XDisplayKeycodes(m_dpy, &m_minKeycode, &m_maxKeycode);
if (m_keysymTable != NULL) XFree(m_keysymTable);
m_keysymTable = XGetKeyboardMapping(m_dpy,
m_minKeycode, m_maxKeycode - m_minKeycode + 1,
&m_keysymPerKeycode);
for (keycode = m_minKeycode; keycode <= m_maxKeycode; keycode++) {
/* if the first keysym is alphabet and the second keysym is NoSymbol,
it is equivalent to pair of lowercase and uppercase alphabet */
inx = (keycode - m_minKeycode) * m_keysymPerKeycode;
if (m_keysymTable[inx + 1] == NoSymbol
&& ((XK_A <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_Z)
|| (XK_a <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_z)))
{
if (XK_A <= m_keysymTable[inx] && m_keysymTable[inx] <= XK_Z)
m_keysymTable[inx] = m_keysymTable[inx] - XK_A + XK_a;
m_keysymTable[inx + 1] = m_keysymTable[inx] - XK_a + XK_A;
}
}
m_altMask = 0;
m_metaMask = 0;
m_altgrMask = 0;
m_altgrKeysym = NoSymbol;
modifiers = XGetModifierMapping(m_dpy);
/* default value, used if we do not find a mapping */
inx_altgr = 3;
for (i = 0; i < 8; i++) {
for (pos = 0; pos < modifiers->max_keypermod; pos++) {
keycode = modifiers->modifiermap[i * modifiers->max_keypermod + pos];
if (keycode < m_minKeycode || m_maxKeycode < keycode) continue;
keysym = m_keysymTable[(keycode - m_minKeycode) * m_keysymPerKeycode];
if (keysym == XK_Alt_L || keysym == XK_Alt_R) {
m_altMask = 1 << i;
} else if (keysym == XK_Meta_L || keysym == XK_Meta_R) {
m_metaMask = 1 << i;
} else if (keysym == XK_Mode_switch) {
if (m_altgrKeysym == XK_ISO_Level3_Shift) {
} else {
m_altgrMask = 0x0101 << i;
/* I don't know why, but 0x2000 was required for mod3 on my Linux box */
m_altgrKeysym = keysym;
/* set the index of the XGetKeyboardMapping for the AltGr key */
inx_altgr = i;
}
} else if (keysym == XK_ISO_Level3_Shift) {
/* if no Mode_switch, try to use ISO_Level3_Shift instead */
/* however, it may not work as intended - I don't know why */
m_altgrMask = 1 << i;
m_altgrKeysym = keysym;
/* set the index of the XGetKeyboardMapping for the AltGr key */
inx_altgr = i;
}
}
}
XFreeModifiermap(modifiers);
} }
/* /*
@ -593,12 +490,13 @@ void AutoTypePlatformX11::ReadKeymap()
* If input focus is specified explicitly, select the window * If input focus is specified explicitly, select the window
* before send event to the window. * before send event to the window.
*/ */
void AutoTypePlatformX11::SendEvent(XKeyEvent* event) void AutoTypePlatformX11::SendEvent(XKeyEvent* event, int event_type)
{ {
XSync(event->display, FALSE); XSync(event->display, FALSE);
int (*oldHandler) (Display*, XErrorEvent*) = XSetErrorHandler(MyErrorHandler); int (*oldHandler) (Display*, XErrorEvent*) = XSetErrorHandler(MyErrorHandler);
XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress, 0); event->type = event_type;
XTestFakeKeyEvent(event->display, event->keycode, event->type == KeyPress , 0);
XFlush(event->display); XFlush(event->display);
XSetErrorHandler(oldHandler); XSetErrorHandler(oldHandler);
@ -609,66 +507,17 @@ void AutoTypePlatformX11::SendEvent(XKeyEvent* event)
* window to simulate keyboard. If modifiers (shift, control, etc) * window to simulate keyboard. If modifiers (shift, control, etc)
* are set ON, many events will be sent. * are set ON, many events will be sent.
*/ */
void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym, unsigned int shift) void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym)
{ {
Window cur_focus; Window cur_focus;
int revert_to; int revert_to;
XKeyEvent event; XKeyEvent event;
int keycode; int keycode;
int phase; int mod_index;
bool found;
KeySym ks;
unsigned int mods_rtrn = 0;
XkbDescPtr kbd = XkbGetKeyboard (m_dpy, XkbCompatMapMask | XkbGeometryMask, XkbUseCoreKbd);
XGetInputFocus(m_dpy, &cur_focus, &revert_to); if (keysym == NoSymbol) {
qWarning("No such key: keysym=0x%lX", static_cast<long>(keysym));
found = FALSE; return;
keycode = 0;
if (keysym != NoSymbol) {
for (phase = 0; phase < 2; phase++) {
keycode = XKeysymToKeycode(m_dpy, keysym);
XkbTranslateKeyCode(kbd, keycode, 0, &mods_rtrn, &ks);
if (ks == keysym) {
shift &= ~m_altgrMask;
shift &= ~ShiftMask;
found = TRUE;
break;
}
XkbTranslateKeyCode(kbd, keycode, ShiftMask, &mods_rtrn, &ks);
if (ks == keysym) {
shift &= ~m_altgrMask;
shift |= ShiftMask;
found = TRUE;
break;
}
XkbTranslateKeyCode(kbd, keycode, Mod5Mask, &mods_rtrn, &ks);
if (ks == keysym) {
shift &= ~ShiftMask;
shift |= m_altgrMask;
found = TRUE;
break;
}
XkbTranslateKeyCode(kbd, keycode, Mod5Mask, &mods_rtrn, &ks);
if (ks == keysym) {
shift |= ShiftMask | m_altgrMask;
found = TRUE;
break;
}
if (found) break;
if (0xF000 <= keysym) {
/* for special keys such as function keys,
first try to add it in the non-shifted position of the keymap */
if (AddKeysym(keysym, TRUE) == NoSymbol) AddKeysym(keysym, FALSE);
} else {
AddKeysym(keysym, FALSE);
}
}
} }
event.display = m_dpy; event.display = m_dpy;
@ -687,105 +536,44 @@ void AutoTypePlatformX11::SendKeyPressedEvent(KeySym keysym, unsigned int shift)
unsigned int mask; unsigned int mask;
XQueryPointer(m_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask); XQueryPointer(m_dpy, event.root, &root, &child, &root_x, &root_y, &x, &y, &mask);
XGetInputFocus(m_dpy, &cur_focus, &revert_to);
event.type = KeyRelease; /* Release all set modifiers */
event.state = 0; event.state = 0;
if (mask & ControlMask) { for (mod_index = 0; mod_index < 8; mod_index ++) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L); if (mask & m_modifier_mask[mod_index]) {
SendEvent(&event); event.keycode = m_modifier_keycode[mod_index];
} SendEvent(&event, KeyRelease);
if (mask & m_altMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
SendEvent(&event);
}
if (mask & m_metaMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
SendEvent(&event);
}
if (mask & m_altgrMask) {
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym);
SendEvent(&event);
}
if (mask & ShiftMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
SendEvent(&event);
}
if (mask & LockMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Caps_Lock);
SendEvent(&event);
}
event.type = KeyPress;
event.state = 0;
if (shift & ControlMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L);
SendEvent(&event);
event.state |= ControlMask;
}
if (shift & m_altMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
SendEvent(&event);
event.state |= m_altMask;
}
if (shift & m_metaMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
SendEvent(&event);
event.state |= m_metaMask;
}
if (shift & m_altgrMask) {
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym);
SendEvent(&event);
event.state |= m_altgrMask;
}
if (shift & ShiftMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
SendEvent(&event);
event.state |= ShiftMask;
}
if (keysym != NoSymbol) { /* send event for the key itself */
event.keycode = found ? keycode : XKeysymToKeycode(m_dpy, keysym);
if (event.keycode == NoSymbol) {
if ((keysym & ~0x7f) == 0 && QChar(static_cast<char>(keysym)).isPrint())
qWarning("No such key: %c", static_cast<char>(keysym));
else if (XKeysymToString(keysym) != NULL)
qWarning("No such key: keysym=%s (0x%lX)", XKeysymToString(keysym), static_cast<long>(keysym));
else
qWarning("No such key: keysym=0x%lX", static_cast<long>(keysym));
} else {
SendEvent(&event);
event.type = KeyRelease;
SendEvent(&event);
} }
} }
event.type = KeyRelease; /* Determine the keycode to press */
if (shift & ShiftMask) { keycode = XKeysymToKeycode(m_dpy, keysym);
/* Case 1: Pressing Shift is required */
if (XkbKeycodeToKeysym(m_dpy, keycode, 0, 1) == keysym) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L); event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
SendEvent(&event); SendEvent(&event, KeyPress);
event.state &= ~ShiftMask; event.state |= ShiftMask;
} else {
/* Case 2: Obtaining a mapping is required */
if (XkbKeycodeToKeysym(m_dpy, keycode, 0, 0) != keysym) {
keycode = AddKeysym(keysym);
}
/* Default Case: No shift or Mapping required */
} }
if (shift & m_altgrMask) {
event.keycode = XKeysymToKeycode(m_dpy, m_altgrKeysym); /* Press and release key */
SendEvent(&event); event.keycode = keycode;
event.state &= ~m_altgrMask; SendEvent(&event, KeyPress);
SendEvent(&event, KeyRelease);
/* Release Shift, if it has been pressed */
if (event.state & ShiftMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Shift_L);
SendEvent(&event, KeyRelease);
event.state &= ShiftMask;
} }
if (shift & m_metaMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Meta_L);
SendEvent(&event);
event.state &= ~m_metaMask;
}
if (shift & m_altMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Alt_L);
SendEvent(&event);
event.state &= ~m_altMask;
}
if (shift & ControlMask) {
event.keycode = XKeysymToKeycode(m_dpy, XK_Control_L);
SendEvent(&event);
event.state &= ~ControlMask;
}
XkbFreeKeyboard(kbd, XkbAllComponentsMask, True);
} }
int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event) int AutoTypePlatformX11::MyErrorHandler(Display* my_dpy, XErrorEvent* event)

View File

@ -26,13 +26,16 @@
#include <QX11Info> #include <QX11Info>
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XTest.h> #include <X11/extensions/XTest.h>
#include <X11/XKBlib.h>
#include <unistd.h>
#include "autotype/AutoTypePlatformPlugin.h" #include "autotype/AutoTypePlatformPlugin.h"
#include "autotype/AutoTypeAction.h" #include "autotype/AutoTypeAction.h"
#include "core/Global.h" #include "core/Global.h"
#define N_MOD_INDICES (Mod5MapIndex + 1)
class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
{ {
Q_OBJECT Q_OBJECT
@ -40,6 +43,7 @@ class AutoTypePlatformX11 : public QObject, public AutoTypePlatformInterface
public: public:
AutoTypePlatformX11(); AutoTypePlatformX11();
~AutoTypePlatformX11();
QStringList windowTitles(); QStringList windowTitles();
WId activeWindow(); WId activeWindow();
QString activeWindowTitle(); QString activeWindowTitle();
@ -52,7 +56,7 @@ public:
KeySym charToKeySym(const QChar& ch); KeySym charToKeySym(const QChar& ch);
KeySym keyToKeySym(Qt::Key key); KeySym keyToKeySym(Qt::Key key);
void SendKeyPressedEvent(KeySym keysym, unsigned int shift = 0); void SendKeyPressedEvent(KeySym keysym);
Q_SIGNALS: Q_SIGNALS:
void globalShortcutTriggered(); void globalShortcutTriggered();
@ -69,10 +73,10 @@ private:
static int x11ErrorHandler(Display* display, XErrorEvent* error); static int x11ErrorHandler(Display* display, XErrorEvent* error);
void updateKeymap(); void updateKeymap();
int AddKeysym(KeySym keysym, bool top); int AddKeysym(KeySym keysym);
void AddModifier(KeySym keysym); void AddModifier(KeySym keysym);
void ReadKeymap(); void ReadKeymap();
void SendEvent(XKeyEvent* event); void SendEvent(XKeyEvent* event, int event_type);
static int MyErrorHandler(Display* my_dpy, XErrorEvent* event); static int MyErrorHandler(Display* my_dpy, XErrorEvent* event);
Display* m_dpy; Display* m_dpy;
@ -96,12 +100,11 @@ private:
int m_minKeycode; int m_minKeycode;
int m_maxKeycode; int m_maxKeycode;
int m_keysymPerKeycode; int m_keysymPerKeycode;
int m_altMask;
int m_metaMask; /* dedicated 'special character' keycode */
int m_altgrMask; int m_specialCharacterKeycode;
/* index of the XGetKeyboardMapping for the AltGr key */ int m_modifier_mask[N_MOD_INDICES];
int inx_altgr; KeyCode m_modifier_keycode[N_MOD_INDICES];
KeySym m_altgrKeysym;
}; };
class AutoTypeExecturorX11 : public AutoTypeExecutor class AutoTypeExecturorX11 : public AutoTypeExecutor