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