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