Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandkeyboard.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2017 Klarälvdalens Datakonsult AB (KDAB).
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4// Qt-Security score:critical reason:network-protocol
5
9#include <QtWaylandCompositor/QWaylandKeymap>
10#include <QtWaylandCompositor/QWaylandCompositor>
11#include <QtWaylandCompositor/QWaylandSeat>
12#include <QtWaylandCompositor/QWaylandClient>
13
14#include <QtCore/QFile>
15#include <QtCore/QStandardPaths>
16
17#include <QKeyEvent>
18#include <fcntl.h>
19#include <unistd.h>
20#if QT_CONFIG(xkbcommon)
21#include <sys/mman.h>
22#include <sys/types.h>
23#include <xkbcommon/xkbcommon-names.h>
24
25 // xkbcommon 1.8 and later defines, otherwise we define it by hand
26#ifndef XKB_MOD_NAME_MOD5
27#define XKB_MOD_NAME_MOD5 "Mod5"
28#endif
29#endif
30
31QT_BEGIN_NAMESPACE
32
33QWaylandKeyboardPrivate::QWaylandKeyboardPrivate(QWaylandSeat *seat)
34 : seat(seat)
35{
36}
37
38QWaylandKeyboardPrivate::~QWaylandKeyboardPrivate()
39{
40#if QT_CONFIG(xkbcommon)
41 if (xkbContext()) {
42 if (keymap_area)
43 munmap(keymap_area, keymap_size);
44 if (keymap_fd >= 0)
45 close(keymap_fd);
46 }
47#endif
48}
49
50QWaylandKeyboardPrivate *QWaylandKeyboardPrivate::get(QWaylandKeyboard *keyboard)
51{
52 return keyboard->d_func();
53}
54
55void QWaylandKeyboardPrivate::checkFocusResource(Resource *keyboardResource)
56{
57 if (!keyboardResource || !focus)
58 return;
59
60 // this is already the current resource, do no send enter twice
61 if (focusResource == keyboardResource)
62 return;
63
64 // check if new wl_keyboard resource is from the client owning the focus surface
65 if (wl_resource_get_client(focus->resource()) == keyboardResource->client()) {
66 sendEnter(focus, keyboardResource);
67 focusResource = keyboardResource;
68 }
69}
70
71void QWaylandKeyboardPrivate::sendEnter(QWaylandSurface *surface, Resource *keyboardResource)
72{
73 uint32_t serial = compositor()->nextSerial();
74 send_modifiers(keyboardResource->handle, serial, modsDepressed, modsLatched, modsLocked, group);
75 send_enter(keyboardResource->handle, serial, surface->resource(), QByteArray::fromRawData((char *)keys.data(), keys.size() * sizeof(uint32_t)));
76}
77
78void QWaylandKeyboardPrivate::focused(QWaylandSurface *surface)
79{
80 if (surface && surface->isCursorSurface())
81 surface = nullptr;
82 if (focus != surface) {
83 if (focusResource) {
84 uint32_t serial = compositor()->nextSerial();
85 send_leave(focusResource->handle, serial, focus->resource());
86 }
87 focusDestroyListener.reset();
88 if (surface)
89 focusDestroyListener.listenForDestruction(surface->resource());
90 }
91
92 Resource *resource = surface ? resourceMap().value(surface->waylandClient()) : 0;
93
94 if (resource && (focus != surface || focusResource != resource))
95 sendEnter(surface, resource);
96
97 focusResource = resource;
98 focus = surface;
99 Q_EMIT q_func()->focusChanged(focus);
100}
101
102
103void QWaylandKeyboardPrivate::keyboard_bind_resource(wl_keyboard::Resource *resource)
104{
105 // Send repeat information
106 if (resource->version() >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
107 send_repeat_info(resource->handle, repeatRate, repeatDelay);
108
109#if QT_CONFIG(xkbcommon)
110 if (xkbContext()) {
111 send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
112 keymap_fd, keymap_size);
113 } else
114#endif
115 {
116 int null_fd = open("/dev/null", O_RDONLY);
117 send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP,
118 null_fd, 0);
119 close(null_fd);
120 }
121 checkFocusResource(resource);
122}
123
124void QWaylandKeyboardPrivate::keyboard_destroy_resource(wl_keyboard::Resource *resource)
125{
126 if (focusResource == resource)
127 focusResource = nullptr;
128}
129
130void QWaylandKeyboardPrivate::keyboard_release(wl_keyboard::Resource *resource)
131{
132 wl_resource_destroy(resource->handle);
133}
134
135void QWaylandKeyboardPrivate::keyEvent(uint code, uint32_t state)
136{
137 uint key = toWaylandKey(code);
138
139 if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
140 keys << key;
141 } else {
142 keys.removeAll(key);
143 }
144}
145
146void QWaylandKeyboardPrivate::sendKeyEvent(uint code, uint32_t state)
147{
148 uint32_t time = compositor()->currentTimeMsecs();
149 uint32_t serial = compositor()->nextSerial();
150 uint key = toWaylandKey(code);
151 if (focusResource)
152 send_key(focusResource->handle, serial, time, key, state);
153}
154
155#if QT_CONFIG(xkbcommon)
156void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable()
157{
158 if (!scanCodesByQtKey.isEmpty() || !xkbState())
159 return;
160
161 if (xkb_keymap *keymap = xkb_state_get_keymap(xkbState())) {
162 xkb_keymap_key_for_each(keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d){
163 auto *scanCodesByQtKey = static_cast<QMap<ScanCodeKey, uint>*>(d);
164 uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode);
165 for (uint layout = 0; layout < numLayouts; ++layout) {
166 const xkb_keysym_t *syms = nullptr;
167 xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, 0, &syms);
168 if (!syms)
169 continue;
170
171 Qt::KeyboardModifiers mods = {};
172 int qtKey = QXkbCommon::keysymToQtKey(syms[0], mods, nullptr, 0, false, false);
173 if (qtKey != 0)
174 scanCodesByQtKey->insert({layout, qtKey}, keycode);
175 }
176 }, &scanCodesByQtKey);
177
178 shiftIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
179 controlIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
180 altIndex = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT);
181 mod5Index = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_MOD5); // AltGr / ISO_Level3_Shift
182 }
183}
184
185void QWaylandKeyboardPrivate::resetKeyboardState()
186{
187 if (!xkbContext())
188 return;
189
190 while (!keys.isEmpty()) {
191 uint32_t code = fromWaylandKey(keys.first());
192 keyEvent(code, WL_KEYBOARD_KEY_STATE_RELEASED);
193 updateModifierState(code, WL_KEYBOARD_KEY_STATE_RELEASED);
194 }
195}
196#endif
197
198void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state)
199{
200#if QT_CONFIG(xkbcommon)
201 if (!xkbContext())
202 return;
203
204 xkb_state_update_key(xkbState(), code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
205
206 uint32_t modsDepressed = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_DEPRESSED);
207 uint32_t modsLatched = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LATCHED);
208 uint32_t modsLocked = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LOCKED);
209 uint32_t group = xkb_state_serialize_layout(xkbState(), XKB_STATE_LAYOUT_EFFECTIVE);
210
211 if (this->modsDepressed == modsDepressed
212 && this->modsLatched == modsLatched
213 && this->modsLocked == modsLocked
214 && this->group == group)
215 return;
216
217 this->modsDepressed = modsDepressed;
218 this->modsLatched = modsLatched;
219 this->modsLocked = modsLocked;
220 this->group = group;
221
222 if (focusResource) {
223 send_modifiers(focusResource->handle, compositor()->nextSerial(), modsDepressed,
224 modsLatched, modsLocked, group);
225
226 Qt::KeyboardModifiers currentState = Qt::NoModifier;
227 if (xkb_state_mod_index_is_active(xkbState(), shiftIndex, XKB_STATE_MODS_EFFECTIVE) == 1)
228 currentState |= Qt::ShiftModifier;
229 if (xkb_state_mod_index_is_active(xkbState(), controlIndex, XKB_STATE_MODS_EFFECTIVE) == 1)
230 currentState |= Qt::ControlModifier;
231 if (xkb_state_mod_index_is_active(xkbState(), altIndex, XKB_STATE_MODS_EFFECTIVE) == 1)
232 currentState |= Qt::AltModifier;
233 if (mod5Index != XKB_MOD_INVALID
234 && xkb_state_mod_index_is_active(xkbState(), mod5Index, XKB_STATE_MODS_EFFECTIVE) == 1)
235 currentState |= Qt::GroupSwitchModifier;
236 currentModifierState = currentState;
237 }
238#else
239 Q_UNUSED(code);
240 Q_UNUSED(state);
241#endif
242}
243
244// If there is no key currently pressed, update the keymap right away.
245// Otherwise, delay the update when keys are released
246// see http://lists.freedesktop.org/archives/wayland-devel/2013-October/011395.html
247void QWaylandKeyboardPrivate::maybeUpdateKeymap()
248{
249 // There must be no keys pressed when changing the keymap,
250 // see http://lists.freedesktop.org/archives/wayland-devel/2013-October/011395.html
251 if (!pendingKeymap || !keys.isEmpty())
252 return;
253
254 pendingKeymap = false;
255#if QT_CONFIG(xkbcommon)
256 if (!xkbContext())
257 return;
258
259 createXKBKeymap();
260 const auto resMap = resourceMap();
261 for (Resource *res : resMap) {
262 send_keymap(res->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size);
263 }
264
265 xkb_state_update_mask(xkbState(), 0, modsLatched, modsLocked, 0, 0, 0);
266 if (focusResource)
267 send_modifiers(focusResource->handle,
268 compositor()->nextSerial(),
269 modsDepressed,
270 modsLatched,
271 modsLocked,
272 group);
273#endif
274}
275
276// In all current XKB keymaps there's a constant offset of 8 (for historical
277// reasons) from hardware/evdev scancodes to XKB keycodes. On X11, we pass
278// XKB keycodes (as sent by X server) via QKeyEvent::nativeScanCode. eglfs+evdev
279// adds 8 for consistency, see qtbase/05c07c7636012ebb4131ca099ca4ea093af76410.
280// eglfs+libinput also adds 8, for the same reason. Wayland protocol uses
281// hardware/evdev scancodes, thus we need to subtract 8 before sending the event
282// out and add it when mapping back.
283#define QTWAYLANDKEYBOARD_XKB_HISTORICAL_OFFSET 8
284
285uint QWaylandKeyboardPrivate::fromWaylandKey(const uint key)
286{
287#if QT_CONFIG(xkbcommon)
288 const uint offset = QTWAYLANDKEYBOARD_XKB_HISTORICAL_OFFSET;
289 return key + offset;
290#else
291 return key;
292#endif
293}
294
295uint QWaylandKeyboardPrivate::toWaylandKey(const uint nativeScanCode)
296{
297#if QT_CONFIG(xkbcommon)
298 const uint offset = QTWAYLANDKEYBOARD_XKB_HISTORICAL_OFFSET;
299 Q_ASSERT(nativeScanCode >= offset);
300 return nativeScanCode - offset;
301#else
302 return nativeScanCode;
303#endif
304}
305
306#if QT_CONFIG(xkbcommon)
307static int createAnonymousFile(size_t size)
308{
309 QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
310 if (path.isEmpty())
311 return -1;
312
313 QByteArray name = QFile::encodeName(path + QStringLiteral("/qtwayland-XXXXXX"));
314
315 int fd = mkstemp(name.data());
316 if (fd < 0)
317 return -1;
318
319 long flags = fcntl(fd, F_GETFD);
320 if (flags == -1 || fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
321 close(fd);
322 fd = -1;
323 }
324 unlink(name.constData());
325
326 if (fd < 0)
327 return -1;
328
329 if (ftruncate(fd, size) < 0) {
330 close(fd);
331 return -1;
332 }
333
334 return fd;
335}
336
337void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap)
338{
339 char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1);
340 if (!keymap_str) {
341 qWarning("Failed to compile global XKB keymap");
342 return;
343 }
344
345 keymap_size = strlen(keymap_str) + 1;
346 if (keymap_fd >= 0)
347 close(keymap_fd);
348 keymap_fd = createAnonymousFile(keymap_size);
349 if (keymap_fd < 0) {
350 qWarning("Failed to create anonymous file of size %lu", static_cast<unsigned long>(keymap_size));
351 return;
352 }
353
354 keymap_area = static_cast<char *>(mmap(nullptr, keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymap_fd, 0));
355 if (keymap_area == MAP_FAILED) {
356 close(keymap_fd);
357 keymap_fd = -1;
358 qWarning("Failed to map shared memory segment");
359 return;
360 }
361
362 strcpy(keymap_area, keymap_str);
363 free(keymap_str);
364
365 mXkbState.reset(xkb_state_new(keymap));
366 if (!mXkbState)
367 qWarning("Failed to create XKB state");
368}
369
370void QWaylandKeyboardPrivate::createXKBKeymap()
371{
372 if (!xkbContext())
373 return;
374
375 QWaylandKeymap *keymap = seat->keymap();
376 QByteArray rules = keymap->rules().toLocal8Bit();
377 QByteArray model = keymap->model().toLocal8Bit();
378 QByteArray layout = keymap->layout().toLocal8Bit();
379 QByteArray variant = keymap->variant().toLocal8Bit();
380 QByteArray options = keymap->options().toLocal8Bit();
381
382 if (!layout.isEmpty() && !layout.contains("us")) {
383 // This is needed for shortucts like "ctrl+c" to function even when
384 // user has selected only non-latin keyboard layouts, e.g. 'ru'.
385 layout.append(",us");
386 variant.append(",");
387 }
388
389 struct xkb_rule_names rule_names = {
390 rules.constData(),
391 model.constData(),
392 layout.constData(),
393 variant.constData(),
394 options.constData()
395 };
396
397 QXkbCommon::ScopedXKBKeymap xkbKeymap(xkb_keymap_new_from_names(xkbContext(), &rule_names,
398 XKB_KEYMAP_COMPILE_NO_FLAGS));
399 if (xkbKeymap) {
400 scanCodesByQtKey.clear();
401 createXKBState(xkbKeymap.get());
402 } else {
403 qWarning("Failed to load the '%s' XKB keymap.", qPrintable(keymap->layout()));
404 }
405}
406#endif // QT_CONFIG(xkbcommon)
407
408void QWaylandKeyboardPrivate::sendRepeatInfo()
409{
410 const auto resMap = resourceMap();
411 for (Resource *resource : resMap) {
412 if (resource->version() >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION)
413 send_repeat_info(resource->handle, repeatRate, repeatDelay);
414 }
415}
416
417/*!
418 * \class QWaylandKeyboard
419 * \inmodule QtWaylandCompositor
420 * \since 5.8
421 * \brief The QWaylandKeyboard class represents a keyboard device.
422 *
423 * This class provides access to the keyboard device in a QWaylandSeat. It corresponds to
424 * the Wayland interface wl_keyboard.
425 */
426
427/*!
428 * Constructs a QWaylandKeyboard for the given \a seat and with the given \a parent.
429 */
430QWaylandKeyboard::QWaylandKeyboard(QWaylandSeat *seat, QObject *parent)
431 : QWaylandObject(* new QWaylandKeyboardPrivate(seat), parent)
432{
433 Q_D(QWaylandKeyboard);
434 connect(&d->focusDestroyListener, &QWaylandDestroyListener::fired, this, &QWaylandKeyboard::focusDestroyed);
435 auto keymap = seat->keymap();
436 connect(keymap, &QWaylandKeymap::layoutChanged, this, &QWaylandKeyboard::updateKeymap);
437 connect(keymap, &QWaylandKeymap::variantChanged, this, &QWaylandKeyboard::updateKeymap);
438 connect(keymap, &QWaylandKeymap::optionsChanged, this, &QWaylandKeyboard::updateKeymap);
439 connect(keymap, &QWaylandKeymap::rulesChanged, this, &QWaylandKeyboard::updateKeymap);
440 connect(keymap, &QWaylandKeymap::modelChanged, this, &QWaylandKeyboard::updateKeymap);
441#if QT_CONFIG(xkbcommon)
442 d->createXKBKeymap();
443#endif
444}
445
446/*!
447 * Returns the seat for this QWaylandKeyboard.
448 */
449QWaylandSeat *QWaylandKeyboard::seat() const
450{
451 Q_D(const QWaylandKeyboard);
452 return d->seat;
453}
454
455/*!
456 * Returns the compositor for this QWaylandKeyboard.
457 */
458QWaylandCompositor *QWaylandKeyboard::compositor() const
459{
460 Q_D(const QWaylandKeyboard);
461 return d->seat->compositor();
462}
463
464/*!
465 * \internal
466 */
467void QWaylandKeyboard::focusDestroyed(void *data)
468{
469 Q_UNUSED(data);
470 Q_D(QWaylandKeyboard);
471 d->focusDestroyListener.reset();
472
473 d->focus = nullptr;
474 d->focusResource = nullptr;
475}
476
477void QWaylandKeyboard::updateKeymap()
478{
479 Q_D(QWaylandKeyboard);
480 d->pendingKeymap = true;
481 d->maybeUpdateKeymap();
482}
483
484/*!
485 * Returns the client that currently has keyboard focus.
486 */
487QWaylandClient *QWaylandKeyboard::focusClient() const
488{
489 Q_D(const QWaylandKeyboard);
490 if (!d->focusResource)
491 return nullptr;
492 return QWaylandClient::fromWlClient(compositor(), d->focusResource->client());
493}
494
495/*!
496 * Sends the current key modifiers to \a client with the given \a serial.
497 */
498void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint32_t serial)
499{
500 Q_D(QWaylandKeyboard);
501 QtWaylandServer::wl_keyboard::Resource *resource = d->resourceMap().value(client->client());
502 if (resource)
503 d->send_modifiers(resource->handle, serial, d->modsDepressed, d->modsLatched, d->modsLocked, d->group);
504}
505
506/*!
507 * Sends a key press event with the key \a code to the current keyboard focus.
508 */
509void QWaylandKeyboard::sendKeyPressEvent(uint code)
510{
511 Q_D(QWaylandKeyboard);
512 d->sendKeyEvent(code, WL_KEYBOARD_KEY_STATE_PRESSED);
513}
514
515/*!
516 * Sends a key release event with the key \a code to the current keyboard focus.
517 */
518void QWaylandKeyboard::sendKeyReleaseEvent(uint code)
519{
520 Q_D(QWaylandKeyboard);
521 d->sendKeyEvent(code, WL_KEYBOARD_KEY_STATE_RELEASED);
522}
523
524void QWaylandKeyboardPrivate::checkAndRepairModifierState(QKeyEvent *ke)
525{
526#if QT_CONFIG(xkbcommon)
527 if (ke->modifiers() != currentModifierState) {
528 if (focusResource && ke->key() != Qt::Key_Shift
529 && ke->key() != Qt::Key_Control && ke->key() != Qt::Key_Alt
530 && ke->key() != Qt::Key_AltGr) {
531 // Only repair the state for non-modifier keys
532 // ### slightly awkward because the standard modifier handling
533 // is done by QtWayland::WindowSystemEventHandler after the
534 // key event is delivered
535 uint32_t mods = 0;
536
537 if (shiftIndex == 0 && controlIndex == 0)
538 maybeUpdateXkbScanCodeTable();
539
540 if (ke->modifiers() & Qt::ShiftModifier)
541 mods |= 1 << shiftIndex;
542 if (ke->modifiers() & Qt::ControlModifier)
543 mods |= 1 << controlIndex;
544 if (ke->modifiers() & Qt::AltModifier)
545 mods |= 1 << altIndex;
546 if ((ke->modifiers() & Qt::GroupSwitchModifier) && mod5Index != XKB_MOD_INVALID)
547 mods |= 1 << mod5Index;
548 qCDebug(qLcWaylandCompositor) << "Keyboard modifier state mismatch detected for event" << ke << "state:" << currentModifierState << "repaired:" << Qt::hex << mods;
549 // Preserve modsLocked so that NumLock and CapsLock state is not lost
550 send_modifiers(focusResource->handle, compositor()->nextSerial(), mods,
551 0, modsLocked, group);
552 currentModifierState = ke->modifiers();
553 }
554 }
555#else
556 Q_UNUSED(ke);
557#endif
558}
559
560/*!
561 * \property QWaylandKeyboard::repeatRate
562 *
563 * This property holds the rate at which key repeat events are generated by the
564 * keyboard, expressed in characters per second.
565 */
566
567/*!
568 * Returns the current repeat rate.
569 */
570quint32 QWaylandKeyboard::repeatRate() const
571{
572 Q_D(const QWaylandKeyboard);
573 return d->repeatRate;
574}
575
576/*!
577 * Sets the repeat rate to \a rate.
578 */
579void QWaylandKeyboard::setRepeatRate(quint32 rate)
580{
581 Q_D(QWaylandKeyboard);
582
583 if (d->repeatRate == rate)
584 return;
585
586 d->sendRepeatInfo();
587
588 d->repeatRate = rate;
589 Q_EMIT repeatRateChanged(rate);
590}
591
592/*!
593 * \property QWaylandKeyboard::repeatDelay
594 *
595 * This property holds the delay before key repeat events begin, expressed in
596 * milliseconds.
597 */
598
599/*!
600 * Returns the current repeat delay.
601 */
602quint32 QWaylandKeyboard::repeatDelay() const
603{
604 Q_D(const QWaylandKeyboard);
605 return d->repeatDelay;
606}
607
608/*!
609 * Sets the repeat delay to \a delay.
610 */
611void QWaylandKeyboard::setRepeatDelay(quint32 delay)
612{
613 Q_D(QWaylandKeyboard);
614
615 if (d->repeatDelay == delay)
616 return;
617
618 d->sendRepeatInfo();
619
620 d->repeatDelay = delay;
621 Q_EMIT repeatDelayChanged(delay);
622}
623
624/*!
625 * Returns the currently focused surface.
626 */
627QWaylandSurface *QWaylandKeyboard::focus() const
628{
629 Q_D(const QWaylandKeyboard);
630 return d->focus;
631}
632
633/*!
634 * Sets the current focus to \a surface.
635 */
636void QWaylandKeyboard::setFocus(QWaylandSurface *surface)
637{
638 Q_D(QWaylandKeyboard);
639 d->focused(surface);
640}
641
642/*!
643 * \internal
644 */
645void QWaylandKeyboard::addClient(QWaylandClient *client, uint32_t id, uint32_t version)
646{
647 Q_D(QWaylandKeyboard);
648 d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_keyboard::interfaceVersion(), version));
649}
650
651uint QWaylandKeyboard::keyToScanCode(int qtKey) const
652{
653 uint scanCode = 0;
654#if QT_CONFIG(xkbcommon)
655 Q_D(const QWaylandKeyboard);
656 const_cast<QWaylandKeyboardPrivate *>(d)->maybeUpdateXkbScanCodeTable();
657 scanCode = d->scanCodesByQtKey.value({d->group, qtKey}, 0);
658#else
659 Q_UNUSED(qtKey);
660#endif
661 return scanCode;
662}
663
664QT_END_NAMESPACE
665
666#include "moc_qwaylandkeyboard.cpp"