/* # 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" }; 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_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; keysyms[Qt::Key_Space] = XK_space; keysyms[Qt::Key_Exclam] = XK_exclam; keysyms[Qt::Key_QuoteDbl] = XK_quotedbl; keysyms[Qt::Key_NumberSign] = XK_numbersign; keysyms[Qt::Key_Dollar] = XK_dollar; keysyms[Qt::Key_Percent] = XK_percent; keysyms[Qt::Key_Ampersand] = XK_ampersand; keysyms[Qt::Key_Apostrophe] = XK_apostrophe; keysyms[Qt::Key_ParenLeft] = XK_parenleft; keysyms[Qt::Key_ParenRight] = XK_parenright; keysyms[Qt::Key_Asterisk] = XK_asterisk; keysyms[Qt::Key_Plus] = XK_plus; keysyms[Qt::Key_Comma] = XK_comma; keysyms[Qt::Key_Minus] = XK_minus; keysyms[Qt::Key_Period] = XK_period; keysyms[Qt::Key_Slash] = XK_slash; keysyms[Qt::Key_0] = XK_0; keysyms[Qt::Key_1] = XK_1; keysyms[Qt::Key_2] = XK_2; keysyms[Qt::Key_3] = XK_3; keysyms[Qt::Key_4] = XK_4; keysyms[Qt::Key_5] = XK_5; keysyms[Qt::Key_6] = XK_6; keysyms[Qt::Key_7] = XK_7; keysyms[Qt::Key_8] = XK_8; keysyms[Qt::Key_9] = XK_9; keysyms[Qt::Key_Colon] = XK_colon; keysyms[Qt::Key_Semicolon] = XK_semicolon; keysyms[Qt::Key_Less] = XK_less; keysyms[Qt::Key_Equal] = XK_equal; keysyms[Qt::Key_Greater] = XK_greater; keysyms[Qt::Key_Question] = XK_question; keysyms[Qt::Key_At] = XK_at; keysyms[Qt::Key_A] = XK_A; keysyms[Qt::Key_B] = XK_B; keysyms[Qt::Key_C] = XK_C; keysyms[Qt::Key_D] = XK_D; keysyms[Qt::Key_E] = XK_E; keysyms[Qt::Key_F] = XK_F; keysyms[Qt::Key_G] = XK_G; keysyms[Qt::Key_H] = XK_H; keysyms[Qt::Key_I] = XK_I; keysyms[Qt::Key_J] = XK_J; keysyms[Qt::Key_K] = XK_K; keysyms[Qt::Key_L] = XK_L; keysyms[Qt::Key_M] = XK_M; keysyms[Qt::Key_N] = XK_N; keysyms[Qt::Key_O] = XK_O; keysyms[Qt::Key_P] = XK_P; keysyms[Qt::Key_Q] = XK_Q; keysyms[Qt::Key_R] = XK_R; keysyms[Qt::Key_S] = XK_S; keysyms[Qt::Key_T] = XK_T; keysyms[Qt::Key_U] = XK_U; keysyms[Qt::Key_V] = XK_V; keysyms[Qt::Key_W] = XK_W; keysyms[Qt::Key_X] = XK_X; keysyms[Qt::Key_Y] = XK_Y; keysyms[Qt::Key_Z] = XK_Z; keysyms[Qt::Key_BracketLeft] = XK_bracketleft; keysyms[Qt::Key_Backslash] = XK_backslash; keysyms[Qt::Key_BracketRight] = XK_bracketright; keysyms[Qt::Key_AsciiCircum] = XK_asciicircum; keysyms[Qt::Key_Underscore] = XK_underscore; keysyms[Qt::Key_QuoteLeft] = XK_quoteleft; keysyms[Qt::Key_BraceLeft] = XK_braceleft; keysyms[Qt::Key_Bar] = XK_bar; keysyms[Qt::Key_BraceRight] = XK_braceright; keysyms[Qt::Key_AsciiTilde] = XK_asciitilde; keysyms[Qt::Key_nobreakspace] = XK_nobreakspace; keysyms[Qt::Key_exclamdown] = XK_exclamdown; keysyms[Qt::Key_cent] = XK_cent; keysyms[Qt::Key_sterling] = XK_sterling; keysyms[Qt::Key_currency] = XK_currency; keysyms[Qt::Key_yen] = XK_yen; keysyms[Qt::Key_brokenbar] = XK_brokenbar; keysyms[Qt::Key_section] = XK_section; keysyms[Qt::Key_diaeresis] = XK_diaeresis; keysyms[Qt::Key_copyright] = XK_copyright; keysyms[Qt::Key_ordfeminine] = XK_ordfeminine; keysyms[Qt::Key_guillemotleft] = XK_guillemotleft; keysyms[Qt::Key_notsign] = XK_notsign; keysyms[Qt::Key_hyphen] = XK_hyphen; keysyms[Qt::Key_registered] = XK_registered; keysyms[Qt::Key_macron] = XK_macron; keysyms[Qt::Key_degree] = XK_degree; keysyms[Qt::Key_plusminus] = XK_plusminus; keysyms[Qt::Key_twosuperior] = XK_twosuperior; keysyms[Qt::Key_threesuperior] = XK_threesuperior; keysyms[Qt::Key_acute] = XK_acute; keysyms[Qt::Key_mu] = XK_mu; keysyms[Qt::Key_paragraph] = XK_paragraph; keysyms[Qt::Key_periodcentered] = XK_periodcentered; keysyms[Qt::Key_cedilla] = XK_cedilla; keysyms[Qt::Key_onesuperior] = XK_onesuperior; keysyms[Qt::Key_masculine] = XK_masculine; keysyms[Qt::Key_guillemotright] = XK_guillemotright; keysyms[Qt::Key_onequarter] = XK_onequarter; keysyms[Qt::Key_onehalf] = XK_onehalf; keysyms[Qt::Key_threequarters] = XK_threequarters; keysyms[Qt::Key_questiondown] = XK_questiondown; keysyms[Qt::Key_Agrave] = XK_Agrave; keysyms[Qt::Key_Aacute] = XK_Aacute; keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; keysyms[Qt::Key_Atilde] = XK_Atilde; keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; keysyms[Qt::Key_Aring] = XK_Aring; keysyms[Qt::Key_AE] = XK_AE; keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; keysyms[Qt::Key_Egrave] = XK_Egrave; keysyms[Qt::Key_Eacute] = XK_Eacute; keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; keysyms[Qt::Key_Igrave] = XK_Igrave; keysyms[Qt::Key_Iacute] = XK_Iacute; keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; keysyms[Qt::Key_ETH] = XK_ETH; keysyms[Qt::Key_Ntilde] = XK_Ntilde; keysyms[Qt::Key_Ograve] = XK_Ograve; keysyms[Qt::Key_Oacute] = XK_Oacute; keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; keysyms[Qt::Key_Otilde] = XK_Otilde; keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; keysyms[Qt::Key_multiply] = XK_multiply; keysyms[Qt::Key_Ooblique] = XK_Ooblique; keysyms[Qt::Key_Ugrave] = XK_Ugrave; keysyms[Qt::Key_Uacute] = XK_Uacute; keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; keysyms[Qt::Key_Yacute] = XK_Yacute; keysyms[Qt::Key_THORN] = XK_THORN; keysyms[Qt::Key_ssharp] = XK_ssharp; keysyms[Qt::Key_Agrave] = XK_Agrave; keysyms[Qt::Key_Aacute] = XK_Aacute; keysyms[Qt::Key_Acircumflex] = XK_Acircumflex; keysyms[Qt::Key_Atilde] = XK_Atilde; keysyms[Qt::Key_Adiaeresis] = XK_Adiaeresis; keysyms[Qt::Key_Aring] = XK_Aring; keysyms[Qt::Key_AE] = XK_AE; keysyms[Qt::Key_Ccedilla] = XK_Ccedilla; keysyms[Qt::Key_Egrave] = XK_Egrave; keysyms[Qt::Key_Eacute] = XK_Eacute; keysyms[Qt::Key_Ecircumflex] = XK_Ecircumflex; keysyms[Qt::Key_Ediaeresis] = XK_Ediaeresis; keysyms[Qt::Key_Igrave] = XK_Igrave; keysyms[Qt::Key_Iacute] = XK_Iacute; keysyms[Qt::Key_Icircumflex] = XK_Icircumflex; keysyms[Qt::Key_Idiaeresis] = XK_Idiaeresis; keysyms[Qt::Key_ETH] = XK_ETH; keysyms[Qt::Key_Ntilde] = XK_Ntilde; keysyms[Qt::Key_Ograve] = XK_Ograve; keysyms[Qt::Key_Oacute] = XK_Oacute; keysyms[Qt::Key_Ocircumflex] = XK_Ocircumflex; keysyms[Qt::Key_Otilde] = XK_Otilde; keysyms[Qt::Key_Odiaeresis] = XK_Odiaeresis; keysyms[Qt::Key_division] = XK_division; keysyms[Qt::Key_Ooblique] = XK_Ooblique; keysyms[Qt::Key_Ugrave] = XK_Ugrave; keysyms[Qt::Key_Uacute] = XK_Uacute; keysyms[Qt::Key_Ucircumflex] = XK_Ucircumflex; keysyms[Qt::Key_Udiaeresis] = XK_Udiaeresis; keysyms[Qt::Key_Yacute] = XK_Yacute; keysyms[Qt::Key_THORN] = XK_THORN; keysyms[Qt::Key_ydiaeresis] = XK_ydiaeresis; 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; } // every keysym with a value under 256 corresponds // directly to an ISO-Latin1 Character. We need // to store how to generate those characters: int basicKeycodes[0x100]; // What key do we need to press? int basicModifiers[0x100]; // What modifiers are required? // 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; // And how do the modifiers relate to Keycodes? typedef std::multimap ModifierToKeycodeMap; ModifierToKeycodeMap ModifierToKeycode; typedef std::map KeycodeToModifierMap; KeycodeToModifierMap KeycodeToModifier; // 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() { // Mark everything as unknown initially for(int i = 0; i < 8; i++) { modifierKeycodes[i] = -1; } for(int i = 0; i < 0x100; i++) { basicKeycodes[i] = -1; } 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; bool xkbPresent = XkbQueryExtension(dpy, &xkbOpcode, &xkbEvent, &xkbError, &xkbMajor, &xkbMinor); 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 < keysymsPerCode; j++) { const int idx = i * keysymsPerCode + j; const KeySym ks = mapping[idx]; const int keycode = minKeycode + i; // to find out if there is a Latin1 character there, if(ks >= ' ' && ks < 0x100) { // that we have not yet found, if(basicKeycodes[ks] != -1) continue; // already found // and record its keycode and needed modifiers. basicKeycodes[ks] = keycode; basicModifiers[ks] = j; } } } // We are finished, free the mapping structure. XFree(mapping); } 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; 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 } // 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++) { KeySym ks = 0; unsigned int unconsumed; if(keybDesc) { if(!XkbTranslateKeyCode(keybDesc, i, j, &unconsumed, &ks) || ks == NoSymbol) continue; } else { if(!XkbLookupKeySym(dpy, i, j, &unconsumed, &ks) || ks == NoSymbol) continue; } if(ks && (ks < 0x100)) { if(basicKeycodes[ks] != -1) continue; basicKeycodes[ks] = i; basicModifiers[ks] = unconsumed & j; } } } // Free the keyboard description: if(keybDesc) { XkbFreeKeyboard(keybDesc, XkbAllComponentsMask, true); } } // 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< IntSet; IntSet pressedModifierKeys; XModifiers currentModifiers; 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) { 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: ConsoleLog writeLine(QString(" releasing key %1").arg(kc)); 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::iterator i, end = tracker.end(); for(i = tracker.begin(); 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(); // 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. switch(evt.qtKeysym()) { case Qt::Key_Backtab: doHandle(InputEvent(evt.type(), evt.code(), evt.qtModifiers() | Qt::ShiftModifier | Qt::Key_Tab)); break; default: ConsoleLog writeLine(QString("Unknown keysym received: %1").arg(evt.qtKeysym(), 8, 16)); } } else { KeySym ks = (*i).second; // is it a "normal" key? if(ks >= ' ' && ks < 0x100) { if(basicKeycodes[ks] != -1) { if(evt.isPress()) { // Try the simple way first: // Does the keycode with current modifiers yield the requested symbol? KeySym unmodSym = XKeycodeToKeysym(dpy, basicKeycodes[ks], currentModifiers); ConsoleLog writeLine(QString("Pressing the key for %1 would yield %2 right now.").arg(XKeysymToString(ks)).arg(XKeysymToString(unmodSym))); if(ks == XKeycodeToKeysym(dpy, basicKeycodes[ks], currentModifiers)) { XTestFakeKeyEvent(dpy, basicKeycodes[ks], 1, CurrentTime); } else { // what modifier keys do we need to press? XModifiers mods = translateModifiers(evt.qtModifiers()); // we may need to press additional modifiers to generate this keysym: if(QChar(ks, 0).isLetter()) { // but, since Qt does not differentiate upper and lower case, // we need to preserve Shift and Lock in their current state. mods |= basicModifiers[ks] & ~(1<