1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlibinputkeyboard_p.h"
5#include <QtCore/QLoggingCategory>
6#include <QtGui/private/qguiapplication_p.h>
7#include <QtGui/private/qinputdevicemanager_p.h>
8#include <qpa/qwindowsysteminterface.h>
9#include <libinput.h>
10#if QT_CONFIG(xkbcommon)
11#include <xkbcommon/xkbcommon-keysyms.h>
12#include <xkbcommon/xkbcommon-names.h>
13#include <QtGui/private/qxkbcommon_p.h>
14#endif
15
16QT_BEGIN_NAMESPACE
17
18Q_DECLARE_LOGGING_CATEGORY(qLcLibInput)
19
20const int REPEAT_DELAY = 500;
21const int REPEAT_RATE = 100;
22
23QLibInputKeyboard::QLibInputKeyboard()
24{
25#if QT_CONFIG(xkbcommon)
26 qCDebug(qLcLibInput) << "Using xkbcommon for key mapping";
27 m_ctx = xkb_context_new(flags: XKB_CONTEXT_NO_FLAGS);
28 if (!m_ctx) {
29 qWarning(msg: "Failed to create xkb context");
30 return;
31 }
32 m_keymap = xkb_keymap_new_from_names(context: m_ctx, names: nullptr, flags: XKB_KEYMAP_COMPILE_NO_FLAGS);
33 if (!m_keymap) {
34 qCWarning(qLcLibInput, "Failed to compile keymap");
35 return;
36 }
37 m_state = xkb_state_new(keymap: m_keymap);
38 if (!m_state) {
39 qCWarning(qLcLibInput, "Failed to create xkb state");
40 return;
41 }
42
43 m_repeatTimer.setSingleShot(true);
44 connect(sender: &m_repeatTimer, signal: &QTimer::timeout, context: this, slot: &QLibInputKeyboard::handleRepeat);
45#else
46 qCWarning(qLcLibInput) << "xkbcommon not available, not performing key mapping";
47#endif
48}
49
50QLibInputKeyboard::~QLibInputKeyboard()
51{
52#if QT_CONFIG(xkbcommon)
53 if (m_state)
54 xkb_state_unref(state: m_state);
55 if (m_keymap)
56 xkb_keymap_unref(keymap: m_keymap);
57 if (m_ctx)
58 xkb_context_unref(context: m_ctx);
59#endif
60}
61
62void QLibInputKeyboard::processKey(libinput_event_keyboard *e)
63{
64#if QT_CONFIG(xkbcommon)
65 if (!m_ctx || !m_keymap || !m_state)
66 return;
67
68 const uint32_t keycode = libinput_event_keyboard_get_key(event: e) + 8;
69 const xkb_keysym_t sym = xkb_state_key_get_one_sym(state: m_state, key: keycode);
70 const bool pressed = libinput_event_keyboard_get_key_state(event: e) == LIBINPUT_KEY_STATE_PRESSED;
71
72 // Modifiers here is the modifier state before the event, i.e. not
73 // including the current key in case it is a modifier. See the XOR
74 // logic in QKeyEvent::modifiers(). ### QTBUG-73826
75 Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(state: m_state);
76
77 const QString text = QXkbCommon::lookupString(state: m_state, code: keycode);
78 const int qtkey = QXkbCommon::keysymToQtKey(keysym: sym, modifiers, state: m_state, code: keycode);
79
80 xkb_state_update_key(state: m_state, key: keycode, direction: pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
81
82 Qt::KeyboardModifiers modifiersAfterStateChange = QXkbCommon::modifiers(state: m_state, keysym: sym);
83 QGuiApplicationPrivate::inputDeviceManager()->setKeyboardModifiers(modifiersAfterStateChange);
84
85 QWindowSystemInterface::handleExtendedKeyEvent(window: nullptr,
86 type: pressed ? QEvent::KeyPress : QEvent::KeyRelease,
87 key: qtkey, modifiers, nativeScanCode: keycode, nativeVirtualKey: sym, nativeModifiers: modifiers, text);
88
89 if (pressed && xkb_keymap_key_repeats(keymap: m_keymap, key: keycode)) {
90 m_repeatData.qtkey = qtkey;
91 m_repeatData.mods = modifiers;
92 m_repeatData.nativeScanCode = keycode;
93 m_repeatData.virtualKey = sym;
94 m_repeatData.nativeMods = modifiers;
95 m_repeatData.unicodeText = text;
96 m_repeatData.repeatCount = 1;
97 m_repeatTimer.setInterval(REPEAT_DELAY);
98 m_repeatTimer.start();
99 } else if (m_repeatTimer.isActive()) {
100 m_repeatTimer.stop();
101 }
102
103#else
104 Q_UNUSED(e);
105#endif
106}
107
108#if QT_CONFIG(xkbcommon)
109void QLibInputKeyboard::handleRepeat()
110{
111 QWindowSystemInterface::handleExtendedKeyEvent(window: nullptr, type: QEvent::KeyPress,
112 key: m_repeatData.qtkey, modifiers: m_repeatData.mods,
113 nativeScanCode: m_repeatData.nativeScanCode, nativeVirtualKey: m_repeatData.virtualKey, nativeModifiers: m_repeatData.nativeMods,
114 text: m_repeatData.unicodeText, autorep: true, count: m_repeatData.repeatCount);
115 m_repeatData.repeatCount += 1;
116 m_repeatTimer.setInterval(REPEAT_RATE);
117 m_repeatTimer.start();
118}
119#endif
120
121QT_END_NAMESPACE
122

source code of qtbase/src/platformsupport/input/libinput/qlibinputkeyboard.cpp