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