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