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

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