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
18QT_BEGIN_NAMESPACE
19
20static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
21 xkb_state *state, xkb_keycode_t code,
22 bool superAsMeta, bool hyperAsMeta);
23
24typedef 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
40template<std::size_t Xkb, std::size_t Qt>
41struct Xkb2Qt
42{
43 using Type = xkb2qt_t;
44 static constexpr Type data() noexcept { return Type{.xkb: Xkb, .qt: Qt}; }
45};
46
47static 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_XF86Keyboard, Qt::Key_Keyboard>,
380 Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch0>,
381 Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch1>,
382 Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch2>,
383 Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch3>,
384 Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch4>,
385 Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch5>,
386 Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch6>,
387 Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch7>,
388 Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_Launch8>,
389 Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_Launch9>,
390 Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchA>,
391 Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchB>,
392 Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchC>,
393 Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchD>,
394 Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchE>,
395 Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchF>
396 >::Data{}
397);
398
399xkb_keysym_t QXkbCommon::qxkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks)
400{
401 xkb_keysym_t lower, upper;
402
403 xkbcommon_XConvertCase(sym: ks, lower: &lower, upper: &upper);
404
405 return upper;
406}
407
408QString QXkbCommon::lookupString(struct xkb_state *state, xkb_keycode_t code)
409{
410 QVarLengthArray<char, 32> chars(32);
411 const int size = xkb_state_key_get_utf8(state, key: code, buffer: chars.data(), size: chars.size());
412 if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
413 chars.resize(sz: size + 1);
414 xkb_state_key_get_utf8(state, key: code, buffer: chars.data(), size: chars.size());
415 }
416 return QString::fromUtf8(utf8: chars.constData(), size);
417}
418
419QString QXkbCommon::lookupStringNoKeysymTransformations(xkb_keysym_t keysym)
420{
421 QVarLengthArray<char, 32> chars(32);
422 const int size = xkb_keysym_to_utf8(keysym, buffer: chars.data(), size: chars.size());
423 if (size == 0)
424 return QString(); // the keysym does not have a Unicode representation
425
426 if (Q_UNLIKELY(size > chars.size())) {
427 chars.resize(sz: size);
428 xkb_keysym_to_utf8(keysym, buffer: chars.data(), size: chars.size());
429 }
430 return QString::fromUtf8(utf8: chars.constData(), size: size - 1);
431}
432
433QList<xkb_keysym_t> QXkbCommon::toKeysym(QKeyEvent *event)
434{
435 QList<xkb_keysym_t> keysyms;
436 int qtKey = event->key();
437
438 if (qtKey >= Qt::Key_F1 && qtKey <= Qt::Key_F35) {
439 keysyms.append(XKB_KEY_F1 + (qtKey - Qt::Key_F1));
440 } else if (event->modifiers() & Qt::KeypadModifier) {
441 if (qtKey >= Qt::Key_0 && qtKey <= Qt::Key_9)
442 keysyms.append(XKB_KEY_KP_0 + (qtKey - Qt::Key_0));
443 } else if (isLatin1(sym: qtKey) && event->text().isUpper()) {
444 keysyms.append(t: qtKey);
445 }
446
447 if (!keysyms.isEmpty())
448 return keysyms;
449
450 // check if we have a direct mapping
451 auto it = std::find_if(first: KeyTbl.cbegin(), last: KeyTbl.cend(), pred: [&qtKey](xkb2qt_t elem) {
452 return elem.qt == static_cast<uint>(qtKey);
453 });
454 if (it != KeyTbl.end()) {
455 keysyms.append(t: it->xkb);
456 return keysyms;
457 }
458
459 QList<uint> ucs4;
460 if (event->text().isEmpty())
461 ucs4.append(t: qtKey);
462 else
463 ucs4 = event->text().toUcs4();
464
465 // From libxkbcommon keysym-utf.c:
466 // "We allow to represent any UCS character in the range U-00000000 to
467 // U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff."
468 for (uint utf32 : std::as_const(t&: ucs4))
469 keysyms.append(t: utf32 | 0x01000000);
470
471 return keysyms;
472}
473
474int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers)
475{
476 return keysymToQtKey(keysym, modifiers, state: nullptr, code: 0);
477}
478
479int QXkbCommon::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
480 xkb_state *state, xkb_keycode_t code,
481 bool superAsMeta, bool hyperAsMeta)
482{
483 // Note 1: All standard key sequences on linux (as defined in platform theme)
484 // that use a latin character also contain a control modifier, which is why
485 // checking for Qt::ControlModifier is sufficient here. It is possible to
486 // override QPlatformTheme::keyBindings() and provide custom sequences for
487 // QKeySequence::StandardKey. Custom sequences probably should respect this
488 // convention (alternatively, we could test against other modifiers here).
489 // Note 2: The possibleKeys() shorcut mechanism is not affected by this value
490 // adjustment and does its own thing.
491 if (modifiers & Qt::ControlModifier) {
492 // With standard shortcuts we should prefer a latin character, this is
493 // for checks like "some qkeyevent == QKeySequence::Copy" to work even
494 // when using for example 'russian' keyboard layout.
495 if (!QXkbCommon::isLatin1(sym: keysym)) {
496 xkb_keysym_t latinKeysym = QXkbCommon::lookupLatinKeysym(state, keycode: code);
497 if (latinKeysym != XKB_KEY_NoSymbol)
498 keysym = latinKeysym;
499 }
500 }
501
502 return keysymToQtKey_internal(keysym, modifiers, state, code, superAsMeta, hyperAsMeta);
503}
504
505static int keysymToQtKey_internal(xkb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
506 xkb_state *state, xkb_keycode_t code,
507 bool superAsMeta, bool hyperAsMeta)
508{
509 int qtKey = 0;
510
511 // lookup from direct mapping
512 if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
513 // function keys
514 qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1);
515 } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
516 // numeric keypad keys
517 qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
518 } else if (QXkbCommon::isLatin1(sym: keysym)) {
519 // Most Qt::Key values are determined by their upper-case version,
520 // where this is in the Latin-1 repertoire. So start with that:
521 qtKey = QXkbCommon::qxkbcommon_xkb_keysym_to_upper(ks: keysym);
522 // However, Key_mu and Key_ydiaeresis are U+00B5 MICRO SIGN and
523 // U+00FF LATIN SMALL LETTER Y WITH DIAERESIS, both lower-case,
524 // with upper-case forms outside Latin-1, so use them as they are
525 // since they're the Qt::Key values.
526 if (!QXkbCommon::isLatin1(sym: qtKey))
527 qtKey = keysym;
528 } else {
529 // check if we have a direct mapping
530 xkb2qt_t searchKey{.xkb: keysym, .qt: 0};
531 auto it = std::lower_bound(first: KeyTbl.cbegin(), last: KeyTbl.cend(), val: searchKey);
532 if (it != KeyTbl.end() && !(searchKey < *it))
533 qtKey = it->qt;
534
535 // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
536 if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R))
537 qtKey = Qt::Key_Meta;
538 if (hyperAsMeta && (qtKey == Qt::Key_Hyper_L || qtKey == Qt::Key_Hyper_R))
539 qtKey = Qt::Key_Meta;
540 }
541
542 if (qtKey)
543 return qtKey;
544
545 // lookup from unicode
546 QString text;
547 if (!state || modifiers & Qt::ControlModifier) {
548 // Control modifier changes the text to ASCII control character, therefore we
549 // can't use this text to map keysym to a qt key. We can use the same keysym
550 // (it is not affectd by transformation) to obtain untransformed text. For details
551 // see "Appendix A. Default Symbol Transformations" in the XKB specification.
552 text = QXkbCommon::lookupStringNoKeysymTransformations(keysym);
553 } else {
554 text = QXkbCommon::lookupString(state, code);
555 }
556 if (!text.isEmpty()) {
557 if (text.unicode()->isDigit()) {
558 // Ensures that also non-latin digits are mapped to corresponding qt keys,
559 // e.g CTRL + Û² (arabic two), is mapped to CTRL + Qt::Key_2.
560 qtKey = Qt::Key_0 + text.unicode()->digitValue();
561 } else {
562 text = text.toUpper();
563 QStringIterator i(text);
564 qtKey = i.next(invalidAs: 0);
565 }
566 }
567
568 return qtKey;
569}
570
571Qt::KeyboardModifiers QXkbCommon::modifiers(struct xkb_state *state, xkb_keysym_t keysym)
572{
573 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
574
575 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, type: XKB_STATE_MODS_EFFECTIVE) > 0)
576 modifiers |= Qt::ControlModifier;
577 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, type: XKB_STATE_MODS_EFFECTIVE) > 0)
578 modifiers |= Qt::AltModifier;
579 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, type: XKB_STATE_MODS_EFFECTIVE) > 0)
580 modifiers |= Qt::ShiftModifier;
581 if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, type: XKB_STATE_MODS_EFFECTIVE) > 0)
582 modifiers |= Qt::MetaModifier;
583
584 if (isKeypad(sym: keysym))
585 modifiers |= Qt::KeypadModifier;
586
587 return modifiers;
588}
589
590// Possible modifier states.
591static const Qt::KeyboardModifiers ModsTbl[] = {
592 Qt::NoModifier, // 0
593 Qt::ShiftModifier, // 1
594 Qt::ControlModifier, // 2
595 Qt::ControlModifier | Qt::ShiftModifier, // 3
596 Qt::AltModifier, // 4
597 Qt::AltModifier | Qt::ShiftModifier, // 5
598 Qt::AltModifier | Qt::ControlModifier, // 6
599 Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
600 Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
601};
602
603/*
604 Compatibility until all sub modules have transitioned to new API below
605*/
606QList<int> QXkbCommon::possibleKeys(xkb_state *state, const QKeyEvent *event,
607 bool superAsMeta, bool hyperAsMeta)
608{
609 QList<int> result;
610 auto keyCombinations = possibleKeyCombinations(state, event, superAsMeta, hyperAsMeta);
611 for (auto keyCombination : keyCombinations)
612 result << keyCombination.toCombined();
613
614 return result;
615}
616
617QList<QKeyCombination> QXkbCommon::possibleKeyCombinations(xkb_state *state, const QKeyEvent *event,
618 bool superAsMeta, bool hyperAsMeta)
619{
620 QList<QKeyCombination> result;
621 quint32 keycode = event->nativeScanCode();
622 if (!keycode)
623 return result;
624
625 Qt::KeyboardModifiers modifiers = event->modifiers();
626 xkb_keymap *keymap = xkb_state_get_keymap(state);
627 // turn off the modifier bits which doesn't participate in shortcuts
628 Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier;
629 modifiers &= ~notNeeded;
630 // create a fresh kb state and test against the relevant modifier combinations
631 ScopedXKBState scopedXkbQueryState(xkb_state_new(keymap));
632 xkb_state *queryState = scopedXkbQueryState.get();
633 if (!queryState) {
634 qCWarning(lcQpaKeyMapper) << Q_FUNC_INFO << "failed to compile xkb keymap";
635 return result;
636 }
637 // get kb state from the master state and update the temporary state
638 xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(state, components: XKB_STATE_LAYOUT_LOCKED);
639 xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, components: XKB_STATE_MODS_LATCHED);
640 xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, components: XKB_STATE_MODS_LOCKED);
641 xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(state, components: XKB_STATE_MODS_DEPRESSED);
642 xkb_state_update_mask(state: queryState, depressed_mods: depressedMods, latched_mods: latchedMods, locked_mods: lockedMods, depressed_layout: 0, latched_layout: 0, locked_layout: lockedLayout);
643 // handle shortcuts for level three and above
644 xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(state: queryState, key: keycode);
645 xkb_level_index_t levelIndex = 0;
646 if (layoutIndex != XKB_LAYOUT_INVALID) {
647 levelIndex = xkb_state_key_get_level(state: queryState, key: keycode, layout: layoutIndex);
648 if (levelIndex == XKB_LEVEL_INVALID)
649 levelIndex = 0;
650 }
651 if (levelIndex <= 1)
652 xkb_state_update_mask(state: queryState, depressed_mods: 0, latched_mods: latchedMods, locked_mods: lockedMods, depressed_layout: 0, latched_layout: 0, locked_layout: lockedLayout);
653
654 xkb_keysym_t sym = xkb_state_key_get_one_sym(state: queryState, key: keycode);
655 if (sym == XKB_KEY_NoSymbol)
656 return result;
657
658 int baseQtKey = keysymToQtKey_internal(keysym: sym, modifiers, state: queryState, code: keycode, superAsMeta, hyperAsMeta);
659 if (baseQtKey)
660 result += QKeyCombination::fromCombined(combined: baseQtKey + int(modifiers));
661
662 xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(keymap, name: "Shift");
663 xkb_mod_index_t altMod = xkb_keymap_mod_get_index(keymap, name: "Alt");
664 xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(keymap, name: "Control");
665 xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(keymap, name: "Meta");
666
667 Q_ASSERT(shiftMod < 32);
668 Q_ASSERT(altMod < 32);
669 Q_ASSERT(controlMod < 32);
670
671 xkb_mod_mask_t depressed;
672 int qtKey = 0;
673 // obtain a list of possible shortcuts for the given key event
674 for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
675 Qt::KeyboardModifiers neededMods = ModsTbl[i];
676 if ((modifiers & neededMods) == neededMods) {
677 if (i == 8) {
678 if (isLatin1(sym: baseQtKey))
679 continue;
680 // add a latin key as a fall back key
681 sym = lookupLatinKeysym(state, keycode);
682 } else {
683 depressed = 0;
684 if (neededMods & Qt::AltModifier)
685 depressed |= (1 << altMod);
686 if (neededMods & Qt::ShiftModifier)
687 depressed |= (1 << shiftMod);
688 if (neededMods & Qt::ControlModifier)
689 depressed |= (1 << controlMod);
690 if (metaMod < 32 && neededMods & Qt::MetaModifier)
691 depressed |= (1 << metaMod);
692 xkb_state_update_mask(state: queryState, depressed_mods: depressed, latched_mods: latchedMods, locked_mods: lockedMods, depressed_layout: 0, latched_layout: 0, locked_layout: lockedLayout);
693 sym = xkb_state_key_get_one_sym(state: queryState, key: keycode);
694 }
695 if (sym == XKB_KEY_NoSymbol)
696 continue;
697
698 Qt::KeyboardModifiers mods = modifiers & ~neededMods;
699 qtKey = keysymToQtKey_internal(keysym: sym, modifiers: mods, state: queryState, code: keycode, superAsMeta, hyperAsMeta);
700 if (!qtKey || qtKey == baseQtKey)
701 continue;
702
703 // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
704 // but Ctrl++ is more specific than +, so we should skip the last one
705 bool ambiguous = false;
706 for (auto keyCombination : std::as_const(t&: result)) {
707 if (keyCombination.key() == qtKey
708 && (keyCombination.keyboardModifiers() & mods) == mods) {
709 ambiguous = true;
710 break;
711 }
712 }
713 if (ambiguous)
714 continue;
715
716 result += QKeyCombination::fromCombined(combined: qtKey + int(mods));
717 }
718 }
719
720 return result;
721}
722
723void QXkbCommon::verifyHasLatinLayout(xkb_keymap *keymap)
724{
725 const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(keymap);
726 const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap);
727 const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap);
728
729 const xkb_keysym_t *keysyms = nullptr;
730 int nrLatinKeys = 0;
731 for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
732 for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
733 xkb_keymap_key_get_syms_by_level(keymap, key: code, layout, level: 0, syms_out: &keysyms);
734 if (keysyms && isLatin1(sym: keysyms[0]))
735 nrLatinKeys++;
736 if (nrLatinKeys > 10) // arbitrarily chosen threshold
737 return;
738 }
739 }
740 // This means that lookupLatinKeysym() will not find anything and latin
741 // key shortcuts might not work. This is a bug in the affected desktop
742 // environment. Usually can be solved via system settings by adding e.g. 'us'
743 // layout to the list of selected layouts, or by using command line, "setxkbmap
744 // -layout rus,en". The position of latin key based layout in the list of the
745 // selected layouts is irrelevant. Properly functioning desktop environments
746 // handle this behind the scenes, even if no latin key based layout has been
747 // explicitly listed in the selected layouts.
748 qCDebug(lcQpaKeyMapper, "no keyboard layouts with latin keys present");
749}
750
751xkb_keysym_t QXkbCommon::lookupLatinKeysym(xkb_state *state, xkb_keycode_t keycode)
752{
753 xkb_layout_index_t layout;
754 xkb_keysym_t sym = XKB_KEY_NoSymbol;
755 if (!state)
756 return sym;
757 xkb_keymap *keymap = xkb_state_get_keymap(state);
758 const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(keymap, key: keycode);
759 const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(state, key: keycode);
760 // Look at user layouts in the order in which they are defined in system
761 // settings to find a latin keysym.
762 for (layout = 0; layout < layoutCount; ++layout) {
763 if (layout == currentLayout)
764 continue;
765 const xkb_keysym_t *syms = nullptr;
766 xkb_level_index_t level = xkb_state_key_get_level(state, key: keycode, layout);
767 if (xkb_keymap_key_get_syms_by_level(keymap, key: keycode, layout, level, syms_out: &syms) != 1)
768 continue;
769 if (isLatin1(sym: syms[0])) {
770 sym = syms[0];
771 break;
772 }
773 }
774
775 if (sym == XKB_KEY_NoSymbol)
776 return sym;
777
778 xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(state, components: XKB_STATE_MODS_LATCHED);
779 xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(state, components: XKB_STATE_MODS_LOCKED);
780
781 // Check for uniqueness, consider the following setup:
782 // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
783 // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
784 // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
785 // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
786 // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
787 // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
788 // generate the same shortcut event in this case.
789 const xkb_keycode_t minKeycode = xkb_keymap_min_keycode(keymap);
790 const xkb_keycode_t maxKeycode = xkb_keymap_max_keycode(keymap);
791 ScopedXKBState queryState(xkb_state_new(keymap));
792 for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
793 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);
794 for (xkb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
795 xkb_keysym_t prevSym = xkb_state_key_get_one_sym(state: queryState.get(), key: code);
796 if (prevSym == sym) {
797 sym = XKB_KEY_NoSymbol;
798 break;
799 }
800 }
801 }
802
803 return sym;
804}
805
806void QXkbCommon::setXkbContext(QPlatformInputContext *inputContext, struct xkb_context *context)
807{
808 if (!inputContext || !context)
809 return;
810
811 const char *const inputContextClassName = "QComposeInputContext";
812 const char *const normalizedSignature = "setXkbContext(xkb_context*)";
813
814 if (inputContext->objectName() != QLatin1StringView(inputContextClassName))
815 return;
816
817 static const QMetaMethod setXkbContext = [&]() {
818 int methodIndex = inputContext->metaObject()->indexOfMethod(method: normalizedSignature);
819 QMetaMethod method = inputContext->metaObject()->method(index: methodIndex);
820 Q_ASSERT(method.isValid());
821 if (!method.isValid())
822 qCWarning(lcQpaKeyMapper) << normalizedSignature << "not found on" << inputContextClassName;
823 return method;
824 }();
825
826 if (!setXkbContext.isValid())
827 return;
828
829 setXkbContext.invoke(obj: inputContext, c: Qt::DirectConnection, Q_ARG(struct xkb_context*, context));
830}
831
832QT_END_NAMESPACE
833

source code of qtbase/src/gui/platform/unix/qxkbcommon.cpp