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
18QT_BEGIN_NAMESPACE
19
20Qt::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.) */
42static 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
59static 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
81struct 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
323void 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
332void 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
380QList<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
385void 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
401static xkb_layout_index_t lockedGroup(quint16 state)
402{
403 return (state >> 13) & 3; // bits 13 and 14 report the state keyboard group
404}
405
406void 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
428void 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
446void 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
455xkb_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
479void 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
491QXcbKeyboard::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
509QXcbKeyboard::~QXcbKeyboard()
510{
511 if (m_key_symbols)
512 xcb_key_symbols_free(syms: m_key_symbols);
513}
514
515void QXcbKeyboard::initialize()
516{
517 auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
518 QXkbCommon::setXkbContext(inputContext, context: m_xkbContext.get());
519}
520
521void 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
555void 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
616void 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
677static inline void applyModifier(uint *mask, int modifierBit)
678{
679 if (modifierBit >= 0 && modifierBit < 8)
680 *mask |= 1 << modifierBit;
681}
682
683void 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
706static 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
715QXcbKeyboard::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
802void 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
826void 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
904static 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
913void 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
918void 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
923QT_END_NAMESPACE
924

source code of qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp