diff options
Diffstat (limited to 'src/input/x11FakeKeyboardHandler.cpp')
-rw-r--r-- | src/input/x11FakeKeyboardHandler.cpp | 877 |
1 files changed, 877 insertions, 0 deletions
diff --git a/src/input/x11FakeKeyboardHandler.cpp b/src/input/x11FakeKeyboardHandler.cpp new file mode 100644 index 0000000..f27ea1e --- /dev/null +++ b/src/input/x11FakeKeyboardHandler.cpp @@ -0,0 +1,877 @@ +/* + # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg + # + # This program is free software distributed under the GPL version 2. + # See http://openslx.org/COPYING + # + # If you have any feedback please consult http://openslx.org/feedback and + # send your suggestions, praise, or complaints to feedback@openslx.org + # + # General information about OpenSLX can be found at http://openslx.org/ + # -------------------------------------------------------------------------- + # x11FakeKeyboardHandler.cpp: + # - Handle keyboard events on X11 - interface + # -------------------------------------------------------------------------- + */ + +#include <map> +#include <set> +#include <cassert> +// Qt headers need to be included before X11 headers +#include <QApplication> +#include <QtCore> +#include "x11FakeKeyboardHandler.h" +// #include <multimap> +#include <X11/X.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +#include <X11/extensions/XTest.h> +#ifdef HAVE_XINPUT2_H +# include <X11/extensions/XInput2.h> +#else +# include <X11/extensions/XInput.h> +#endif +#include <X11/XKBlib.h> +#include <src/util/consoleLogger.h> +#include "x11InputUtils.h" + +// X11 supports 8 modifiers, so a bitmask of modifiers fits into a byte: +typedef unsigned char XModifiers; + +// For debugging output, these are the names of modifier keys: +char modifiernames[][8] = { + "SHIFT", + "LOCK", + "CONTROL", + "MOD1", + "MOD2", + "MOD3", + "MOD4", + "MOD5" +}; + +// We generally want to find a key sequence with as few modifiers as +// possible. These are all modifier combinations sorted by number of +// bits set: +char modifierpriority[256] = { + 0, 1, 2, 4, 8, 16, 32, 64, 128, + 3, 5, 6, 9, 10, 12, 17, 18, + 20, 24, 33, 34, 36, 40, 48, 65, + 66, 68, 72, 80, 96, 129, 130, 132, + 136, 144, 160, 192, 7, 11, 13, 14, + 19, 21, 22, 25, 26, 28, 35, 37, + 38, 41, 42, 44, 49, 50, 52, 56, + 67, 69, 70, 73, 74, 76, 81, 82, + 84, 88, 97, 98, 100, 104, 112, 131, + 133, 134, 137, 138, 140, 145, 146, 148, + 152, 161, 162, 164, 168, 176, 193, 194, + 196, 200, 208, 224, 15, 23, 27, 29, + 30, 39, 43, 45, 46, 51, 53, 54, + 57, 58, 60, 71, 75, 77, 78, 83, + 85, 86, 89, 90, 92, 99, 101, 102, + 105, 106, 108, 113, 114, 116, 120, 135, + 139, 141, 142, 147, 149, 150, 153, 154, + 156, 163, 165, 166, 169, 170, 172, 177, + 178, 180, 184, 195, 197, 198, 201, 202, + 204, 209, 210, 212, 216, 225, 226, 228, + 232, 240, 31, 47, 55, 59, 61, 62, + 79, 87, 91, 93, 94, 103, 107, 109, + 110, 115, 117, 118, 121, 122, 124, 143, + 151, 155, 157, 158, 167, 171, 173, 174, + 179, 181, 182, 185, 186, 188, 199, 203, + 205, 206, 211, 213, 214, 217, 218, 220, + 227, 229, 230, 233, 234, 236, 241, 242, + 244, 248, 63, 95, 111, 119, 123, 125, + 126, 159, 175, 183, 187, 189, 190, 207, + 215, 219, 221, 222, 231, 235, 237, 238, + 243, 245, 246, 249, 250, 252, 127, 191, + 223, 239, 247, 251, 253, 254, 255 +}; + +QString modifiersToString(XModifiers mods) +{ + QString s; + for(int i = 0; i < 8; i++) + { + if(mods & (1<<i)) + { + s += modifiernames[i]; + s += ", "; + } + } + s.chop(2); + return s; +} + +///////////////////////////////////////////////////////////////////////// +// Input event translation code +///////////////////////////////////////////////////////////////////////// + +// We need to translate between Qt's keycodes and X11's keycodes. +// Unfortunately, there does not seem to be a direct correspondence +// between them, so we are stuck with storing a lookup table: +typedef std::map<quint32, KeySym> KeycodeLookupTable; +KeycodeLookupTable keysyms; + +// The following code has been partially autogenerated by +// an insane sed(1) script and then hand-edited to make +// the errors go away. +void initializeKeycodeLookupTable() { + keysyms[Qt::Key_Escape] = XK_Escape; + keysyms[Qt::Key_Tab] = XK_Tab; + keysyms[Qt::Key_Backtab] = XK_ISO_Left_Tab; + keysyms[Qt::Key_Backspace] = XK_BackSpace; + keysyms[Qt::Key_Return] = XK_Return; + keysyms[Qt::Key_Enter] = XK_KP_Enter; + keysyms[Qt::Key_Insert] = XK_Insert; + keysyms[Qt::Key_Delete] = XK_Delete; + keysyms[Qt::Key_Pause] = XK_Pause; + keysyms[Qt::Key_Print] = XK_Print; + keysyms[Qt::Key_SysReq] = XK_Sys_Req; + keysyms[Qt::Key_Clear] = XK_Clear; + keysyms[Qt::Key_Home] = XK_Home; + keysyms[Qt::Key_End] = XK_End; + keysyms[Qt::Key_Left] = XK_Left; + keysyms[Qt::Key_Up] = XK_Up; + keysyms[Qt::Key_Right] = XK_Right; + keysyms[Qt::Key_Down] = XK_Down; + keysyms[Qt::Key_PageUp] = XK_Page_Up; + keysyms[Qt::Key_PageDown] = XK_Page_Down; + keysyms[Qt::Key_Shift] = XK_Shift_L; + keysyms[Qt::Key_Control] = XK_Control_L; + keysyms[Qt::Key_Meta] = XK_Meta_L; + keysyms[Qt::Key_Alt] = XK_Alt_L; + keysyms[Qt::Key_CapsLock] = XK_Caps_Lock; + keysyms[Qt::Key_NumLock] = XK_Num_Lock; + keysyms[Qt::Key_ScrollLock] = XK_Scroll_Lock; + keysyms[Qt::Key_F1] = XK_F1; + keysyms[Qt::Key_F2] = XK_F2; + keysyms[Qt::Key_F3] = XK_F3; + keysyms[Qt::Key_F4] = XK_F4; + keysyms[Qt::Key_F5] = XK_F5; + keysyms[Qt::Key_F6] = XK_F6; + keysyms[Qt::Key_F7] = XK_F7; + keysyms[Qt::Key_F8] = XK_F8; + keysyms[Qt::Key_F9] = XK_F9; + keysyms[Qt::Key_F10] = XK_F10; + keysyms[Qt::Key_F11] = XK_F11; + keysyms[Qt::Key_F12] = XK_F12; + keysyms[Qt::Key_F13] = XK_F13; + keysyms[Qt::Key_F14] = XK_F14; + keysyms[Qt::Key_F15] = XK_F15; + keysyms[Qt::Key_F16] = XK_F16; + keysyms[Qt::Key_F17] = XK_F17; + keysyms[Qt::Key_F18] = XK_F18; + keysyms[Qt::Key_F19] = XK_F19; + keysyms[Qt::Key_F20] = XK_F20; + keysyms[Qt::Key_F21] = XK_F21; + keysyms[Qt::Key_F22] = XK_F22; + keysyms[Qt::Key_F23] = XK_F23; + keysyms[Qt::Key_F24] = XK_F24; + keysyms[Qt::Key_F25] = XK_F25; + keysyms[Qt::Key_F26] = XK_F26; + keysyms[Qt::Key_F27] = XK_F27; + keysyms[Qt::Key_F28] = XK_F28; + keysyms[Qt::Key_F29] = XK_F29; + keysyms[Qt::Key_F30] = XK_F30; + keysyms[Qt::Key_F31] = XK_F31; + keysyms[Qt::Key_F32] = XK_F32; + keysyms[Qt::Key_F33] = XK_F33; + keysyms[Qt::Key_F34] = XK_F34; + keysyms[Qt::Key_F35] = XK_F35; + keysyms[Qt::Key_Super_L] = XK_Super_L; + keysyms[Qt::Key_Super_R] = XK_Super_R; + keysyms[Qt::Key_Menu] = XK_Menu; + keysyms[Qt::Key_Hyper_L] = XK_Hyper_L; + keysyms[Qt::Key_Hyper_R] = XK_Hyper_R; + keysyms[Qt::Key_Help] = XK_Help; + + // Latin1 symbols do directly map to + // Keycodes both in Qt and in X11: + for(int i = 0x20; i < 0x100; i++) + { + keysyms[i] = i; + } + + keysyms[Qt::Key_AltGr] = XK_ISO_Level3_Shift; + keysyms[Qt::Key_Multi_key] = XK_Multi_key; + keysyms[Qt::Key_Codeinput] = XK_Codeinput; + keysyms[Qt::Key_SingleCandidate] = XK_SingleCandidate; + keysyms[Qt::Key_MultipleCandidate] = XK_MultipleCandidate; + keysyms[Qt::Key_PreviousCandidate] = XK_PreviousCandidate; + keysyms[Qt::Key_Mode_switch] = XK_Mode_switch; + keysyms[Qt::Key_Kanji] = XK_Kanji; + keysyms[Qt::Key_Muhenkan] = XK_Muhenkan; + keysyms[Qt::Key_Henkan] = XK_Henkan; + keysyms[Qt::Key_Romaji] = XK_Romaji; + keysyms[Qt::Key_Hiragana] = XK_Hiragana; + keysyms[Qt::Key_Katakana] = XK_Katakana; + keysyms[Qt::Key_Hiragana_Katakana] = XK_Hiragana_Katakana; + keysyms[Qt::Key_Zenkaku] = XK_Zenkaku; + keysyms[Qt::Key_Hankaku] = XK_Hankaku; + keysyms[Qt::Key_Zenkaku_Hankaku] = XK_Zenkaku_Hankaku; + keysyms[Qt::Key_Touroku] = XK_Touroku; + keysyms[Qt::Key_Massyo] = XK_Massyo; + keysyms[Qt::Key_Kana_Lock] = XK_Kana_Lock; + keysyms[Qt::Key_Kana_Shift] = XK_Kana_Shift; + keysyms[Qt::Key_Eisu_Shift] = XK_Eisu_Shift; + keysyms[Qt::Key_Eisu_toggle] = XK_Eisu_toggle; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul] = XK_Hangul; + keysyms[Qt::Key_Hangul_Start] = XK_Hangul_Start; + keysyms[Qt::Key_Hangul_End] = XK_Hangul_End; + keysyms[Qt::Key_Hangul_Hanja] = XK_Hangul_Hanja; + keysyms[Qt::Key_Hangul_Jamo] = XK_Hangul_Jamo; + keysyms[Qt::Key_Hangul_Romaja] = XK_Hangul_Romaja; + keysyms[Qt::Key_Hangul_Jeonja] = XK_Hangul_Jeonja; + keysyms[Qt::Key_Hangul_Banja] = XK_Hangul_Banja; + keysyms[Qt::Key_Hangul_PreHanja] = XK_Hangul_PreHanja; + keysyms[Qt::Key_Hangul_PostHanja] = XK_Hangul_PostHanja; + keysyms[Qt::Key_Hangul_Special] = XK_Hangul_Special; + keysyms[Qt::Key_Dead_Grave] = XK_dead_grave; + keysyms[Qt::Key_Dead_Acute] = XK_dead_acute; + keysyms[Qt::Key_Dead_Circumflex] = XK_dead_circumflex; + keysyms[Qt::Key_Dead_Tilde] = XK_dead_tilde; + keysyms[Qt::Key_Dead_Macron] = XK_dead_macron; + keysyms[Qt::Key_Dead_Breve] = XK_dead_breve; + keysyms[Qt::Key_Dead_Abovedot] = XK_dead_abovedot; + keysyms[Qt::Key_Dead_Diaeresis] = XK_dead_diaeresis; + keysyms[Qt::Key_Dead_Abovering] = XK_dead_abovering; + keysyms[Qt::Key_Dead_Doubleacute] = XK_dead_doubleacute; + keysyms[Qt::Key_Dead_Caron] = XK_dead_caron; + keysyms[Qt::Key_Dead_Cedilla] = XK_dead_cedilla; + keysyms[Qt::Key_Dead_Ogonek] = XK_dead_ogonek; + keysyms[Qt::Key_Dead_Iota] = XK_dead_iota; + keysyms[Qt::Key_Dead_Voiced_Sound] = XK_dead_voiced_sound; + keysyms[Qt::Key_Dead_Semivoiced_Sound] = XK_dead_semivoiced_sound; + keysyms[Qt::Key_Dead_Belowdot] = XK_dead_belowdot; + keysyms[Qt::Key_Dead_Hook] = XK_dead_hook; + keysyms[Qt::Key_Dead_Horn] = XK_dead_horn; + keysyms[Qt::Key_Select] = XK_Select; + keysyms[Qt::Key_Cancel] = XK_Cancel; + keysyms[Qt::Key_Execute] = XK_Execute; + keysyms[Qt::Key_unknown] = XK_VoidSymbol; +} + +// We need to store how to generate KeySyms: +struct KeySymInfo +{ + KeyCode keycode; + XModifiers neededModifiers; + XModifiers modifierMask; +}; +typedef std::map<KeySym, KeySymInfo> KeySymToInfoMap; +KeySymToInfoMap keysymInfos; + +// What keys do we need to press to generate the +// modifiers? +int modifierKeycodes[8]; +// How do the X11 modifiers relate to Qt's KeyboardModifiers? +Qt::KeyboardModifier modifierMeaning[8]; + +// How do the X11 modifiers correspond to Qt's KeyboardModifiers? +typedef std::map<XModifiers, Qt::KeyboardModifier> XToQtModifierMap; +XToQtModifierMap XToQtModifier; +typedef std::map<Qt::KeyboardModifier, XModifiers> QtToXModifierMap; +QtToXModifierMap QtToXModifier; + + +// And how do the modifiers relate to Keycodes? +typedef std::multimap<XModifiers, int> ModifierToKeycodeMap; +ModifierToKeycodeMap ModifierToKeycode; +typedef std::map<int, XModifiers> KeycodeToModifierMap; +KeycodeToModifierMap KeycodeToModifier; + +#ifdef HAVE_XKBLIB_H +void recordModifierMapping(XkbDescPtr keybDesc, int keycode, Qt::KeyboardModifier qtMod) +{ + XModifiers rmod = 0; + if(QtToXModifier.find(qtMod) == QtToXModifier.end()) + { + for(int i = 1; i < 0x100; i <<= 1) + { + if(keybDesc->map->modmap[keycode] == i) + { + rmod = i; + XToQtModifier[rmod] = qtMod; + QtToXModifier[qtMod] = rmod; + } + } + } + else + { + rmod = QtToXModifier[qtMod]; + } + + if(rmod) + { + ModifierToKeycode.insert(std::make_pair(rmod, keycode)); + KeycodeToModifier[keycode] = rmod; + ConsoleLog writeLine(QString("%1 %2 %3 ... generates modifier %4").arg(keycode, 3).arg("", 40).arg("", 40).arg(rmod, 2, 16)); + } +} +#endif + +// We need to query the input devices, preferrable through XInput2, but +// if that is not available we will contend ourselves with XInput1: +#ifndef HAVE_XINPUT2_H +# define XIAllDevices 1 /* does not matter */ +# define XIDeviceInfo XDeviceInfo +# define XIQueryDevice(dpy, which, ninfos) XListInputDevices(dpy, ninfos) +# define XIFreeDeviceInfo(infos) XFreeDeviceList(infos) +# define XIMasterKeyboard IsXKeyboard +# define XISlaveKeyboard IsXExtensionKeyboard + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + if(devinfo->use == IsXKeyboard) + { + // According to XOpenDevice(3X11) you cannot open the Core Keyboard. + *nprops = 0; + return 0; + } + + XDevice* dev = XOpenDevice(dpy, devinfo->id); + Atom* props = XListDeviceProperties(dpy, dev, nprops); + XCloseDevice(dpy, dev); + return props; + } +#else + static inline Atom* getDeviceProperties(Display* dpy, XIDeviceInfo* devinfo, int* nprops) + { + return XIListProperties(dpy, devinfo->deviceid, nprops); + } +#endif + +// Initialize the above data structures: +void initializeBasicKeycodes() +{ + // We temporarily need a list of all known KeySyms: + typedef std::set<KeySym> KeySymSet; + KeySymSet knownKeySyms; + + for(KeycodeLookupTable::const_iterator kcIter = keysyms.begin(); + kcIter != keysyms.end(); + kcIter++) + { + knownKeySyms.insert((*kcIter).second); + } + + // Mark everything as unknown initially + for(int i = 0; i < 8; i++) { + modifierKeycodes[i] = -1; + } + keysymInfos.clear(); + + Display* dpy = X11InputUtils::display(); + + // Find out the valid range of keycodes + int minKeycode, maxKeycode; + XDisplayKeycodes(dpy, &minKeycode, &maxKeycode); + + // Initialize the XKB client-side code, and find out whether + // the XKB extension is present in the server. + int xkbOpcode, xkbEvent, xkbError, xkbMajor, xkbMinor; +#ifdef HAVE_XKBLIB_H + bool xkbPresent = XkbQueryExtension(dpy, &xkbOpcode, &xkbEvent, &xkbError, &xkbMajor, &xkbMinor); +#else + bool xkbPresent = false; +#endif + + if(!xkbPresent) { + // No XKB. This is probably not a very recent + // system, but we will do the best we can. + // Retrieve the keyboard mapping + int keysymsPerCode; + const int count = maxKeycode - minKeycode + 1; + KeySym* mapping = XGetKeyboardMapping(dpy, minKeycode, count, &keysymsPerCode); + + // and traverse every entry + for(int i = 0; i < count; i++) + { + for(int j = 0; j < 0x100; j++) + { + const XModifiers mods = modifierpriority[j]; + if(mods >= keysymsPerCode) + continue; + + const int idx = i * keysymsPerCode + mods; + const KeySym ks = mapping[idx]; + const int keycode = minKeycode + i; + + // to find out if there is a KeySym there that we know about + if(knownKeySyms.find(ks) != knownKeySyms.end()) + { + // that we have not yet found, + if(keysymInfos.find(ks) != keysymInfos.end()) + continue; // already found + + // and record its keycode and needed modifiers. + KeySymInfo info; + info.keycode = keycode; + info.neededModifiers = mods; + info.modifierMask = mods; + keysymInfos[ks] = info; + } + } + } + + // We are finished, free the mapping structure. + XFree(mapping); + + // find out which keycodes cause the modifier state bits: + XModifierKeymap* modkm; + modkm = XGetModifierMapping(dpy); + + const int shiftCode = XKeysymToKeycode(dpy, XK_Shift_L); + const int controlCode = XKeysymToKeycode(dpy, XK_Control_L); + const int altCode = XKeysymToKeycode(dpy, XK_Alt_L); + const int metaCode = XKeysymToKeycode(dpy, XK_Meta_L); + const int switchCode = XKeysymToKeycode(dpy, XK_Mode_switch); + + // and use this information to find out which + // X11 modifiers correspond to Qt's modifiers: + for(int i = 0; i < 8; i++) { + for(int j = 0; j < modkm->max_keypermod; j++) { + const int idx = i * modkm->max_keypermod + j; + const int kc = modkm->modifiermap[idx]; + + ModifierToKeycode.insert(std::make_pair((1<<i), kc)); + KeycodeToModifier[kc] = (1<<i); + if(kc == shiftCode) { + XToQtModifier[1<<i] = Qt::ShiftModifier; + } else if(kc == controlCode) { + XToQtModifier[1<<i] = Qt::ControlModifier; + } else if(kc == altCode) { + XToQtModifier[1<<i] = Qt::AltModifier; + } else if(kc == metaCode) { + XToQtModifier[1<<i] = Qt::MetaModifier; + } else if(kc == switchCode) { + XToQtModifier[1<<i] = Qt::GroupSwitchModifier; + } + + // and record the keycode we need to press/release + // to activate or deactivate a modifier: + if(modifierKeycodes[i] != -1) { + continue; // already found + } + + // select one arbitrarily + modifierKeycodes[i] = kc; + } + } + + XFreeModifiermap(modkm); + } +#ifdef HAVE_XKBLIB_H + else + { + // XKB is present. + + // As different input devices can have different keymaps, + // we need to find out what device XTest will send events on: + unsigned int xkbDeviceId = XkbUseCoreKbd; + + + // Is XInput present? +#if defined(HAVE_XINPUT_H) || defined(HAVE_XINPUT2_H) + // We don't care about these: + int i1, i2, i3; + bool haveXInput = XQueryExtension(dpy, "XInputExtension", &i1, &i2, &i3); + + if(haveXInput) + { + Atom xtestDeviceProp = XInternAtom(dpy, "XTEST Device", false); + + // Find the list of input devices: + int ndevinfos; + XIDeviceInfo* devinfos = XIQueryDevice(dpy, XIAllDevices, &ndevinfos); + if(devinfos) + { +#ifndef HAVE_XINPUT2_H +# define deviceid id +#endif + for(int i = 0; i < ndevinfos && xkbDeviceId == XkbUseCoreKbd; i++) + { + XIDeviceInfo* devinfo = devinfos + i; + qDebug("Found device %lu of type %d with name %s", (unsigned long)devinfo->deviceid, devinfo->use, devinfo->name); + + // We want it to be a keyboard. + if(devinfo->use != XIMasterKeyboard && devinfo->use != XISlaveKeyboard) + continue; + + int nprops; + Atom* props = getDeviceProperties(dpy, devinfo, &nprops); + if(props) + { + for(int j = 0; j < nprops && xkbDeviceId == XkbUseCoreKbd; j++) + { + Atom prop = props[j]; + if(prop == xtestDeviceProp) + { + // The device is the XTest Keyboard: + xkbDeviceId = devinfo->deviceid; + } + } + XFree(props); + } + } + XIFreeDeviceInfo(devinfos); +#ifdef deviceid /* XInput1 */ +# undef deviceid +#endif + } + } +#endif /* HAVE_XINPUT_H || HAVE_XINPUT2_H */ + + // at this point, xkbDeviceId contains the identifier + // of the XTEST Device, or xkbUseCoreKbd if none was + // found. + + // Okay, we know which device to query. Now get its keymap: + XkbDescPtr keybDesc = XkbGetKeyboard(dpy, XkbAllComponentsMask, xkbDeviceId); + if(!keybDesc) + { + qWarning("Unable to retrieve keyboard description for device %d. Falling back to unreliable global mapping", xkbDeviceId); + } + + // This is basically the same loop as above, it just queries XKB + // instead of the old core Xlib routines. + for(int i = minKeycode; i <= maxKeycode; i++) + { + for(int j = 0; j <= 0xff; j++) + { + XModifiers mods = modifierpriority[j]; + KeySym ks = 0; + unsigned int unconsumed; + if(keybDesc) + { + if(!XkbTranslateKeyCode(keybDesc, i, mods, &unconsumed, &ks) || ks == NoSymbol) + continue; + } + else + { + if(!XkbLookupKeySym(dpy, i, mods, &unconsumed, &ks) || ks == NoSymbol) + continue; + } + + if(knownKeySyms.find(ks) != knownKeySyms.end()) + { + if(mods & ~unconsumed) + { + // we would be recording extraneous modifiers + continue; + } + + if(keysymInfos.find(ks) != keysymInfos.end()) + continue; + + KeySymInfo info; + info.keycode = i; + info.neededModifiers = mods & unconsumed; + info.modifierMask = unconsumed; + keysymInfos[ks] = info; + + ConsoleLog writeLine(QString("%1 %2 %3 %4") + .arg(i, 3) + .arg(modifiersToString(info.neededModifiers), 40) + .arg(modifiersToString(info.modifierMask), 40) + .arg(XKeysymToString(ks))); + } + + if(j == 0) + { + switch(ks) + { + case XK_Shift_L: + case XK_Shift_R: + recordModifierMapping(keybDesc, i, Qt::ShiftModifier); + break; + case XK_Control_L: + case XK_Control_R: + recordModifierMapping(keybDesc, i, Qt::ControlModifier); + break; + case XK_Meta_L: + case XK_Meta_R: + recordModifierMapping(keybDesc, i, Qt::MetaModifier); + break; + case XK_Alt_L: + case XK_Alt_R: + recordModifierMapping(keybDesc, i, Qt::AltModifier); + break; + case XK_ISO_Level3_Shift: + recordModifierMapping(keybDesc, i, Qt::GroupSwitchModifier); + break; + } + } + } + } + + // Free the keyboard description: + if(keybDesc) + { + XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true); + } + } +#endif /* HAVE_XKBLIB_H */ + + ConsoleLog writeLine("After initialization, XToQtModifier has the following entries:"); + for(XToQtModifierMap::const_iterator i = XToQtModifier.begin(); + i != XToQtModifier.end(); + i++) + { + ConsoleLog writeLine(QString(" %1 %2").arg(i->first, 2, 16).arg(i->second, 8, 16)); + } +} + +// Translate a Qt KeyboardModifiers flag to +// its corresponding X11 modifier bitmask +XModifiers translateModifiers(quint32 mods) +{ + XModifiers ret = 0; + + for(int j = 0; j < 8; j++) { + XModifiers i = 1<<j; + + if(mods & XToQtModifier[i]) + { + ret |= i; + } + } + + return ret; +} + +// We need to keep track of which modifiers +// are currently active +typedef std::set<int> IntSet; +IntSet pressedModifierKeys; +XModifiers currentModifiers = 0; + +void trackModifiers(int keycode, bool down) +{ + // is this a modifier key? + const bool isModifier = KeycodeToModifier.find(keycode) != KeycodeToModifier.end(); + + if(!isModifier) + { + return; + } + + if(down) { + pressedModifierKeys.insert(keycode); + } else { + pressedModifierKeys.erase(keycode); + } + + IntSet::iterator i, end = pressedModifierKeys.end(); + XModifiers modifs = 0; + for(i = pressedModifierKeys.begin(); i != end; i++) + { + KeycodeToModifierMap::iterator foundCode = KeycodeToModifier.find(*i); + if(foundCode != KeycodeToModifier.end()) + { + modifs |= (*foundCode).second; + } + } + currentModifiers = modifs; + + ConsoleLog writeLine(QString("[trackModifiers] current modifiers: %1").arg(modifiersToString(modifs))); +} + +// And, if we need to tweak the modifiers to generate +// a particular keysym, we need to keep track of which +// ones we pressed or released. +typedef std::pair<int, bool> ModifierTweak; +typedef std::vector<ModifierTweak> TweakSequence; + +// Tweak the modifiers to reach the neededState, from actualState +// and record tweaks in the tracker. +void tweakModifiers(Display* dpy, XModifiers neededState, XModifiers actualState, TweakSequence& tracker) +{ + if(neededState == actualState) + // nothing to do. + return; + + ConsoleLog writeLine(QString("tweakModifiers: Trying to get to `%1' from `%2'").arg(modifiersToString(neededState)).arg(modifiersToString(actualState))); + for(int j = 0; j < 8; j++) + { + XModifiers i = 1<<j; + + // Do we need to activate the modifier? + if((i & neededState) && !(i & actualState)) + { + ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be pressed").arg(modifiernames[j])); + + //find the keycode + ModifierToKeycodeMap::iterator iter = ModifierToKeycode.find(i); + + if((iter == ModifierToKeycode.end()) || ((*iter).first != i)) + { + // we don't know a key that triggers this modifier + continue; + } + + XTestFakeKeyEvent(dpy, (*iter).second, 1, CurrentTime); + + // record the tweak + tracker.push_back(std::make_pair((*iter).second, true)); + } + // or do we need to deactivate it? + else if((!(i & neededState)) && (i & actualState)) + { + ConsoleLog writeLine(QString("tweakModifiers: Modifier %1 needs to be released").arg(modifiernames[j])); + + int kc = -1; + + // first, check whether any of the currently pressed keys has triggered this modifier: + IntSet::iterator iter, end = pressedModifierKeys.end(); + for(iter = pressedModifierKeys.begin(); iter != end; iter++) + { + KeycodeToModifierMap::iterator modmapIter = KeycodeToModifier.find(*iter); + if(modmapIter != KeycodeToModifier.end()) { + if(modmapIter->second == i) { + kc = *iter; + + // release this key: + XTestFakeKeyEvent(dpy, kc, 0, CurrentTime); + tracker.push_back(std::make_pair(kc, false)); + } + } + } + + if(kc == -1) { + // strange, but we need to release some other key: + // we don't know which one, so we abort this and hope for the best + continue; + } + } + } +} + +// Undo a recorded sequence of modifier tweaks +void untweakModifiers(Display* dpy, TweakSequence& tracker) +{ + TweakSequence::reverse_iterator i, end = tracker.rend(); + for(i = tracker.rbegin(); i != end; i++) { + ModifierTweak& t = *i; + XTestFakeKeyEvent(dpy, t.first, !t.second, CurrentTime); + } +} + +// initialize the handler +void X11FakeKeyboardHandler::initialize() +{ + initializeKeycodeLookupTable(); + initializeBasicKeycodes(); + pressedModifierKeys.clear(); + currentModifiers = 0; +} + +// actually try our best to generate the correct input sequence +// for the event +void X11FakeKeyboardHandler::doHandle(InputEvent const& evt, InputEventContext const*) +{ + Display* dpy = X11InputUtils::display(); + + XTestGrabControl(dpy, 1); + + // find out which keysym caused this event: + KeycodeLookupTable::const_iterator i = keysyms.find(evt.qtKeysym()); + if(i == keysyms.end()) { + // Special cases. We don't know how to directly translate those, so we will try to emulate them. + ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qtKeysym(), 8, 16)); + } else { + KeySym ks = (*i).second; + + KeySymToInfoMap::const_iterator infoIter = keysymInfos.find(ks); + if(infoIter != keysymInfos.end()) + { + KeySymInfo info = infoIter->second; + + if(evt.isPress()) + { + QString format = "Trying to press the key for %1 with modifiers %2 (X: %3, Qt: %4), while current modifiers are %5 and needed are %6 with mask %7"; + if(ks >= ' ' && ks < 0x100) + { + ConsoleLog writeLine(format + .arg((char)ks) + .arg(modifiersToString(translateModifiers(evt.qtModifiers()))) + .arg(translateModifiers(evt.qtModifiers()), 2, 16) + .arg(evt.qtModifiers(), 8, 16) + .arg(modifiersToString(currentModifiers)) + .arg(modifiersToString(info.neededModifiers)) + .arg(modifiersToString(info.modifierMask))); + } + else + { + ConsoleLog writeLine(format + .arg(XKeysymToString(ks)) + .arg(modifiersToString(translateModifiers(evt.qtModifiers()))) + .arg(translateModifiers(evt.qtModifiers()), 2, 16) + .arg(evt.qtModifiers(), 8, 16) + .arg(modifiersToString(currentModifiers)) + .arg(modifiersToString(info.neededModifiers)) + .arg(modifiersToString(info.modifierMask))); + } + + // what modifier keys do we need to press? + XModifiers mods = translateModifiers(evt.qtModifiers()); + XModifiers needed = info.neededModifiers; + XModifiers mask = info.modifierMask; + + // This is quite ad-hoc, but we do NOT want to + // mask Alt. + // On some configurations, the F-Keys consume Alt, + // and when we AND it out, we disable Alt+Fn + // combinations. That's just wrong. + // FIXME: Are there situations + // where we actually need to keep Alt masked? + // Hint: I found none. + // If so, how do we determine that? + QtToXModifierMap::const_iterator altIter = QtToXModifier.find(Qt::AltModifier); + if(altIter != QtToXModifier.end()) + { + mask &= ~(altIter->second); + } + + // Determine which modifiers actually need to be active + XModifiers neededMods; + neededMods = (mods + & (needed | ~mask)) // Cleer the needed-clear modifiers + | (needed & mask); // Set the needed-set modifiers + + // Modifiers need to be tracked BEFORE tweaking, as we may be + // pressing a modifier key, and we don't want to release it + // when untweaking. + trackModifiers(info.keycode, true); + + // now, tweak the modifiers + TweakSequence tweaks; + tweakModifiers(dpy, neededMods, currentModifiers, tweaks); + + // press the key: + XTestFakeKeyEvent(dpy, info.keycode, 1, CurrentTime); + + // and release the modifiers: + untweakModifiers(dpy, tweaks); + } + else + { + // just release the key. + XTestFakeKeyEvent(dpy, info.keycode, 0, CurrentTime); + trackModifiers(info.keycode, false); + } + } + else + { + ConsoleLog writeLine(QString("No keycode is mapped to `%1'").arg(XKeysymToString(ks))); + } + } + + XTestGrabControl(dpy, 0); + + // Since there may not be a mainloop running, we need to manually flush the event queue + XFlush(dpy); +} |