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 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | Qt::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.) */ |
78 | static 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 | |
95 | static 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 | |
117 | struct 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 | |
359 | void 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 | |
368 | void 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 | |
379 | void 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 | |
432 | QList<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 | |
437 | void 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 | |
453 | static xkb_layout_index_t lockedGroup(quint16 state) |
454 | { |
455 | return (state >> 13) & 3; // bits 13 and 14 report the state keyboard group |
456 | } |
457 | |
458 | void 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 | |
480 | void 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 | |
498 | void 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 | |
507 | xkb_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 | |
531 | void 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 | |
543 | QXcbKeyboard::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 | |
561 | QXcbKeyboard::~QXcbKeyboard() |
562 | { |
563 | if (m_key_symbols) |
564 | xcb_key_symbols_free(syms: m_key_symbols); |
565 | } |
566 | |
567 | void QXcbKeyboard::initialize() |
568 | { |
569 | auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext(); |
570 | QXkbCommon::setXkbContext(inputContext, context: m_xkbContext.get()); |
571 | } |
572 | |
573 | void 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 | |
607 | void 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 | |
668 | void 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 |
729 | static inline void applyModifier(uint *mask, int modifierBit) |
730 | { |
731 | if (modifierBit >= 0 && modifierBit < 8) |
732 | *mask |= 1 << modifierBit; |
733 | } |
734 | |
735 | void 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 |
758 | static 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 | |
767 | QXcbKeyboard::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 | |
854 | void 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 | |
878 | void 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 | |
956 | static 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 | |
965 | void 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 | |
970 | void 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 | |
975 | QT_END_NAMESPACE |
976 | |