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

source code of qtwayland/src/compositor/compositor_api/qwaylandkeyboard.cpp