/* # 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 #include #include // Qt headers need to be included before X11 headers #include #include #include "x11FakeKeyboardHandler.h" // #include #include #include #include #include #include #ifdef HAVE_XINPUT2_H # include #else # include #endif #include #include #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< 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 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 XToQtModifierMap; XToQtModifierMap XToQtModifier; typedef std::map QtToXModifierMap; QtToXModifierMap QtToXModifier; // And how do the modifiers relate to Keycodes? typedef std::multimap ModifierToKeycodeMap; ModifierToKeycodeMap ModifierToKeycode; typedef std::map 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 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<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< 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 ModifierTweak; typedef std::vector 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<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); }