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