Krita Source Code Documentation
Loading...
Searching...
No Matches
KisWaylandKeyboardWatcher.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <kis_debug.h>
10
11#include <QWaylandClientExtension>
12#include <qwayland-wayland.h>
13
14#include <config-qt-wayland-patches-present.h>
15#include <QtGui/private/qxkbcommon_p.h>
16
17#include <sys/mman.h>
18#include <unistd.h>
19
20
21#if KRITA_QT_HAS_XKB_CONTEXT_IN_NATIVE_INTERFACE
22
23#include <QGuiApplication>
24#include <QtWaylandClient/private/qwaylandintegration_p.h>
25
26#else
27
28#include <QtGui/private/qguiapplication_p.h>
29#include <QtWaylandClient/private/qwaylandintegration_p.h>
30#include <QtWaylandClient/private/qwaylanddisplay_p.h>
31
32#endif /* KRITA_QT_HAS_XKB_CONTEXT_IN_NATIVE_INTERFACE */
33
34static struct xkb_context *getGlobalXkbContextFromQt() {
35#if KRITA_QT_HAS_XKB_CONTEXT_IN_NATIVE_INTERFACE
36 auto waylandApp = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
37 return waylandApp ? waylandApp->xkbContext() : nullptr;
38#else
43 QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
44 auto waylandIntegration = dynamic_cast<QtWaylandClient::QWaylandIntegration *>(platformIntegration);
45 return waylandIntegration ? waylandIntegration->display()->xkbContext() : nullptr;
46#endif /* KRITA_QT_HAS_XKB_CONTEXT_IN_NATIVE_INTERFACE */
47}
48
49/****************************************************************************/
50/* KisWaylandKeyboardWatcher::Watcher::Keyboard */
51/****************************************************************************/
52
53class KisWaylandKeyboardWatcher::Keyboard : public QtWayland::wl_keyboard
54{
55public:
56 Keyboard(::wl_keyboard *keyboard)
57 : wl_keyboard(keyboard)
58 {
59 }
60
62 {
63 release();
64 }
65
67 {
68 QList<Qt::Key> keys;
69
70 if (mXkbState) {
71 for (uint32_t key : std::as_const(mNativeKeys)) {
72 auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
73
74 xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState.get(), code);
75 Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(mXkbState.get(), sym);
76
77 Qt::Key qtKey = static_cast<Qt::Key>(QXkbCommon::keysymToQtKey(sym, modifiers, mXkbState.get(), code));
78
79 if (qtKey != Qt::Key_unknown)
80 keys.append(qtKey);
81 }
82 }
83
84 return keys;
85 }
86
87 bool hasKeyboardFocus() const
88 {
89 return m_focus;
90 }
91
92 Qt::KeyboardModifiers modifiers() const
93 {
94 Qt::KeyboardModifiers ret = Qt::NoModifier;
95 if (!mXkbState)
96 return ret;
97 ret = QXkbCommon::modifiers(mXkbState.get());
98 return ret;
99 }
100
101private:
102 void keyboard_keymap(uint32_t format, int32_t fd, uint32_t size) override
103 {
104 mKeymapFormat = format;
105
106 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
107 qDebug() << "unknown keymap format:" << format;
108 close(fd);
109 return;
110 }
111
112 char *map_str = static_cast<char *>(mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0));
113 if (map_str == MAP_FAILED) {
114 close(fd);
115 return;
116 }
117
118 struct xkb_context *xkbContext = getGlobalXkbContextFromQt();
119 if (xkbContext) {
120 mXkbKeymap.reset(xkb_keymap_new_from_string(xkbContext, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS));
121 QXkbCommon::verifyHasLatinLayout(mXkbKeymap.get());
122 }
123
124 munmap(map_str, size);
125 close(fd);
126
127 if (mXkbKeymap)
128 mXkbState.reset(xkb_state_new(mXkbKeymap.get()));
129 else
130 mXkbState.reset(nullptr);
131 }
132
133 void keyboard_key([[maybe_unused]] uint32_t serial, [[maybe_unused]] uint32_t time, uint32_t key, uint32_t state) override
134 {
135 const bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
136 if (isDown) {
137 auto it = std::find(mNativeKeys.begin(), mNativeKeys.end(), key);
138 if (it != mNativeKeys.end()) {
139 qWarning().nospace() << "WARNING: got a key-down for a key (" << Qt::hex << Qt::showbase << key << ") which is reported to be pressed already!";
140 } else {
141 mNativeKeys.append(key);
142 }
143 } else {
144 auto it = std::find(mNativeKeys.begin(), mNativeKeys.end(), key);
145 if (it == mNativeKeys.end()) {
146 qWarning().nospace() << "WARNING: got a key-up for a key (" << Qt::hex << Qt::showbase << key << ") which is NOT reported to be pressed!";
147 } else {
148 mNativeKeys.erase(it);
149 }
150 }
151 }
152 void keyboard_modifiers([[maybe_unused]] uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) override
153 {
154 if (mXkbState)
155 xkb_state_update_mask(mXkbState.get(), mods_depressed, mods_latched, mods_locked, 0, 0, group);
156 mNativeModifiers = mods_depressed | mods_latched | mods_locked;
157 }
158
159 void keyboard_enter([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface, [[maybe_unused]] wl_array *keys) override
160 {
161 mNativeKeys.clear();
162 mNativeKeys.reserve(keys->size / sizeof(uint32_t));
163
164 for (const uint32_t *keyPtr = static_cast<const uint32_t *>(keys->data);
165 reinterpret_cast<const char *>(keyPtr) < static_cast<const char *>(keys->data) + keys->size;
166 keyPtr++) {
167 mNativeKeys.append(*keyPtr);
168 }
169
170 m_focus = true;
171 }
172 void keyboard_leave([[maybe_unused]] uint32_t serial, [[maybe_unused]] wl_surface *surface) override
173 {
174 mNativeKeys.clear();
175 m_focus = false;
176 }
177
178 uint32_t mNativeModifiers = 0;
180
181 uint32_t mKeymapFormat = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1;
182 QXkbCommon::ScopedXKBKeymap mXkbKeymap;
183 QXkbCommon::ScopedXKBState mXkbState;
184
185 bool m_focus = false;
186};
187
188
189/****************************************************************************/
190/* KisWaylandKeyboardWatcher::Seat */
191/****************************************************************************/
192
193
195 : public QWaylandClientExtensionTemplate<KisWaylandKeyboardWatcher::Seat>
196 , public QtWayland::wl_seat
197{
198 Q_OBJECT
199public:
200 Seat();
201 ~Seat() override;
202 void seat_capabilities(uint32_t capabilities) override;
203 bool hasKeyboardFocus() const;
204
206 Qt::KeyboardModifiers modifiers() const;
207
208private:
209 class Keyboard;
210
211 bool m_focus = false;
212 std::unique_ptr<KisWaylandKeyboardWatcher::Keyboard> m_keyboard;
213};
214
216 : QWaylandClientExtensionTemplate(5)
217{
218 initialize();
219}
221{
222 if (isActive()) {
223 release();
224 }
225}
226
228{
229 const bool hasKeyboard = capabilities & capability_keyboard;
230 if (hasKeyboard && !m_keyboard) {
231 m_keyboard = std::make_unique<KisWaylandKeyboardWatcher::Keyboard>(get_keyboard());
232 } else if (!hasKeyboard && m_keyboard) {
233 m_keyboard.reset();
234 }
235}
237{
238 return m_keyboard && m_keyboard->hasKeyboardFocus();
239}
240
242{
243 if (m_keyboard) {
244 return m_keyboard->pressedKeys();
245 }
246 return {};
247}
248
250{
251 if (!m_keyboard)
252 return Qt::NoModifier;
253
254 return m_keyboard->modifiers();
255}
256
257/****************************************************************************/
258/* KisWaylandKeyboardWatcher */
259/****************************************************************************/
260
261
266
270
272{
273 return m_seat->hasKeyboardFocus();
274}
275
277{
278 return m_seat->pressedKeys();
279}
280
281Qt::KeyboardModifiers KisWaylandKeyboardWatcher::modifiers() const
282{
283 return m_seat->modifiers();
284}
285
286#include "KisWaylandKeyboardWatcher.moc"
static struct xkb_context * getGlobalXkbContextFromQt()
void keyboard_modifiers(uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) override
void keyboard_enter(uint32_t serial, wl_surface *surface, wl_array *keys) override
void keyboard_leave(uint32_t serial, wl_surface *surface) override
void keyboard_keymap(uint32_t format, int32_t fd, uint32_t size) override
void keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state) override
Qt::KeyboardModifiers modifiers() const
std::unique_ptr< KisWaylandKeyboardWatcher::Keyboard > m_keyboard
void seat_capabilities(uint32_t capabilities) override
Qt::KeyboardModifiers modifiers() const