| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2019 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtGui module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #include "qxkbcommon_p.h" |
| 41 | |
| 42 | #include <private/qmakearray_p.h> |
| 43 | |
| 44 | #include <QtCore/QMetaMethod> |
| 45 | #include <QtGui/QKeyEvent> |
| 46 | #include <QtGui/private/qguiapplication_p.h> |
| 47 | |
| 48 | #include <qpa/qplatforminputcontext.h> |
| 49 | #include <qpa/qplatformintegration.h> |
| 50 | |
| 51 | QT_BEGIN_NAMESPACE |
| 52 | |
| 53 | Q_LOGGING_CATEGORY(lcXkbcommon, "qt.xkbcommon" ) |
| 54 | |
| 55 | static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, |
| 56 | xkb_state *state, xkb_keycode_t code, |
| 57 | bool superAsMeta, bool hyperAsMeta); |
| 58 | |
| 59 | typedef struct xkb2qt |
| 60 | { |
| 61 | unsigned int xkb; |
| 62 | unsigned int qt; |
| 63 | |
| 64 | constexpr bool operator <=(const xkb2qt &that) const noexcept |
| 65 | { |
| 66 | return xkb <= that.xkb; |
| 67 | } |
| 68 | |
| 69 | constexpr bool operator <(const xkb2qt &that) const noexcept |
| 70 | { |
| 71 | return xkb < that.xkb; |
| 72 | } |
| 73 | } xkb2qt_t; |
| 74 | |
| 75 | template<std::size_t Xkb, std::size_t Qt> |
| 76 | struct Xkb2Qt |
| 77 | { |
| 78 | using Type = xkb2qt_t; |
| 79 | static constexpr Type data() noexcept { return Type{Xkb, Qt}; } |
| 80 | }; |
| 81 | |
| 82 | static constexpr const auto KeyTbl = qMakeArray( |
| 83 | QSortedData< |
| 84 | // misc keys |
| 85 | |
| 86 | Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>, |
| 87 | Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>, |
| 88 | Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>, |
| 89 | Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>, |
| 90 | Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>, |
| 91 | Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>, |
| 92 | Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>, |
| 93 | Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>, |
| 94 | Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>, |
| 95 | Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>, |
| 96 | Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq |
| 97 | Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq |
| 98 | |
| 99 | // cursor movement |
| 100 | |
| 101 | Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>, |
| 102 | Xkb2Qt<XKB_KEY_End, Qt::Key_End>, |
| 103 | Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>, |
| 104 | Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>, |
| 105 | Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>, |
| 106 | Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>, |
| 107 | Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>, |
| 108 | Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>, |
| 109 | |
| 110 | // modifiers |
| 111 | |
| 112 | Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>, |
| 113 | Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>, |
| 114 | Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>, |
| 115 | Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>, |
| 116 | Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>, |
| 117 | Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>, |
| 118 | Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>, |
| 119 | Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>, |
| 120 | Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>, |
| 121 | Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>, |
| 122 | Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>, |
| 123 | Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>, |
| 124 | Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>, |
| 125 | Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>, |
| 126 | Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>, |
| 127 | Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>, |
| 128 | Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>, |
| 129 | Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>, |
| 130 | Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab |
| 131 | Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11) |
| 132 | Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12) |
| 133 | |
| 134 | // numeric and function keypad keys |
| 135 | |
| 136 | Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>, |
| 137 | Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>, |
| 138 | Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>, |
| 139 | Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>, |
| 140 | Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>, |
| 141 | Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>, |
| 142 | Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>, |
| 143 | Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>, |
| 144 | Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>, |
| 145 | Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>, |
| 146 | Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>, |
| 147 | Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>, |
| 148 | Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>, |
| 149 | Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>, |
| 150 | Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>, |
| 151 | Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>, |
| 152 | Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>, |
| 153 | Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>, |
| 154 | Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>, |
| 155 | Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>, |
| 156 | Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>, |
| 157 | |
| 158 | // special non-XF86 function keys |
| 159 | |
| 160 | Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>, |
| 161 | Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>, |
| 162 | Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>, |
| 163 | Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>, |
| 164 | |
| 165 | // International input method support keys |
| 166 | |
| 167 | // International & multi-key character composition |
| 168 | Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>, |
| 169 | Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>, |
| 170 | Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>, |
| 171 | Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>, |
| 172 | Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>, |
| 173 | Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>, |
| 174 | |
| 175 | // Misc Functions |
| 176 | Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>, |
| 177 | Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>, |
| 178 | |
| 179 | // Japanese keyboard support |
| 180 | Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>, |
| 181 | Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>, |
| 182 | //Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>, |
| 183 | Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>, |
| 184 | Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>, |
| 185 | Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>, |
| 186 | Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>, |
| 187 | Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>, |
| 188 | Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>, |
| 189 | Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>, |
| 190 | Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>, |
| 191 | Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>, |
| 192 | Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>, |
| 193 | Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>, |
| 194 | Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>, |
| 195 | Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>, |
| 196 | Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>, |
| 197 | Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>, |
| 198 | //Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>, |
| 199 | //Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>, |
| 200 | //Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>, |
| 201 | Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>, |
| 202 | Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>, |
| 203 | Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>, |
| 204 | |
| 205 | // Korean keyboard support |
| 206 | Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>, |
| 207 | Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>, |
| 208 | Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>, |
| 209 | Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>, |
| 210 | Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>, |
| 211 | Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>, |
| 212 | //Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>, |
| 213 | Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>, |
| 214 | Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>, |
| 215 | Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>, |
| 216 | Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>, |
| 217 | Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>, |
| 218 | //Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>, |
| 219 | //Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>, |
| 220 | //Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>, |
| 221 | Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>, |
| 222 | Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>, |
| 223 | Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>, |
| 224 | Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>, |
| 225 | //Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>, |
| 226 | Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>, |
| 227 | |
| 228 | // dead keys |
| 229 | Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>, |
| 230 | Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>, |
| 231 | Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>, |
| 232 | Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>, |
| 233 | Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>, |
| 234 | Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>, |
| 235 | Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>, |
| 236 | Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>, |
| 237 | Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>, |
| 238 | Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>, |
| 239 | Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>, |
| 240 | Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>, |
| 241 | Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>, |
| 242 | Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>, |
| 243 | Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>, |
| 244 | Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>, |
| 245 | Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>, |
| 246 | Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>, |
| 247 | Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>, |
| 248 | Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>, |
| 249 | Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>, |
| 250 | Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>, |
| 251 | Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>, |
| 252 | Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>, |
| 253 | Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>, |
| 254 | Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>, |
| 255 | Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>, |
| 256 | Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>, |
| 257 | Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>, |
| 258 | Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>, |
| 259 | Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>, |
| 260 | Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>, |
| 261 | Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>, |
| 262 | Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>, |
| 263 | Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>, |
| 264 | Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>, |
| 265 | Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>, |
| 266 | Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>, |
| 267 | Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>, |
| 268 | Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>, |
| 269 | Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>, |
| 270 | Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>, |
| 271 | Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>, |
| 272 | Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>, |
| 273 | Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>, |
| 274 | Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>, |
| 275 | Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>, |
| 276 | Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>, |
| 277 | Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>, |
| 278 | |
| 279 | // Special keys from X.org - This include multimedia keys, |
| 280 | // wireless/bluetooth/uwb keys, special launcher keys, etc. |
| 281 | Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>, |
| 282 | Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>, |
| 283 | Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>, |
| 284 | Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>, |
| 285 | Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>, |
| 286 | Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>, |
| 287 | Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>, |
| 288 | Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>, |
| 289 | Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>, |
| 290 | Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>, |
| 291 | Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>, |
| 292 | Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>, |
| 293 | Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>, |
| 294 | Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>, |
| 295 | Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>, |
| 296 | Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>, |
| 297 | Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>, |
| 298 | Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>, |
| 299 | Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>, |
| 300 | Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_Launch0>, // ### Qt 6: remap properly |
| 301 | Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Launch1>, |
| 302 | Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>, |
| 303 | Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>, |
| 304 | Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>, |
| 305 | Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>, |
| 306 | Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>, |
| 307 | Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>, |
| 308 | Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>, |
| 309 | Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>, |
| 310 | Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>, |
| 311 | Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>, |
| 312 | Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>, |
| 313 | Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>, |
| 314 | Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>, |
| 315 | Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>, |
| 316 | Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>, |
| 317 | Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>, |
| 318 | Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>, |
| 319 | Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>, |
| 320 | Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>, |
| 321 | Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>, |
| 322 | Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>, |
| 323 | Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>, |
| 324 | Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>, |
| 325 | Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>, |
| 326 | Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>, |
| 327 | Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>, |
| 328 | Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>, |
| 329 | Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>, |
| 330 | Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>, |
| 331 | Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>, |
| 332 | Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>, |
| 333 | Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>, |
| 334 | Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>, |
| 335 | Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>, |
| 336 | Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>, |
| 337 | Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>, |
| 338 | Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>, |
| 339 | Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>, |
| 340 | Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>, |
| 341 | Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>, |
| 342 | Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>, |
| 343 | Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>, |
| 344 | Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>, |
| 345 | Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>, |
| 346 | Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>, |
| 347 | Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>, |
| 348 | Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>, |
| 349 | Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>, |
| 350 | Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>, |
| 351 | Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>, |
| 352 | Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>, |
| 353 | Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>, |
| 354 | Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>, |
| 355 | Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>, |
| 356 | Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>, |
| 357 | Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>, |
| 358 | Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>, |
| 359 | Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>, |
| 360 | Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>, |
| 361 | Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>, |
| 362 | Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>, |
| 363 | Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>, |
| 364 | Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>, |
| 365 | Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>, |
| 366 | Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>, |
| 367 | Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>, |
| 368 | Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>, |
| 369 | Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>, |
| 370 | Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>, |
| 371 | Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>, |
| 372 | Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>, |
| 373 | Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>, |
| 374 | Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>, |
| 375 | Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>, |
| 376 | Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>, |
| 377 | Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>, |
| 378 | Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>, |
| 379 | Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>, |
| 380 | Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>, |
| 381 | Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>, |
| 382 | Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>, |
| 383 | Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>, |
| 384 | Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>, |
| 385 | Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>, |
| 386 | Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>, |
| 387 | Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>, |
| 388 | Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>, |
| 389 | Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>, |
| 390 | Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>, |
| 391 | Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>, |
| 392 | Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>, |
| 393 | Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>, |
| 394 | Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>, |
| 395 | Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>, |
| 396 | Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>, |
| 397 | Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>, |
| 398 | Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>, |
| 399 | Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>, |
| 400 | Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>, |
| 401 | Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>, |
| 402 | Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>, |
| 403 | Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>, |
| 404 | Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>, |
| 405 | Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>, |
| 406 | Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>, |
| 407 | Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>, |
| 408 | Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>, |
| 409 | Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch2>, // ### Qt 6: remap properly |
| 410 | Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch3>, |
| 411 | Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch4>, |
| 412 | Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch5>, |
| 413 | Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch6>, |
| 414 | Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch7>, |
| 415 | Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch8>, |
| 416 | Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch9>, |
| 417 | Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_LaunchA>, |
| 418 | Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_LaunchB>, |
| 419 | Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchC>, |
| 420 | Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchD>, |
| 421 | Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchE>, |
| 422 | Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchF>, |
| 423 | Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchG>, |
| 424 | Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchH> |
| 425 | >::Data{} |
| 426 | ); |
| 427 | |
| 428 | xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks) |
| 429 | { |
| 430 | xkb_keysym_t lower, upper; |
| 431 | |
| 432 | xkbcommon_XConvertCase(ks, &lower, &upper); |
| 433 | |
| 434 | return upper; |
| 435 | } |
| 436 | |
| 437 | QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code) |
| 438 | { |
| 439 | QVarLengthArray<char, 32> chars(32); |
| 440 | const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); |
| 441 | if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL |
| 442 | chars.resize(size + 1); |
| 443 | xkb_state_key_get_utf8(state, code, chars.data(), chars.size()); |
| 444 | } |
| 445 | return QString::fromUtf8(chars.constData(), size); |
| 446 | } |
| 447 | |
| 448 | QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) |
| 449 | { |
| 450 | QVarLengthArray<char, 32> chars(32); |
| 451 | const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); |
| 452 | if (size == 0) |
| 453 | return QString(); // the keysym does not have a Unicode representation |
| 454 | |
| 455 | if (Q_UNLIKELY(size > chars.size())) { |
| 456 | chars.resize(size); |
| 457 | xkb_keysym_to_utf8(keysym, chars.data(), chars.size()); |
| 458 | } |
| 459 | return QString::fromUtf8(chars.constData(), size - 1); |
| 460 | } |
| 461 | |
| 462 | QList<xkb_keysym_t> QXkbCommon::toKeysym(QKeyEvent *event) |
| 463 | { |
| 464 | QList<xkb_keysym_t> keysyms; |
| 465 | int qtKey = event->key(); |
| 466 | |
| 467 | if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) { |
| 468 | keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1)); |
| 469 | } else if (event->modifiers() & Qt::KeypadModifier) { |
| 470 | if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9) |
| 471 | keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0)); |
| 472 | } else if (isLatin(qtKey) && event->text().isUpper()) { |
| 473 | keysyms.append(qtKey); |
| 474 | } |
| 475 | |
| 476 | if (!keysyms.isEmpty()) |
| 477 | return keysyms; |
| 478 | |
| 479 | // check if we have a direct mapping |
| 480 | auto it = std::find_if(KeyTbl.cbegin(), KeyTbl.cend(), [&qtKey](xkb2qt_t elem) { |
| 481 | return elem.qt == static_cast<uint>(qtKey); |
| 482 | }); |
| 483 | if (it != KeyTbl.end()) { |
| 484 | keysyms.append(it->xkb); |
| 485 | return keysyms; |
| 486 | } |
| 487 | |
| 488 | QList<uint> ucs4; |
| 489 | if (event->text().isEmpty()) |
| 490 | ucs4.append(qtKey); |
| 491 | else |
| 492 | ucs4 = event->text().toUcs4(); |
| 493 | |
| 494 | // From libxkbcommon keysym-utf.c: |
| 495 | // "We allow to represent any UCS character in the range U-00000000 to |
| 496 | // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff." |
| 497 | for (uint utf32 : qAsConst(ucs4)) |
| 498 | keysyms.append(utf32 | 0x01000000); |
| 499 | |
| 500 | return keysyms; |
| 501 | } |
| 502 | |
| 503 | int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers) |
| 504 | { |
| 505 | return keysymToQtKey(keysym, modifiers, nullptr, 0); |
| 506 | } |
| 507 | |
| 508 | int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, |
| 509 | xkb_state *state, xkb_keycode_t code, |
| 510 | bool superAsMeta, bool hyperAsMeta) |
| 511 | { |
| 512 | // Note 1: All standard key sequences on linux (as defined in platform theme) |
| 513 | // that use a latin character also contain a control modifier, which is why |
| 514 | // checking for Qt::ControlModifier is sufficient here. It is possible to |
| 515 | // override QPlatformTheme::keyBindings() and provide custom sequences for |
| 516 | // QKeySequence::StandardKey. Custom sequences probably should respect this |
| 517 | // convention (alternatively, we could test against other modifiers here). |
| 518 | // Note 2: The possibleKeys() shorcut mechanism is not affected by this value |
| 519 | // adjustment and does its own thing. |
| 520 | if (modifiers & Qt::ControlModifier) { |
| 521 | // With standard shortcuts we should prefer a latin character, this is |
| 522 | // for checks like "some qkeyevent == QKeySequence::Copy" to work even |
| 523 | // when using for example 'russian' keyboard layout. |
| 524 | if (!QXkbCommon::isLatin(keysym)) { |
| 525 | xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, code); |
| 526 | if (latinKeysym != XKB_KEY_NoSymbol) |
| 527 | keysym = latinKeysym; |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta); |
| 532 | } |
| 533 | |
| 534 | static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers, |
| 535 | xkb_state *state, xkb_keycode_t code, |
| 536 | bool superAsMeta, bool hyperAsMeta) |
| 537 | { |
| 538 | int qtKey = 0; |
| 539 | |
| 540 | // lookup from direct mapping |
| 541 | if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) { |
| 542 | // function keys |
| 543 | qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1); |
| 544 | } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) { |
| 545 | // numeric keypad keys |
| 546 | qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0); |
| 547 | } else if (QXkbCommon::isLatin(keysym)) { |
| 548 | qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(keysym); |
| 549 | } else { |
| 550 | // check if we have a direct mapping |
| 551 | xkb2qt_t searchKey{keysym, 0}; |
| 552 | auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey); |
| 553 | if (it != KeyTbl.end() && !(searchKey < *it)) |
| 554 | qtKey = it->qt; |
| 555 | } |
| 556 | |
| 557 | if (qtKey) |
| 558 | return qtKey; |
| 559 | |
| 560 | // lookup from unicode |
| 561 | QString text; |
| 562 | if (!state || modifiers & Qt::ControlModifier) { |
| 563 | // Control modifier changes the text to ASCII control character, therefore we |
| 564 | // can't use this text to map keysym to a qt key. We can use the same keysym |
| 565 | // (it is not affectd by transformation) to obtain untransformed text. For details |
| 566 | // see "Appendix A. Default Symbol Transformations" in the XKB specification. |
| 567 | text = QXkbCommon::lookupStringNoKeysymTransformations(keysym); |
| 568 | } else { |
| 569 | text = QXkbCommon::lookupString(state, code); |
| 570 | } |
| 571 | if (!text.isEmpty()) { |
| 572 | if (text.unicode()->isDigit()) { |
| 573 | // Ensures that also non-latin digits are mapped to corresponding qt keys, |
| 574 | // e.g CTRL + Û² (arabic two), is mapped to CTRL + Qt::Key_2. |
| 575 | qtKey = Qt::Key_0 + text.unicode()->digitValue(); |
| 576 | } else { |
| 577 | qtKey = text.unicode()->toUpper().unicode(); |
| 578 | } |
| 579 | } |
| 580 | |
| 581 | // translate Super/Hyper keys to Meta if we're using them as the MetaModifier |
| 582 | if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) |
| 583 | qtKey = Qt::Key_Meta; |
| 584 | if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R)) |
| 585 | qtKey = Qt::Key_Meta; |
| 586 | |
| 587 | return qtKey; |
| 588 | } |
| 589 | |
| 590 | Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state) |
| 591 | { |
| 592 | Qt::KeyboardModifiers modifiers = Qt::NoModifier; |
| 593 | |
| 594 | if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0) |
| 595 | modifiers |= Qt::ControlModifier; |
| 596 | if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, XKB_STATE_MODS_EFFECTIVE) > 0) |
| 597 | modifiers |= Qt::AltModifier; |
| 598 | if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, XKB_STATE_MODS_EFFECTIVE) > 0) |
| 599 | modifiers |= Qt::ShiftModifier; |
| 600 | if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, XKB_STATE_MODS_EFFECTIVE) > 0) |
| 601 | modifiers |= Qt::MetaModifier; |
| 602 | |
| 603 | return modifiers; |
| 604 | } |
| 605 | |
| 606 | // Possible modifier states. |
| 607 | static const Qt::KeyboardModifiers ModsTbl[] = { |
| 608 | Qt::NoModifier, // 0 |
| 609 | Qt::ShiftModifier, // 1 |
| 610 | Qt::ControlModifier, // 2 |
| 611 | Qt::ControlModifier | Qt::ShiftModifier, // 3 |
| 612 | Qt::AltModifier, // 4 |
| 613 | Qt::AltModifier | Qt::ShiftModifier, // 5 |
| 614 | Qt::AltModifier | Qt::ControlModifier, // 6 |
| 615 | Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7 |
| 616 | Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts |
| 617 | }; |
| 618 | |
| 619 | QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event, |
| 620 | bool superAsMeta, bool hyperAsMeta) |
| 621 | { |
| 622 | QList<int> result; |
| 623 | quint32 keycode = event->nativeScanCode(); |
| 624 | if (!keycode) |
| 625 | return result; |
| 626 | |
| 627 | Qt::KeyboardModifiers modifiers = event->modifiers(); |
| 628 | xkb_keymap *keymap = xkb_state_get_keymap(state); |
| 629 | // turn off the modifier bits which doesn't participate in shortcuts |
| 630 | Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier; |
| 631 | modifiers &= ~notNeeded; |
| 632 | // create a fresh kb state and test against the relevant modifier combinations |
| 633 | ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap)); |
| 634 | xkb_state *queryState = scopedXkbQueryState.get(); |
| 635 | if (!queryState) { |
| 636 | qCWarning(lcXkbcommon) << Q_FUNC_INFO << "failed to compile xkb keymap" ; |
| 637 | return result; |
| 638 | } |
| 639 | // get kb state from the master state and update the temporary state |
| 640 | xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, XKB_STATE_LAYOUT_LOCKED); |
| 641 | xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); |
| 642 | xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); |
| 643 | xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_DEPRESSED); |
| 644 | xkb_state_update_mask(queryState, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout); |
| 645 | // handle shortcuts for level three and above |
| 646 | xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(queryState, keycode); |
| 647 | xkb_level_index_t levelIndex = 0; |
| 648 | if (layoutIndex != XKB_LAYOUT_INVALID) { |
| 649 | levelIndex = xkb_state_key_get_level(queryState, keycode, layoutIndex); |
| 650 | if (levelIndex == XKB_LEVEL_INVALID) |
| 651 | levelIndex = 0; |
| 652 | } |
| 653 | if (levelIndex <= 1) |
| 654 | xkb_state_update_mask(queryState, 0, latchedMods, lockedMods, 0, 0, lockedLayout); |
| 655 | |
| 656 | xkb_keysym_t sym = xkb_state_key_get_one_sym(queryState, keycode); |
| 657 | if (sym == XKB_KEY_NoSymbol) |
| 658 | return result; |
| 659 | |
| 660 | int baseQtKey = keysymToQtKey_internal(sym, modifiers, queryState, keycode, superAsMeta, hyperAsMeta); |
| 661 | if (baseQtKey) |
| 662 | result += (baseQtKey + int(modifiers)); |
| 663 | |
| 664 | xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, "Shift" ); |
| 665 | xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, "Alt" ); |
| 666 | xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, "Control" ); |
| 667 | xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, "Meta" ); |
| 668 | |
| 669 | Q_ASSERT(shiftMod < 32); |
| 670 | Q_ASSERT(altMod < 32); |
| 671 | Q_ASSERT(controlMod < 32); |
| 672 | |
| 673 | xkb_mod_mask_t depressed; |
| 674 | int qtKey = 0; |
| 675 | // obtain a list of possible shortcuts for the given key event |
| 676 | for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) { |
| 677 | Qt::KeyboardModifiers neededMods = ModsTbl[i]; |
| 678 | if ((modifiers & neededMods) == neededMods) { |
| 679 | if (i == 8) { |
| 680 | if (isLatin(baseQtKey)) |
| 681 | continue; |
| 682 | // add a latin key as a fall back key |
| 683 | sym = lookupLatinKeysym(state, keycode); |
| 684 | } else { |
| 685 | depressed = 0; |
| 686 | if (neededMods & Qt::AltModifier) |
| 687 | depressed |= (1 << altMod); |
| 688 | if (neededMods & Qt::ShiftModifier) |
| 689 | depressed |= (1 << shiftMod); |
| 690 | if (neededMods & Qt::ControlModifier) |
| 691 | depressed |= (1 << controlMod); |
| 692 | if (metaMod < 32 && neededMods & Qt::MetaModifier) |
| 693 | depressed |= (1 << metaMod); |
| 694 | xkb_state_update_mask(queryState, depressed, latchedMods, lockedMods, 0, 0, lockedLayout); |
| 695 | sym = xkb_state_key_get_one_sym(queryState, keycode); |
| 696 | } |
| 697 | if (sym == XKB_KEY_NoSymbol) |
| 698 | continue; |
| 699 | |
| 700 | Qt::KeyboardModifiers mods = modifiers & ~neededMods; |
| 701 | qtKey = keysymToQtKey_internal(sym, mods, queryState, keycode, superAsMeta, hyperAsMeta); |
| 702 | if (!qtKey || qtKey == baseQtKey) |
| 703 | continue; |
| 704 | |
| 705 | // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +, |
| 706 | // but Ctrl++ is more specific than +, so we should skip the last one |
| 707 | bool ambiguous = false; |
| 708 | for (int shortcut : qAsConst(result)) { |
| 709 | if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) { |
| 710 | ambiguous = true; |
| 711 | break; |
| 712 | } |
| 713 | } |
| 714 | if (ambiguous) |
| 715 | continue; |
| 716 | |
| 717 | result += (qtKey + int(mods)); |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | return result; |
| 722 | } |
| 723 | |
| 724 | void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap) |
| 725 | { |
| 726 | const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap); |
| 727 | const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); |
| 728 | const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); |
| 729 | |
| 730 | const xkb_keysym_t *keysyms = nullptr; |
| 731 | int nrLatinKeys = 0; |
| 732 | for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) { |
| 733 | for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { |
| 734 | xkb_keymap_key_get_syms_by_level(keymap, code, layout, 0, &keysyms); |
| 735 | if (keysyms && isLatin(keysyms[0])) |
| 736 | nrLatinKeys++; |
| 737 | if (nrLatinKeys > 10) // arbitrarily chosen threshold |
| 738 | return; |
| 739 | } |
| 740 | } |
| 741 | // This means that lookupLatinKeysym() will not find anything and latin |
| 742 | // key shortcuts might not work. This is a bug in the affected desktop |
| 743 | // environment. Usually can be solved via system settings by adding e.g. 'us' |
| 744 | // layout to the list of seleced layouts, or by using command line, "setxkbmap |
| 745 | // -layout rus,en". The position of latin key based layout in the list of the |
| 746 | // selected layouts is irrelevant. Properly functioning desktop environments |
| 747 | // handle this behind the scenes, even if no latin key based layout has been |
| 748 | // explicitly listed in the selected layouts. |
| 749 | qCDebug(lcXkbcommon, "no keyboard layouts with latin keys present" ); |
| 750 | } |
| 751 | |
| 752 | xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode) |
| 753 | { |
| 754 | xkb_layout_index_t layout; |
| 755 | xkb_keysym_t sym = XKB_KEY_NoSymbol; |
| 756 | xkb_keymap *keymap = xkb_state_get_keymap(state); |
| 757 | const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, keycode); |
| 758 | const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, keycode); |
| 759 | // Look at user layouts in the order in which they are defined in system |
| 760 | // settings to find a latin keysym. |
| 761 | for (layout = 0; layout < layoutCount; ++layout) { |
| 762 | if (layout == currentLayout) |
| 763 | continue; |
| 764 | const xkb_keysym_t *syms = nullptr; |
| 765 | xkb_level_index_t level = xkb_state_key_get_level(state, keycode, layout); |
| 766 | if (xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, level, &syms) != 1) |
| 767 | continue; |
| 768 | if (isLatin(syms[0])) { |
| 769 | sym = syms[0]; |
| 770 | break; |
| 771 | } |
| 772 | } |
| 773 | |
| 774 | if (sym == XKB_KEY_NoSymbol) |
| 775 | return sym; |
| 776 | |
| 777 | xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LATCHED); |
| 778 | xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, XKB_STATE_MODS_LOCKED); |
| 779 | |
| 780 | // Check for uniqueness, consider the following setup: |
| 781 | // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active). |
| 782 | // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>, |
| 783 | // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained |
| 784 | // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired |
| 785 | // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q |
| 786 | // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to |
| 787 | // generate the same shortcut event in this case. |
| 788 | const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap); |
| 789 | const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap); |
| 790 | ScopedXKBState queryState(xkb_state_new(keymap)); |
| 791 | for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) { |
| 792 | xkb_state_update_mask(queryState.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout); |
| 793 | for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) { |
| 794 | xkb_keysym_t prevSym = xkb_state_key_get_one_sym(queryState.get(), code); |
| 795 | if (prevSym == sym) { |
| 796 | sym = XKB_KEY_NoSymbol; |
| 797 | break; |
| 798 | } |
| 799 | } |
| 800 | } |
| 801 | |
| 802 | return sym; |
| 803 | } |
| 804 | |
| 805 | void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context) |
| 806 | { |
| 807 | if (!inputContext || !context) |
| 808 | return; |
| 809 | |
| 810 | const char *const inputContextClassName = "QComposeInputContext" ; |
| 811 | const char *const normalizedSignature = "setXkbContext(xkb_context*)" ; |
| 812 | |
| 813 | if (inputContext->objectName() != QLatin1String(inputContextClassName)) |
| 814 | return; |
| 815 | |
| 816 | static const QMetaMethod setXkbContext = [&]() { |
| 817 | int methodIndex = inputContext->metaObject()->indexOfMethod(normalizedSignature); |
| 818 | QMetaMethod method = inputContext->metaObject()->method(methodIndex); |
| 819 | Q_ASSERT(method.isValid()); |
| 820 | if (!method.isValid()) |
| 821 | qCWarning(lcXkbcommon) << normalizedSignature << "not found on" << inputContextClassName; |
| 822 | return method; |
| 823 | }(); |
| 824 | |
| 825 | if (!setXkbContext.isValid()) |
| 826 | return; |
| 827 | |
| 828 | setXkbContext.invoke(inputContext, Qt::DirectConnection, Q_ARG(struct xkb_context*, context)); |
| 829 | } |
| 830 | |
| 831 | QT_END_NAMESPACE |
| 832 | |