1 | // Copyright (C) 2016 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 | #include "qxcbkeyboard.h" |
4 | #include "qxcbwindow.h" |
5 | #include "qxcbscreen.h" |
6 | |
7 | #include <qpa/qwindowsysteminterface.h> |
8 | #include <qpa/qplatforminputcontext.h> |
9 | #include <qpa/qplatformintegration.h> |
10 | #include <qpa/qplatformcursor.h> |
11 | |
12 | #include <QtCore/QMetaEnum> |
13 | |
14 | #include <private/qguiapplication_p.h> |
15 | |
16 | #include <xcb/xinput.h> |
17 | |
18 | QT_BEGIN_NAMESPACE |
19 | |
20 | Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const |
21 | { |
22 | Qt::KeyboardModifiers ret = Qt::NoModifier; |
23 | if (s & XCB_MOD_MASK_SHIFT) |
24 | ret |= Qt::ShiftModifier; |
25 | if (s & XCB_MOD_MASK_CONTROL) |
26 | ret |= Qt::ControlModifier; |
27 | if (s & rmod_masks.alt) |
28 | ret |= Qt::AltModifier; |
29 | if (s & rmod_masks.meta) |
30 | ret |= Qt::MetaModifier; |
31 | if (s & rmod_masks.altgr) |
32 | ret |= Qt::GroupSwitchModifier; |
33 | return ret; |
34 | } |
35 | |
36 | /* Look at a pair of unshifted and shifted key symbols. |
37 | * If the 'unshifted' symbol is uppercase and there is no shifted symbol, |
38 | * return the matching lowercase symbol; otherwise return 0. |
39 | * The caller can then use the previously 'unshifted' symbol as the new |
40 | * 'shifted' (uppercase) symbol and the symbol returned by the function |
41 | * as the new 'unshifted' (lowercase) symbol.) */ |
42 | static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifted) |
43 | { |
44 | if (shifted != XKB_KEY_NoSymbol) // Has a shifted symbol |
45 | return 0; |
46 | |
47 | xcb_keysym_t xlower; |
48 | xcb_keysym_t xupper; |
49 | QXkbCommon::xkbcommon_XConvertCase(sym: unshifted, lower: &xlower, upper: &xupper); |
50 | |
51 | if (xlower != xupper // Check if symbol is cased |
52 | && unshifted == xupper) { // Unshifted must be upper case |
53 | return xlower; |
54 | } |
55 | |
56 | return 0; |
57 | } |
58 | |
59 | static QByteArray symbolsGroupString(const xcb_keysym_t *symbols, int count) |
60 | { |
61 | // Don't output trailing NoSymbols |
62 | while (count > 0 && symbols[count - 1] == XKB_KEY_NoSymbol) |
63 | count--; |
64 | |
65 | QByteArray groupString; |
66 | for (int symIndex = 0; symIndex < count; symIndex++) { |
67 | xcb_keysym_t sym = symbols[symIndex]; |
68 | char symString[64]; |
69 | if (sym == XKB_KEY_NoSymbol) |
70 | strcpy(dest: symString, src: "NoSymbol" ); |
71 | else |
72 | xkb_keysym_get_name(keysym: sym, buffer: symString, size: sizeof(symString)); |
73 | |
74 | if (!groupString.isEmpty()) |
75 | groupString += ", " ; |
76 | groupString += symString; |
77 | } |
78 | return groupString; |
79 | } |
80 | |
81 | struct xkb_keymap *QXcbKeyboard::keymapFromCore(const KeysymModifierMap &keysymMods) |
82 | { |
83 | /* Construct an XKB keymap string from information queried from |
84 | * the X server */ |
85 | QByteArray keymap; |
86 | keymap += "xkb_keymap {\n" ; |
87 | |
88 | const xcb_keycode_t minKeycode = connection()->setup()->min_keycode; |
89 | const xcb_keycode_t maxKeycode = connection()->setup()->max_keycode; |
90 | |
91 | // Generate symbolic names from keycodes |
92 | { |
93 | keymap += |
94 | "xkb_keycodes \"core\" {\n" |
95 | "\tminimum = " + QByteArray::number(minKeycode) + ";\n" |
96 | "\tmaximum = " + QByteArray::number(maxKeycode) + ";\n" ; |
97 | for (int code = minKeycode; code <= maxKeycode; code++) { |
98 | auto codeStr = QByteArray::number(code); |
99 | keymap += "<K" + codeStr + "> = " + codeStr + ";\n" ; |
100 | } |
101 | /* TODO: indicators? |
102 | */ |
103 | keymap += "};\n" ; // xkb_keycodes |
104 | } |
105 | |
106 | /* Set up default types (xkbcommon automatically assigns these to |
107 | * symbols, but doesn't have shift info) */ |
108 | keymap += |
109 | "xkb_types \"core\" {\n" |
110 | "virtual_modifiers NumLock,Alt,LevelThree;\n" |
111 | "type \"ONE_LEVEL\" {\n" |
112 | "modifiers= none;\n" |
113 | "level_name[Level1] = \"Any\";\n" |
114 | "};\n" |
115 | "type \"TWO_LEVEL\" {\n" |
116 | "modifiers= Shift;\n" |
117 | "map[Shift]= Level2;\n" |
118 | "level_name[Level1] = \"Base\";\n" |
119 | "level_name[Level2] = \"Shift\";\n" |
120 | "};\n" |
121 | "type \"ALPHABETIC\" {\n" |
122 | "modifiers= Shift+Lock;\n" |
123 | "map[Shift]= Level2;\n" |
124 | "map[Lock]= Level2;\n" |
125 | "level_name[Level1] = \"Base\";\n" |
126 | "level_name[Level2] = \"Caps\";\n" |
127 | "};\n" |
128 | "type \"KEYPAD\" {\n" |
129 | "modifiers= Shift+NumLock;\n" |
130 | "map[Shift]= Level2;\n" |
131 | "map[NumLock]= Level2;\n" |
132 | "level_name[Level1] = \"Base\";\n" |
133 | "level_name[Level2] = \"Number\";\n" |
134 | "};\n" |
135 | "type \"FOUR_LEVEL\" {\n" |
136 | "modifiers= Shift+LevelThree;\n" |
137 | "map[Shift]= Level2;\n" |
138 | "map[LevelThree]= Level3;\n" |
139 | "map[Shift+LevelThree]= Level4;\n" |
140 | "level_name[Level1] = \"Base\";\n" |
141 | "level_name[Level2] = \"Shift\";\n" |
142 | "level_name[Level3] = \"Alt Base\";\n" |
143 | "level_name[Level4] = \"Shift Alt\";\n" |
144 | "};\n" |
145 | "type \"FOUR_LEVEL_ALPHABETIC\" {\n" |
146 | "modifiers= Shift+Lock+LevelThree;\n" |
147 | "map[Shift]= Level2;\n" |
148 | "map[Lock]= Level2;\n" |
149 | "map[LevelThree]= Level3;\n" |
150 | "map[Shift+LevelThree]= Level4;\n" |
151 | "map[Lock+LevelThree]= Level4;\n" |
152 | "map[Shift+Lock+LevelThree]= Level3;\n" |
153 | "level_name[Level1] = \"Base\";\n" |
154 | "level_name[Level2] = \"Shift\";\n" |
155 | "level_name[Level3] = \"Alt Base\";\n" |
156 | "level_name[Level4] = \"Shift Alt\";\n" |
157 | "};\n" |
158 | "type \"FOUR_LEVEL_SEMIALPHABETIC\" {\n" |
159 | "modifiers= Shift+Lock+LevelThree;\n" |
160 | "map[Shift]= Level2;\n" |
161 | "map[Lock]= Level2;\n" |
162 | "map[LevelThree]= Level3;\n" |
163 | "map[Shift+LevelThree]= Level4;\n" |
164 | "map[Lock+LevelThree]= Level3;\n" |
165 | "preserve[Lock+LevelThree]= Lock;\n" |
166 | "map[Shift+Lock+LevelThree]= Level4;\n" |
167 | "preserve[Shift+Lock+LevelThree]= Lock;\n" |
168 | "level_name[Level1] = \"Base\";\n" |
169 | "level_name[Level2] = \"Shift\";\n" |
170 | "level_name[Level3] = \"Alt Base\";\n" |
171 | "level_name[Level4] = \"Shift Alt\";\n" |
172 | "};\n" |
173 | "type \"FOUR_LEVEL_KEYPAD\" {\n" |
174 | "modifiers= Shift+NumLock+LevelThree;\n" |
175 | "map[Shift]= Level2;\n" |
176 | "map[NumLock]= Level2;\n" |
177 | "map[LevelThree]= Level3;\n" |
178 | "map[Shift+LevelThree]= Level4;\n" |
179 | "map[NumLock+LevelThree]= Level4;\n" |
180 | "map[Shift+NumLock+LevelThree]= Level3;\n" |
181 | "level_name[Level1] = \"Base\";\n" |
182 | "level_name[Level2] = \"Number\";\n" |
183 | "level_name[Level3] = \"Alt Base\";\n" |
184 | "level_name[Level4] = \"Alt Number\";\n" |
185 | "};\n" |
186 | "};\n" ; // xkb_types |
187 | |
188 | // Generate mapping between symbolic names and keysyms |
189 | { |
190 | QList<xcb_keysym_t> xkeymap; |
191 | int keysymsPerKeycode = 0; |
192 | { |
193 | int keycodeCount = maxKeycode - minKeycode + 1; |
194 | if (auto keymapReply = Q_XCB_REPLY(xcb_get_keyboard_mapping, xcb_connection(), |
195 | minKeycode, keycodeCount)) { |
196 | keysymsPerKeycode = keymapReply->keysyms_per_keycode; |
197 | int numSyms = keycodeCount * keysymsPerKeycode; |
198 | auto keymapPtr = xcb_get_keyboard_mapping_keysyms(R: keymapReply.get()); |
199 | xkeymap.resize(size: numSyms); |
200 | for (int i = 0; i < numSyms; i++) |
201 | xkeymap[i] = keymapPtr[i]; |
202 | } |
203 | } |
204 | if (xkeymap.isEmpty()) |
205 | return nullptr; |
206 | |
207 | static const char *const builtinModifiers[] = |
208 | { "Shift" , "Lock" , "Control" , "Mod1" , "Mod2" , "Mod3" , "Mod4" , "Mod5" }; |
209 | |
210 | /* Level 3 symbols (e.g. AltGr+something) seem to come in two flavors: |
211 | * - as a proper level 3 in group 1, at least on recent X.org versions |
212 | * - 'disguised' as group 2, on 'legacy' X servers |
213 | * In the 2nd case, remap group 2 to level 3, that seems to work better |
214 | * in practice */ |
215 | bool mapGroup2ToLevel3 = keysymsPerKeycode < 5; |
216 | |
217 | keymap += "xkb_symbols \"core\" {\n" ; |
218 | for (int code = minKeycode; code <= maxKeycode; code++) { |
219 | auto codeMap = xkeymap.constData() + (code - minKeycode) * keysymsPerKeycode; |
220 | |
221 | const int maxGroup1 = 4; // We only support 4 shift states anyway |
222 | const int maxGroup2 = 2; // Only 3rd and 4th keysym are group 2 |
223 | xcb_keysym_t symbolsGroup1[maxGroup1]; |
224 | xcb_keysym_t symbolsGroup2[maxGroup2] = { XKB_KEY_NoSymbol, XKB_KEY_NoSymbol }; |
225 | for (int i = 0; i < maxGroup1 + maxGroup2; i++) { |
226 | xcb_keysym_t sym = i < keysymsPerKeycode ? codeMap[i] : XKB_KEY_NoSymbol; |
227 | if (mapGroup2ToLevel3) { |
228 | // Merge into single group |
229 | if (i < maxGroup1) |
230 | symbolsGroup1[i] = sym; |
231 | } else { |
232 | // Preserve groups |
233 | if (i < 2) |
234 | symbolsGroup1[i] = sym; |
235 | else if (i < 4) |
236 | symbolsGroup2[i - 2] = sym; |
237 | else |
238 | symbolsGroup1[i - 2] = sym; |
239 | } |
240 | } |
241 | |
242 | /* Fix symbols so the unshifted and shifted symbols have |
243 | * lower resp. upper case */ |
244 | if (auto lowered = getUnshiftedXKey(unshifted: symbolsGroup1[0], shifted: symbolsGroup1[1])) { |
245 | symbolsGroup1[1] = symbolsGroup1[0]; |
246 | symbolsGroup1[0] = lowered; |
247 | } |
248 | if (auto lowered = getUnshiftedXKey(unshifted: symbolsGroup2[0], shifted: symbolsGroup2[1])) { |
249 | symbolsGroup2[1] = symbolsGroup2[0]; |
250 | symbolsGroup2[0] = lowered; |
251 | } |
252 | |
253 | QByteArray groupStr1 = symbolsGroupString(symbols: symbolsGroup1, count: maxGroup1); |
254 | if (groupStr1.isEmpty()) |
255 | continue; |
256 | |
257 | keymap += "key <K" + QByteArray::number(code) + "> { " ; |
258 | keymap += "symbols[Group1] = [ " + groupStr1 + " ]" ; |
259 | QByteArray groupStr2 = symbolsGroupString(symbols: symbolsGroup2, count: maxGroup2); |
260 | if (!groupStr2.isEmpty()) |
261 | keymap += ", symbols[Group2] = [ " + groupStr2 + " ]" ; |
262 | |
263 | // See if this key code is for a modifier |
264 | xcb_keysym_t modifierSym = XKB_KEY_NoSymbol; |
265 | for (int symIndex = 0; symIndex < keysymsPerKeycode; symIndex++) { |
266 | xcb_keysym_t sym = codeMap[symIndex]; |
267 | |
268 | if (sym == XKB_KEY_Alt_L |
269 | || sym == XKB_KEY_Meta_L |
270 | || sym == XKB_KEY_Mode_switch |
271 | || sym == XKB_KEY_Super_L |
272 | || sym == XKB_KEY_Super_R |
273 | || sym == XKB_KEY_Hyper_L |
274 | || sym == XKB_KEY_Hyper_R) { |
275 | modifierSym = sym; |
276 | break; |
277 | } |
278 | } |
279 | |
280 | // AltGr |
281 | if (modifierSym == XKB_KEY_Mode_switch) |
282 | keymap += ", virtualMods=LevelThree" ; |
283 | keymap += " };\n" ; // key |
284 | |
285 | // Generate modifier mappings |
286 | int modNum = keysymMods.value(key: modifierSym, defaultValue: -1); |
287 | if (modNum != -1) { |
288 | // Here modNum is always < 8 (see keysymsToModifiers()) |
289 | keymap += QByteArray("modifier_map " ) + builtinModifiers[modNum] |
290 | + " { <K" + QByteArray::number(code) + "> };\n" ; |
291 | } |
292 | } |
293 | // TODO: indicators? |
294 | keymap += "};\n" ; // xkb_symbols |
295 | } |
296 | |
297 | // We need an "Alt" modifier, provide via the xkb_compatibility section |
298 | keymap += |
299 | "xkb_compatibility \"core\" {\n" |
300 | "virtual_modifiers NumLock,Alt,LevelThree;\n" |
301 | "interpret Alt_L+AnyOf(all) {\n" |
302 | "virtualModifier= Alt;\n" |
303 | "action= SetMods(modifiers=modMapMods,clearLocks);\n" |
304 | "};\n" |
305 | "interpret Alt_R+AnyOf(all) {\n" |
306 | "virtualModifier= Alt;\n" |
307 | "action= SetMods(modifiers=modMapMods,clearLocks);\n" |
308 | "};\n" |
309 | "};\n" ; |
310 | |
311 | /* TODO: There is an issue with modifier state not being handled |
312 | * correctly if using Xming with XKEYBOARD disabled. */ |
313 | |
314 | keymap += "};\n" ; // xkb_keymap |
315 | |
316 | return xkb_keymap_new_from_buffer(context: m_xkbContext.get(), |
317 | buffer: keymap.constData(), |
318 | length: keymap.size(), |
319 | format: XKB_KEYMAP_FORMAT_TEXT_V1, |
320 | flags: XKB_KEYMAP_COMPILE_NO_FLAGS); |
321 | } |
322 | |
323 | void QXcbKeyboard::updateKeymap(xcb_mapping_notify_event_t *event) |
324 | { |
325 | if (connection()->hasXKB() || event->request == XCB_MAPPING_POINTER) |
326 | return; |
327 | |
328 | xcb_refresh_keyboard_mapping(syms: m_key_symbols, event); |
329 | updateKeymap(); |
330 | } |
331 | |
332 | void QXcbKeyboard::updateKeymap() |
333 | { |
334 | KeysymModifierMap keysymMods; |
335 | if (!connection()->hasXKB()) |
336 | keysymMods = keysymsToModifiers(); |
337 | updateModifiers(keysymMods); |
338 | |
339 | m_config = true; |
340 | |
341 | if (!m_xkbContext) { |
342 | m_xkbContext.reset(p: xkb_context_new(flags: XKB_CONTEXT_NO_DEFAULT_INCLUDES)); |
343 | if (!m_xkbContext) { |
344 | qCWarning(lcQpaKeyboard, "failed to create XKB context" ); |
345 | m_config = false; |
346 | return; |
347 | } |
348 | xkb_log_level logLevel = lcQpaKeyboard().isDebugEnabled() ? |
349 | XKB_LOG_LEVEL_DEBUG : XKB_LOG_LEVEL_CRITICAL; |
350 | xkb_context_set_log_level(context: m_xkbContext.get(), level: logLevel); |
351 | } |
352 | |
353 | if (connection()->hasXKB()) { |
354 | m_xkbKeymap.reset(p: xkb_x11_keymap_new_from_device(context: m_xkbContext.get(), connection: xcb_connection(), |
355 | device_id: core_device_id, flags: XKB_KEYMAP_COMPILE_NO_FLAGS)); |
356 | if (m_xkbKeymap) |
357 | m_xkbState.reset(p: xkb_x11_state_new_from_device(keymap: m_xkbKeymap.get(), connection: xcb_connection(), device_id: core_device_id)); |
358 | } else { |
359 | m_xkbKeymap.reset(p: keymapFromCore(keysymMods)); |
360 | if (m_xkbKeymap) |
361 | m_xkbState.reset(p: xkb_state_new(keymap: m_xkbKeymap.get())); |
362 | } |
363 | |
364 | if (!m_xkbKeymap) { |
365 | qCWarning(lcQpaKeyboard, "failed to compile a keymap" ); |
366 | m_config = false; |
367 | return; |
368 | } |
369 | if (!m_xkbState) { |
370 | qCWarning(lcQpaKeyboard, "failed to create XKB state" ); |
371 | m_config = false; |
372 | return; |
373 | } |
374 | |
375 | updateXKBMods(); |
376 | |
377 | QXkbCommon::verifyHasLatinLayout(keymap: m_xkbKeymap.get()); |
378 | } |
379 | |
380 | QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const |
381 | { |
382 | return QXkbCommon::possibleKeys(state: m_xkbState.get(), event, superAsMeta: m_superAsMeta, hyperAsMeta: m_hyperAsMeta); |
383 | } |
384 | |
385 | void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) |
386 | { |
387 | if (m_config && connection()->hasXKB()) { |
388 | const xkb_state_component changedComponents |
389 | = xkb_state_update_mask(state: m_xkbState.get(), |
390 | depressed_mods: state->baseMods, |
391 | latched_mods: state->latchedMods, |
392 | locked_mods: state->lockedMods, |
393 | depressed_layout: state->baseGroup, |
394 | latched_layout: state->latchedGroup, |
395 | locked_layout: state->lockedGroup); |
396 | |
397 | handleStateChanges(changedComponents); |
398 | } |
399 | } |
400 | |
401 | static xkb_layout_index_t lockedGroup(quint16 state) |
402 | { |
403 | return (state >> 13) & 3; // bits 13 and 14 report the state keyboard group |
404 | } |
405 | |
406 | void QXcbKeyboard::updateXKBStateFromCore(quint16 state) |
407 | { |
408 | if (m_config) { |
409 | struct xkb_state *xkbState = m_xkbState.get(); |
410 | xkb_mod_mask_t modsDepressed = xkb_state_serialize_mods(state: xkbState, components: XKB_STATE_MODS_DEPRESSED); |
411 | xkb_mod_mask_t modsLatched = xkb_state_serialize_mods(state: xkbState, components: XKB_STATE_MODS_LATCHED); |
412 | xkb_mod_mask_t modsLocked = xkb_state_serialize_mods(state: xkbState, components: XKB_STATE_MODS_LOCKED); |
413 | xkb_mod_mask_t xkbMask = xkbModMask(state); |
414 | |
415 | xkb_mod_mask_t latched = modsLatched & xkbMask; |
416 | xkb_mod_mask_t locked = modsLocked & xkbMask; |
417 | xkb_mod_mask_t depressed = modsDepressed & xkbMask; |
418 | // set modifiers in depressed if they don't appear in any of the final masks |
419 | depressed |= ~(depressed | latched | locked) & xkbMask; |
420 | |
421 | xkb_state_component changedComponents = xkb_state_update_mask( |
422 | state: xkbState, depressed_mods: depressed, latched_mods: latched, locked_mods: locked, depressed_layout: 0, latched_layout: 0, locked_layout: lockedGroup(state)); |
423 | |
424 | handleStateChanges(changedComponents); |
425 | } |
426 | } |
427 | |
428 | void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo) |
429 | { |
430 | if (m_config) { |
431 | auto *mods = static_cast<xcb_input_modifier_info_t *>(modInfo); |
432 | auto *group = static_cast<xcb_input_group_info_t *>(groupInfo); |
433 | const xkb_state_component changedComponents |
434 | = xkb_state_update_mask(state: m_xkbState.get(), |
435 | depressed_mods: mods->base, |
436 | latched_mods: mods->latched, |
437 | locked_mods: mods->locked, |
438 | depressed_layout: group->base, |
439 | latched_layout: group->latched, |
440 | locked_layout: group->locked); |
441 | |
442 | handleStateChanges(changedComponents); |
443 | } |
444 | } |
445 | |
446 | void QXcbKeyboard::handleStateChanges(xkb_state_component changedComponents) |
447 | { |
448 | // Note: Ubuntu (with Unity) always creates a new keymap when layout is changed |
449 | // via system settings, which means that the layout change would not be detected |
450 | // by this code. That can be solved by emitting KeyboardLayoutChange also from updateKeymap(). |
451 | if ((changedComponents & XKB_STATE_LAYOUT_EFFECTIVE) == XKB_STATE_LAYOUT_EFFECTIVE) |
452 | qCDebug(lcQpaKeyboard, "TODO: Support KeyboardLayoutChange on QPA (QTBUG-27681)" ); |
453 | } |
454 | |
455 | xkb_mod_mask_t QXcbKeyboard::xkbModMask(quint16 state) |
456 | { |
457 | xkb_mod_mask_t xkb_mask = 0; |
458 | |
459 | if ((state & XCB_MOD_MASK_SHIFT) && xkb_mods.shift != XKB_MOD_INVALID) |
460 | xkb_mask |= (1 << xkb_mods.shift); |
461 | if ((state & XCB_MOD_MASK_LOCK) && xkb_mods.lock != XKB_MOD_INVALID) |
462 | xkb_mask |= (1 << xkb_mods.lock); |
463 | if ((state & XCB_MOD_MASK_CONTROL) && xkb_mods.control != XKB_MOD_INVALID) |
464 | xkb_mask |= (1 << xkb_mods.control); |
465 | if ((state & XCB_MOD_MASK_1) && xkb_mods.mod1 != XKB_MOD_INVALID) |
466 | xkb_mask |= (1 << xkb_mods.mod1); |
467 | if ((state & XCB_MOD_MASK_2) && xkb_mods.mod2 != XKB_MOD_INVALID) |
468 | xkb_mask |= (1 << xkb_mods.mod2); |
469 | if ((state & XCB_MOD_MASK_3) && xkb_mods.mod3 != XKB_MOD_INVALID) |
470 | xkb_mask |= (1 << xkb_mods.mod3); |
471 | if ((state & XCB_MOD_MASK_4) && xkb_mods.mod4 != XKB_MOD_INVALID) |
472 | xkb_mask |= (1 << xkb_mods.mod4); |
473 | if ((state & XCB_MOD_MASK_5) && xkb_mods.mod5 != XKB_MOD_INVALID) |
474 | xkb_mask |= (1 << xkb_mods.mod5); |
475 | |
476 | return xkb_mask; |
477 | } |
478 | |
479 | void QXcbKeyboard::updateXKBMods() |
480 | { |
481 | xkb_mods.shift = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), XKB_MOD_NAME_SHIFT); |
482 | xkb_mods.lock = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), XKB_MOD_NAME_CAPS); |
483 | xkb_mods.control = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), XKB_MOD_NAME_CTRL); |
484 | xkb_mods.mod1 = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), name: "Mod1" ); |
485 | xkb_mods.mod2 = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), name: "Mod2" ); |
486 | xkb_mods.mod3 = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), name: "Mod3" ); |
487 | xkb_mods.mod4 = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), name: "Mod4" ); |
488 | xkb_mods.mod5 = xkb_keymap_mod_get_index(keymap: m_xkbKeymap.get(), name: "Mod5" ); |
489 | } |
490 | |
491 | QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) |
492 | : QXcbObject(connection) |
493 | { |
494 | core_device_id = 0; |
495 | if (connection->hasXKB()) { |
496 | selectEvents(); |
497 | core_device_id = xkb_x11_get_core_keyboard_device_id(connection: xcb_connection()); |
498 | if (core_device_id == -1) { |
499 | qCWarning(lcQpaXcb, "failed to get core keyboard device info" ); |
500 | return; |
501 | } |
502 | } else { |
503 | m_key_symbols = xcb_key_symbols_alloc(c: xcb_connection()); |
504 | } |
505 | |
506 | updateKeymap(); |
507 | } |
508 | |
509 | QXcbKeyboard::~QXcbKeyboard() |
510 | { |
511 | if (m_key_symbols) |
512 | xcb_key_symbols_free(syms: m_key_symbols); |
513 | } |
514 | |
515 | void QXcbKeyboard::initialize() |
516 | { |
517 | auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); |
518 | QXkbCommon::setXkbContext(inputContext, context: m_xkbContext.get()); |
519 | } |
520 | |
521 | void QXcbKeyboard::selectEvents() |
522 | { |
523 | const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | |
524 | XCB_XKB_MAP_PART_KEY_SYMS | |
525 | XCB_XKB_MAP_PART_MODIFIER_MAP | |
526 | XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | |
527 | XCB_XKB_MAP_PART_KEY_ACTIONS | |
528 | XCB_XKB_MAP_PART_KEY_BEHAVIORS | |
529 | XCB_XKB_MAP_PART_VIRTUAL_MODS | |
530 | XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); |
531 | |
532 | const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | |
533 | XCB_XKB_EVENT_TYPE_MAP_NOTIFY | |
534 | XCB_XKB_EVENT_TYPE_STATE_NOTIFY); |
535 | |
536 | // XKB events are reported to all interested clients without regard |
537 | // to the current keyboard input focus or grab state |
538 | xcb_void_cookie_t select = xcb_xkb_select_events_checked( |
539 | c: xcb_connection(), |
540 | deviceSpec: XCB_XKB_ID_USE_CORE_KBD, |
541 | affectWhich: required_events, |
542 | clear: 0, |
543 | selectAll: required_events, |
544 | affectMap: required_map_parts, |
545 | map: required_map_parts, |
546 | details: nullptr); |
547 | |
548 | xcb_generic_error_t *error = xcb_request_check(c: xcb_connection(), cookie: select); |
549 | if (error) { |
550 | free(ptr: error); |
551 | qCWarning(lcQpaXcb, "failed to select notify events from XKB" ); |
552 | } |
553 | } |
554 | |
555 | void QXcbKeyboard::updateVModMapping() |
556 | { |
557 | xcb_xkb_get_names_value_list_t names_list; |
558 | |
559 | memset(s: &vmod_masks, c: 0, n: sizeof(vmod_masks)); |
560 | |
561 | auto name_reply = Q_XCB_REPLY(xcb_xkb_get_names, xcb_connection(), |
562 | XCB_XKB_ID_USE_CORE_KBD, |
563 | XCB_XKB_NAME_DETAIL_VIRTUAL_MOD_NAMES); |
564 | if (!name_reply) { |
565 | qWarning(msg: "Qt: failed to retrieve the virtual modifier names from XKB" ); |
566 | return; |
567 | } |
568 | |
569 | const void *buffer = xcb_xkb_get_names_value_list(R: name_reply.get()); |
570 | xcb_xkb_get_names_value_list_unpack(buffer: buffer, |
571 | nTypes: name_reply->nTypes, |
572 | indicators: name_reply->indicators, |
573 | virtualMods: name_reply->virtualMods, |
574 | groupNames: name_reply->groupNames, |
575 | nKeys: name_reply->nKeys, |
576 | nKeyAliases: name_reply->nKeyAliases, |
577 | nRadioGroups: name_reply->nRadioGroups, |
578 | which: name_reply->which, |
579 | aux: &names_list); |
580 | |
581 | int count = 0; |
582 | uint vmod_mask, bit; |
583 | char *vmod_name; |
584 | vmod_mask = name_reply->virtualMods; |
585 | // find the virtual modifiers for which names are defined. |
586 | for (bit = 1; vmod_mask; bit <<= 1) { |
587 | vmod_name = nullptr; |
588 | |
589 | if (!(vmod_mask & bit)) |
590 | continue; |
591 | |
592 | vmod_mask &= ~bit; |
593 | // virtualModNames - the list of virtual modifier atoms beginning with the lowest-numbered |
594 | // virtual modifier for which a name is defined and proceeding to the highest. |
595 | QByteArray atomName = connection()->atomName(atom: names_list.virtualModNames[count]); |
596 | vmod_name = atomName.data(); |
597 | count++; |
598 | |
599 | if (!vmod_name) |
600 | continue; |
601 | |
602 | // similarly we could retrieve NumLock, Super, Hyper modifiers if needed. |
603 | if (qstrcmp(str1: vmod_name, str2: "Alt" ) == 0) |
604 | vmod_masks.alt = bit; |
605 | else if (qstrcmp(str1: vmod_name, str2: "Meta" ) == 0) |
606 | vmod_masks.meta = bit; |
607 | else if (qstrcmp(str1: vmod_name, str2: "AltGr" ) == 0) |
608 | vmod_masks.altgr = bit; |
609 | else if (qstrcmp(str1: vmod_name, str2: "Super" ) == 0) |
610 | vmod_masks.super = bit; |
611 | else if (qstrcmp(str1: vmod_name, str2: "Hyper" ) == 0) |
612 | vmod_masks.hyper = bit; |
613 | } |
614 | } |
615 | |
616 | void QXcbKeyboard::updateVModToRModMapping() |
617 | { |
618 | xcb_xkb_get_map_map_t map; |
619 | |
620 | memset(s: &rmod_masks, c: 0, n: sizeof(rmod_masks)); |
621 | |
622 | auto map_reply = Q_XCB_REPLY(xcb_xkb_get_map, |
623 | xcb_connection(), |
624 | XCB_XKB_ID_USE_CORE_KBD, |
625 | XCB_XKB_MAP_PART_VIRTUAL_MODS, |
626 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); |
627 | if (!map_reply) { |
628 | qWarning(msg: "Qt: failed to retrieve the virtual modifier map from XKB" ); |
629 | return; |
630 | } |
631 | |
632 | const void *buffer = xcb_xkb_get_map_map(R: map_reply.get()); |
633 | xcb_xkb_get_map_map_unpack(buffer: buffer, |
634 | nTypes: map_reply->nTypes, |
635 | nKeySyms: map_reply->nKeySyms, |
636 | nKeyActions: map_reply->nKeyActions, |
637 | totalActions: map_reply->totalActions, |
638 | totalKeyBehaviors: map_reply->totalKeyBehaviors, |
639 | virtualMods: map_reply->nVModMapKeys, |
640 | totalKeyExplicit: map_reply->totalKeyExplicit, |
641 | totalModMapKeys: map_reply->totalModMapKeys, |
642 | totalVModMapKeys: map_reply->totalVModMapKeys, |
643 | present: map_reply->present, |
644 | aux: &map); |
645 | |
646 | uint vmod_mask, bit; |
647 | // the virtual modifiers mask for which a set of corresponding |
648 | // real modifiers is to be returned |
649 | vmod_mask = map_reply->virtualMods; |
650 | int count = 0; |
651 | |
652 | for (bit = 1; vmod_mask; bit <<= 1) { |
653 | uint modmap; |
654 | |
655 | if (!(vmod_mask & bit)) |
656 | continue; |
657 | |
658 | vmod_mask &= ~bit; |
659 | // real modifier bindings for the specified virtual modifiers |
660 | modmap = map.vmods_rtrn[count]; |
661 | count++; |
662 | |
663 | if (vmod_masks.alt == bit) |
664 | rmod_masks.alt = modmap; |
665 | else if (vmod_masks.meta == bit) |
666 | rmod_masks.meta = modmap; |
667 | else if (vmod_masks.altgr == bit) |
668 | rmod_masks.altgr = modmap; |
669 | else if (vmod_masks.super == bit) |
670 | rmod_masks.super = modmap; |
671 | else if (vmod_masks.hyper == bit) |
672 | rmod_masks.hyper = modmap; |
673 | } |
674 | } |
675 | |
676 | // Small helper: set modifier bit, if modifier position is valid |
677 | static inline void applyModifier(uint *mask, int modifierBit) |
678 | { |
679 | if (modifierBit >= 0 && modifierBit < 8) |
680 | *mask |= 1 << modifierBit; |
681 | } |
682 | |
683 | void QXcbKeyboard::updateModifiers(const KeysymModifierMap &keysymMods) |
684 | { |
685 | if (connection()->hasXKB()) { |
686 | updateVModMapping(); |
687 | updateVModToRModMapping(); |
688 | } else { |
689 | memset(s: &rmod_masks, c: 0, n: sizeof(rmod_masks)); |
690 | // Compute X modifier bits for Qt modifiers |
691 | applyModifier(mask: &rmod_masks.alt, modifierBit: keysymMods.value(XKB_KEY_Alt_L, defaultValue: -1)); |
692 | applyModifier(mask: &rmod_masks.alt, modifierBit: keysymMods.value(XKB_KEY_Alt_R, defaultValue: -1)); |
693 | applyModifier(mask: &rmod_masks.meta, modifierBit: keysymMods.value(XKB_KEY_Meta_L, defaultValue: -1)); |
694 | applyModifier(mask: &rmod_masks.meta, modifierBit: keysymMods.value(XKB_KEY_Meta_R, defaultValue: -1)); |
695 | applyModifier(mask: &rmod_masks.altgr, modifierBit: keysymMods.value(XKB_KEY_Mode_switch, defaultValue: -1)); |
696 | applyModifier(mask: &rmod_masks.super, modifierBit: keysymMods.value(XKB_KEY_Super_L, defaultValue: -1)); |
697 | applyModifier(mask: &rmod_masks.super, modifierBit: keysymMods.value(XKB_KEY_Super_R, defaultValue: -1)); |
698 | applyModifier(mask: &rmod_masks.hyper, modifierBit: keysymMods.value(XKB_KEY_Hyper_L, defaultValue: -1)); |
699 | applyModifier(mask: &rmod_masks.hyper, modifierBit: keysymMods.value(XKB_KEY_Hyper_R, defaultValue: -1)); |
700 | } |
701 | |
702 | resolveMaskConflicts(); |
703 | } |
704 | |
705 | // Small helper: check if an array of xcb_keycode_t contains a certain code |
706 | static inline bool keycodes_contains(xcb_keycode_t *codes, xcb_keycode_t which) |
707 | { |
708 | while (*codes != XCB_NO_SYMBOL) { |
709 | if (*codes == which) return true; |
710 | codes++; |
711 | } |
712 | return false; |
713 | } |
714 | |
715 | QXcbKeyboard::KeysymModifierMap QXcbKeyboard::keysymsToModifiers() |
716 | { |
717 | // The core protocol does not provide a convenient way to determine the mapping |
718 | // of modifier bits. Clients must retrieve and search the modifier map to determine |
719 | // the keycodes bound to each modifier, and then retrieve and search the keyboard |
720 | // mapping to determine the keysyms bound to the keycodes. They must repeat this |
721 | // process for all modifiers whenever any part of the modifier mapping is changed. |
722 | |
723 | KeysymModifierMap map; |
724 | |
725 | auto modMapReply = Q_XCB_REPLY(xcb_get_modifier_mapping, xcb_connection()); |
726 | if (!modMapReply) { |
727 | qWarning(msg: "Qt: failed to get modifier mapping" ); |
728 | return map; |
729 | } |
730 | |
731 | // for Alt and Meta L and R are the same |
732 | static const xcb_keysym_t symbols[] = { |
733 | XKB_KEY_Alt_L, XKB_KEY_Meta_L, XKB_KEY_Mode_switch, XKB_KEY_Super_L, XKB_KEY_Super_R, |
734 | XKB_KEY_Hyper_L, XKB_KEY_Hyper_R |
735 | }; |
736 | static const size_t numSymbols = sizeof symbols / sizeof *symbols; |
737 | |
738 | // Figure out the modifier mapping, ICCCM 6.6 |
739 | xcb_keycode_t* modKeyCodes[numSymbols]; |
740 | for (size_t i = 0; i < numSymbols; ++i) |
741 | modKeyCodes[i] = xcb_key_symbols_get_keycode(syms: m_key_symbols, keysym: symbols[i]); |
742 | |
743 | xcb_keycode_t *modMap = xcb_get_modifier_mapping_keycodes(R: modMapReply.get()); |
744 | const int modMapLength = xcb_get_modifier_mapping_keycodes_length(R: modMapReply.get()); |
745 | /* For each modifier of "Shift, Lock, Control, Mod1, Mod2, Mod3, |
746 | * Mod4, and Mod5" the modifier map contains keycodes_per_modifier |
747 | * key codes that are associated with a modifier. |
748 | * |
749 | * As an example, take this 'xmodmap' output: |
750 | * xmodmap: up to 4 keys per modifier, (keycodes in parentheses): |
751 | * |
752 | * shift Shift_L (0x32), Shift_R (0x3e) |
753 | * lock Caps_Lock (0x42) |
754 | * control Control_L (0x25), Control_R (0x69) |
755 | * mod1 Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd) |
756 | * mod2 Num_Lock (0x4d) |
757 | * mod3 |
758 | * mod4 Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf) |
759 | * mod5 ISO_Level3_Shift (0x5c), Mode_switch (0xcb) |
760 | * |
761 | * The corresponding raw modifier map would contain keycodes for: |
762 | * Shift_L (0x32), Shift_R (0x3e), 0, 0, |
763 | * Caps_Lock (0x42), 0, 0, 0, |
764 | * Control_L (0x25), Control_R (0x69), 0, 0, |
765 | * Alt_L (0x40), Alt_R (0x6c), Meta_L (0xcd), 0, |
766 | * Num_Lock (0x4d), 0, 0, 0, |
767 | * 0,0,0,0, |
768 | * Super_L (0x85), Super_R (0x86), Super_L (0xce), Hyper_L (0xcf), |
769 | * ISO_Level3_Shift (0x5c), Mode_switch (0xcb), 0, 0 |
770 | */ |
771 | |
772 | /* Create a map between a modifier keysym (as per the symbols array) |
773 | * and the modifier bit it's associated with (if any). |
774 | * As modMap contains key codes, search modKeyCodes for a match; |
775 | * if one is found we can look up the associated keysym. |
776 | * Together with the modifier index this will be used |
777 | * to compute a mapping between X modifier bits and Qt's |
778 | * modifiers (Alt, Ctrl etc). */ |
779 | for (int i = 0; i < modMapLength; i++) { |
780 | if (modMap[i] == XCB_NO_SYMBOL) |
781 | continue; |
782 | // Get key symbol for key code |
783 | for (size_t k = 0; k < numSymbols; k++) { |
784 | if (modKeyCodes[k] && keycodes_contains(codes: modKeyCodes[k], which: modMap[i])) { |
785 | // Key code is for modifier. Record mapping |
786 | xcb_keysym_t sym = symbols[k]; |
787 | /* As per modMap layout explanation above, dividing |
788 | * by keycodes_per_modifier gives the 'row' in the |
789 | * modifier map, which in turn is the modifier bit. */ |
790 | map[sym] = i / modMapReply->keycodes_per_modifier; |
791 | break; |
792 | } |
793 | } |
794 | } |
795 | |
796 | for (size_t i = 0; i < numSymbols; ++i) |
797 | free(ptr: modKeyCodes[i]); |
798 | |
799 | return map; |
800 | } |
801 | |
802 | void QXcbKeyboard::resolveMaskConflicts() |
803 | { |
804 | // if we don't have a meta key (or it's hidden behind alt), use super or hyper to generate |
805 | // Qt::Key_Meta and Qt::MetaModifier, since most newer XFree86/Xorg installations map the Windows |
806 | // key to Super |
807 | if (rmod_masks.alt == rmod_masks.meta) |
808 | rmod_masks.meta = 0; |
809 | |
810 | if (rmod_masks.meta == 0) { |
811 | // no meta keys... s/meta/super, |
812 | rmod_masks.meta = rmod_masks.super; |
813 | if (rmod_masks.meta == 0) { |
814 | // no super keys either? guess we'll use hyper then |
815 | rmod_masks.meta = rmod_masks.hyper; |
816 | } |
817 | } |
818 | |
819 | // translate Super/Hyper keys to Meta if we're using them as the MetaModifier |
820 | if (rmod_masks.meta && rmod_masks.meta == rmod_masks.super) |
821 | m_superAsMeta = true; |
822 | if (rmod_masks.meta && rmod_masks.meta == rmod_masks.hyper) |
823 | m_hyperAsMeta = true; |
824 | } |
825 | |
826 | void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code, |
827 | quint16 state, xcb_timestamp_t time, bool fromSendEvent) |
828 | { |
829 | if (!m_config) |
830 | return; |
831 | |
832 | QXcbWindow *source = connection()->platformWindowFromId(id: sourceWindow); |
833 | QXcbWindow *targetWindow = connection()->focusWindow() ? connection()->focusWindow() : source; |
834 | if (!targetWindow || !source) |
835 | return; |
836 | if (type == QEvent::KeyPress) |
837 | targetWindow->updateNetWmUserTime(timestamp: time); |
838 | |
839 | QXkbCommon::ScopedXKBState sendEventState; |
840 | if (fromSendEvent) { |
841 | // Have a temporary keyboard state filled in from state |
842 | // this way we allow for synthetic events to have different state |
843 | // from the current state i.e. you can have Alt+Ctrl pressed |
844 | // and receive a synthetic key event that has neither Alt nor Ctrl pressed |
845 | sendEventState.reset(p: xkb_state_new(keymap: m_xkbKeymap.get())); |
846 | if (!sendEventState) |
847 | return; |
848 | |
849 | xkb_mod_mask_t depressed = xkbModMask(state); |
850 | xkb_state_update_mask(state: sendEventState.get(), depressed_mods: depressed, latched_mods: 0, locked_mods: 0, depressed_layout: 0, latched_layout: 0, locked_layout: lockedGroup(state)); |
851 | } |
852 | |
853 | struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get(); |
854 | |
855 | xcb_keysym_t sym = xkb_state_key_get_one_sym(state: xkbState, key: code); |
856 | QString text = QXkbCommon::lookupString(state: xkbState, code); |
857 | |
858 | Qt::KeyboardModifiers modifiers = translateModifiers(s: state); |
859 | if (QXkbCommon::isKeypad(sym)) |
860 | modifiers |= Qt::KeypadModifier; |
861 | |
862 | int qtcode = QXkbCommon::keysymToQtKey(keysym: sym, modifiers, state: xkbState, code, superAsMeta: m_superAsMeta, hyperAsMeta: m_hyperAsMeta); |
863 | |
864 | if (type == QEvent::KeyPress) { |
865 | if (m_isAutoRepeat && m_autoRepeatCode != code) |
866 | // Some other key was pressed while we are auto-repeating on a different key. |
867 | m_isAutoRepeat = false; |
868 | } else { |
869 | m_isAutoRepeat = false; |
870 | // Look at the next event in the queue to see if we are auto-repeating. |
871 | connection()->eventQueue()->peek(option: QXcbEventQueue::PeekRetainMatch, |
872 | peeker: [this, time, code](xcb_generic_event_t *event, int type) { |
873 | if (type == XCB_KEY_PRESS) { |
874 | auto keyPress = reinterpret_cast<xcb_key_press_event_t *>(event); |
875 | m_isAutoRepeat = keyPress->time == time && keyPress->detail == code; |
876 | if (m_isAutoRepeat) |
877 | m_autoRepeatCode = code; |
878 | } |
879 | return true; |
880 | }); |
881 | } |
882 | |
883 | bool filtered = false; |
884 | if (auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext()) { |
885 | QKeyEvent event(type, qtcode, modifiers, code, sym, state, text, m_isAutoRepeat, text.size()); |
886 | event.setTimestamp(time); |
887 | filtered = inputContext->filterEvent(event: &event); |
888 | } |
889 | |
890 | if (!filtered) { |
891 | QWindow *window = targetWindow->window(); |
892 | #ifndef QT_NO_CONTEXTMENU |
893 | if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu) { |
894 | const QPoint globalPos = window->screen()->handle()->cursor()->pos(); |
895 | const QPoint pos = window->mapFromGlobal(pos: globalPos); |
896 | QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered: false, pos, globalPos, modifiers); |
897 | } |
898 | #endif |
899 | QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp: time, type, key: qtcode, modifiers, |
900 | nativeScanCode: code, nativeVirtualKey: sym, nativeModifiers: state, text, autorep: m_isAutoRepeat); |
901 | } |
902 | } |
903 | |
904 | static bool fromSendEvent(const void *event) |
905 | { |
906 | // From X11 protocol: Every event contains an 8-bit type code. The most |
907 | // significant bit in this code is set if the event was generated from |
908 | // a SendEvent request. |
909 | const xcb_generic_event_t *e = reinterpret_cast<const xcb_generic_event_t *>(event); |
910 | return (e->response_type & 0x80) != 0; |
911 | } |
912 | |
913 | void QXcbKeyboard::handleKeyPressEvent(const xcb_key_press_event_t *e) |
914 | { |
915 | handleKeyEvent(sourceWindow: e->event, type: QEvent::KeyPress, code: e->detail, state: e->state, time: e->time, fromSendEvent: fromSendEvent(event: e)); |
916 | } |
917 | |
918 | void QXcbKeyboard::handleKeyReleaseEvent(const xcb_key_release_event_t *e) |
919 | { |
920 | handleKeyEvent(sourceWindow: e->event, type: QEvent::KeyRelease, code: e->detail, state: e->state, time: e->time, fromSendEvent: fromSendEvent(event: e)); |
921 | } |
922 | |
923 | QT_END_NAMESPACE |
924 | |