KWin
Loading...
Searching...
No Matches
xkb.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2013, 2016, 2017 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "xkb.h"
10#include "dbusproperties_interface.h"
11#include "inputmethod.h"
12#include "utils/c_ptr.h"
13#include "utils/common.h"
15#include "wayland/keyboard.h"
16#include "wayland/seat.h"
17// frameworks
18#include <KConfigGroup>
19// Qt
20#include <QKeyEvent>
21#include <QTemporaryFile>
22#include <QtGui/private/qxkbcommon_p.h>
23// xkbcommon
24#include <xkbcommon/xkbcommon-compose.h>
25#include <xkbcommon/xkbcommon-keysyms.h>
26// system
27#include "main.h"
28#include <bitset>
29#include <linux/input-event-codes.h>
30#include <sys/mman.h>
31#include <unistd.h>
32
33Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtWarningMsg)
34
35/* The offset between KEY_* numbering, and keycodes in the XKB evdev
36 * dataset. */
37static const int EVDEV_OFFSET = 8;
38static const char *s_locale1Interface = "org.freedesktop.locale1";
39
40namespace KWin
41{
42
43static void xkbLogHandler(xkb_context *context, xkb_log_level priority, const char *format, va_list args)
44{
45 char buf[1024];
46 int length = std::vsnprintf(buf, 1023, format, args);
47 while (length > 0 && std::isspace(buf[length - 1])) {
48 --length;
49 }
50 if (length <= 0) {
51 return;
52 }
53 switch (priority) {
54 case XKB_LOG_LEVEL_DEBUG:
55 qCDebug(KWIN_XKB, "XKB: %.*s", length, buf);
56 break;
57 case XKB_LOG_LEVEL_INFO:
58 qCInfo(KWIN_XKB, "XKB: %.*s", length, buf);
59 break;
60 case XKB_LOG_LEVEL_WARNING:
61 qCWarning(KWIN_XKB, "XKB: %.*s", length, buf);
62 break;
63 case XKB_LOG_LEVEL_ERROR:
64 case XKB_LOG_LEVEL_CRITICAL:
65 default:
66 qCCritical(KWIN_XKB, "XKB: %.*s", length, buf);
67 break;
68 }
69}
70
71#if HAVE_XKBCOMMON_NO_SECURE_GETENV
72constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS = XKB_CONTEXT_NO_SECURE_GETENV;
73#else
74constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS = XKB_CONTEXT_NO_FLAGS;
75#endif
76
77Xkb::Xkb(bool followLocale1)
78 : m_context(xkb_context_new(KWIN_XKB_CONTEXT_FLAGS))
79 , m_keymap(nullptr)
80 , m_state(nullptr)
81 , m_shiftModifier(0)
82 , m_capsModifier(0)
83 , m_controlModifier(0)
84 , m_altModifier(0)
85 , m_metaModifier(0)
86 , m_numModifier(0)
87 , m_numLock(0)
88 , m_capsLock(0)
89 , m_scrollLock(0)
90 , m_modifiers(Qt::NoModifier)
91 , m_consumedModifiers(Qt::NoModifier)
92 , m_keysym(XKB_KEY_NoSymbol)
93 , m_leds()
94 , m_followLocale1(followLocale1)
95{
96 qRegisterMetaType<KWin::LEDs>();
97 if (!m_context) {
98 qCDebug(KWIN_XKB) << "Could not create xkb context";
99 } else {
100 xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG);
101 xkb_context_set_log_fn(m_context, &xkbLogHandler);
102
103 // get locale as described in xkbcommon doc
104 // cannot use QLocale as it drops the modifier part
105 QByteArray locale = qgetenv("LC_ALL");
106 if (locale.isEmpty()) {
107 locale = qgetenv("LC_CTYPE");
108 }
109 if (locale.isEmpty()) {
110 locale = qgetenv("LANG");
111 }
112 if (locale.isEmpty()) {
113 locale = QByteArrayLiteral("C");
114 }
115
116 m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS);
117 if (m_compose.table) {
118 m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS);
119 }
120 }
121
122 if (m_followLocale1) {
123 bool connected = QDBusConnection::systemBus().connect(s_locale1Interface, "/org/freedesktop/locale1", QStringLiteral("org.freedesktop.DBus.Properties"),
124 QStringLiteral("PropertiesChanged"),
125 this,
126 SLOT(reconfigure()));
127 if (!connected) {
128 qCWarning(KWIN_XKB) << "Could not connect to org.freedesktop.locale1";
129 }
130 }
131}
132
134{
135 xkb_compose_state_unref(m_compose.state);
136 xkb_compose_table_unref(m_compose.table);
137 xkb_state_unref(m_state);
138 xkb_keymap_unref(m_keymap);
139 xkb_context_unref(m_context);
140}
141
142void Xkb::setConfig(const KSharedConfigPtr &config)
143{
144 m_configGroup = config->group(QStringLiteral("Layout"));
145}
146
147void Xkb::setNumLockConfig(const KSharedConfigPtr &config)
148{
149 m_numLockConfig = config;
150}
151
153{
154 if (!m_context) {
155 return;
156 }
157
158 xkb_keymap *keymap = nullptr;
159 if (!qEnvironmentVariableIsSet("KWIN_XKB_DEFAULT_KEYMAP")) {
160 if (m_followLocale1) {
161 keymap = loadKeymapFromLocale1();
162 } else {
163 keymap = loadKeymapFromConfig();
164 }
165 }
166 if (!keymap) {
167 qCDebug(KWIN_XKB) << "Could not create xkb keymap from configuration";
168 keymap = loadDefaultKeymap();
169 }
170 if (keymap) {
171 updateKeymap(keymap);
172 } else {
173 qCDebug(KWIN_XKB) << "Could not create default xkb keymap";
174 }
175}
176
177static bool stringIsEmptyOrNull(const char *str)
178{
179 return str == nullptr || str[0] == '\0';
180}
181
187void Xkb::applyEnvironmentRules(xkb_rule_names &ruleNames)
188{
189 if (stringIsEmptyOrNull(ruleNames.rules)) {
190 ruleNames.rules = getenv("XKB_DEFAULT_RULES");
191 }
192
193 if (stringIsEmptyOrNull(ruleNames.model)) {
194 ruleNames.model = getenv("XKB_DEFAULT_MODEL");
195 }
196
197 if (stringIsEmptyOrNull(ruleNames.layout)) {
198 ruleNames.layout = getenv("XKB_DEFAULT_LAYOUT");
199 ruleNames.variant = getenv("XKB_DEFAULT_VARIANT");
200 }
201
202 if (ruleNames.options == nullptr) {
203 ruleNames.options = getenv("XKB_DEFAULT_OPTIONS");
204 }
205}
206
207xkb_keymap *Xkb::loadKeymapFromConfig()
208{
209 // load config
210 if (!m_configGroup.isValid()) {
211 return nullptr;
212 }
213 const QByteArray model = m_configGroup.readEntry("Model", "pc104").toLatin1();
214 const QByteArray layout = m_configGroup.readEntry("LayoutList").toLatin1();
215 const QByteArray variant = m_configGroup.readEntry("VariantList").toLatin1();
216 const QByteArray options = m_configGroup.readEntry("Options").toLatin1();
217
218 xkb_rule_names ruleNames = {
219 .rules = nullptr,
220 .model = model.constData(),
221 .layout = layout.constData(),
222 .variant = variant.constData(),
223 .options = nullptr,
224 };
225
226 if (m_configGroup.readEntry("ResetOldOptions", false)) {
227 ruleNames.options = options.constData();
228 }
229
230 applyEnvironmentRules(ruleNames);
231
232 m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
233
234 return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
235}
236
237xkb_keymap *Xkb::loadDefaultKeymap()
238{
239 xkb_rule_names ruleNames = {};
240 applyEnvironmentRules(ruleNames);
241 m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
242 return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
243}
244
245xkb_keymap *Xkb::loadKeymapFromLocale1()
246{
247 OrgFreedesktopDBusPropertiesInterface locale1Properties(s_locale1Interface, "/org/freedesktop/locale1", QDBusConnection::systemBus(), this);
248 const QVariantMap properties = locale1Properties.GetAll(s_locale1Interface);
249
250 const QByteArray model = properties["X11Model"].toByteArray();
251 const QByteArray layout = properties["X11Layout"].toByteArray();
252 const QByteArray variant = properties["X11Variant"].toByteArray();
253 const QByteArray options = properties["X11Options"].toByteArray();
254
255 xkb_rule_names ruleNames = {
256 .rules = nullptr,
257 .model = model.constData(),
258 .layout = layout.constData(),
259 .variant = variant.constData(),
260 .options = options.constData(),
261 };
262
263 applyEnvironmentRules(ruleNames);
264
265 m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
266
267 return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
268}
269
270void Xkb::updateKeymap(xkb_keymap *keymap)
271{
272 Q_ASSERT(keymap);
273 xkb_state *state = xkb_state_new(keymap);
274 if (!state) {
275 qCDebug(KWIN_XKB) << "Could not create XKB state";
276 xkb_keymap_unref(keymap);
277 return;
278 }
279
280 // save Locks
281 bool numLockIsOn, capsLockIsOn;
282 static bool s_startup = true;
283 if (!s_startup) {
284 numLockIsOn = xkb_state_mod_index_is_active(m_state, m_numModifier, XKB_STATE_MODS_LOCKED);
285 capsLockIsOn = xkb_state_mod_index_is_active(m_state, m_capsModifier, XKB_STATE_MODS_LOCKED);
286 }
287
288 // now release the old ones
289 xkb_state_unref(m_state);
290 xkb_keymap_unref(m_keymap);
291
292 m_keymap = keymap;
293 m_state = state;
294
295 m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
296 m_capsModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CAPS);
297 m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
298 m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
299 m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
300 m_numModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_NUM);
301
302 m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
303 m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
304 m_scrollLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL);
305
306 m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
307
308 m_modifierState.depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
309 m_modifierState.latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
310 m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
311
312 auto setLock = [this](xkb_mod_index_t modifier, bool value) {
313 if (modifier != XKB_MOD_INVALID) {
314 std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};
315 if (mask.size() > modifier) {
316 mask[modifier] = value;
317 m_modifierState.locked = mask.to_ulong();
318 xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.latched, m_modifierState.locked, 0, 0, m_currentLayout);
319 m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
320 }
321 }
322 };
323
324 if (s_startup || qEnvironmentVariableIsSet("KWIN_FORCE_NUM_LOCK_EVALUATION")) {
325 s_startup = false;
326 if (m_numLockConfig) {
327 const KConfigGroup config = m_numLockConfig->group(QStringLiteral("Keyboard"));
328 // STATE_ON = 0, STATE_OFF = 1, STATE_UNCHANGED = 2, see plasma-desktop/kcms/keyboard/kcmmisc.h
329 const auto setting = config.readEntry("NumLock", 2);
330 if (setting != 2) {
331 setLock(m_numModifier, !setting);
332 }
333 }
334 } else {
335 setLock(m_numModifier, numLockIsOn);
336 setLock(m_capsModifier, capsLockIsOn);
337 }
338
339 createKeymapFile();
341 if (auto *inputmethod = kwinApp()->inputMethod()) {
342 inputmethod->forwardModifiers(InputMethod::Force);
343 }
345}
346
347void Xkb::createKeymapFile()
348{
349 const auto currentKeymap = keymapContents();
350 if (currentKeymap.isEmpty()) {
351 return;
352 }
353 m_seat->keyboard()->setKeymap(currentKeymap);
354 auto *inputmethod = kwinApp()->inputMethod();
355 if (!inputmethod) {
356 return;
357 }
358 if (auto *keyboardGrab = inputmethod->keyboardGrab()) {
359 keyboardGrab->sendKeymap(currentKeymap);
360 }
361}
362
363QByteArray Xkb::keymapContents() const
364{
365 if (!m_seat || !m_seat->keyboard()) {
366 return {};
367 }
368 // TODO: uninstall keymap on server?
369 if (!m_keymap) {
370 return {};
371 }
372
373 UniqueCPtr<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
374 if (!keymapString) {
375 return {};
376 }
377 return keymapString.get();
378}
379
380void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
381{
382 if (!m_keymap || !m_state) {
383 return;
384 }
385 // Avoid to create a infinite loop between input method and compositor.
386 if (xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group) == 0) {
387 return;
388 }
391}
392
394{
395 if (!m_keymap || !m_state) {
396 return;
397 }
398 xkb_state_update_key(m_state, key + EVDEV_OFFSET, static_cast<xkb_key_direction>(state));
400 const auto sym = toKeysym(key);
401 if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
402 switch (xkb_compose_state_get_status(m_compose.state)) {
403 case XKB_COMPOSE_NOTHING:
404 m_keysym = sym;
405 break;
406 case XKB_COMPOSE_COMPOSED:
407 m_keysym = xkb_compose_state_get_one_sym(m_compose.state);
408 break;
409 default:
410 m_keysym = XKB_KEY_NoSymbol;
411 break;
412 }
413 } else {
414 m_keysym = sym;
415 }
416 }
418 updateConsumedModifiers(key);
419}
420
421void Xkb::updateModifiers()
422{
423 Qt::KeyboardModifiers mods = Qt::NoModifier;
424 if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
425 mods |= Qt::ShiftModifier;
426 }
427 if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
428 mods |= Qt::AltModifier;
429 }
430 if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
431 mods |= Qt::ControlModifier;
432 }
433 if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
434 mods |= Qt::MetaModifier;
435 }
436 if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
437 mods |= Qt::KeypadModifier;
438 }
439 m_modifiers = mods;
440
441 // update LEDs
442 LEDs leds;
443 if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) {
445 }
446 if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) {
448 }
449 if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) {
451 }
452 if (m_leds != leds) {
453 m_leds = leds;
454 Q_EMIT ledsChanged(m_leds);
455 }
456
457 const uint32_t newLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
458 const uint32_t depressed = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_DEPRESSED);
459 const uint32_t latched = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_LATCHED);
460 const uint32_t locked = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_LOCKED);
461
462 if (newLayout != m_currentLayout || depressed != m_modifierState.depressed || latched != m_modifierState.latched || locked != m_modifierState.locked) {
463 m_currentLayout = newLayout;
464 m_modifierState.depressed = depressed;
465 m_modifierState.latched = latched;
466 m_modifierState.locked = locked;
467
468 Q_EMIT modifierStateChanged();
469 }
470}
471
473{
474 if (!m_seat || !m_seat->keyboard()) {
475 return;
476 }
477 m_seat->notifyKeyboardModifiers(m_modifierState.depressed,
478 m_modifierState.latched,
479 m_modifierState.locked,
480 m_currentLayout);
481}
482
484{
485 if (!m_keymap) {
486 return QString{};
487 }
488 return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, index));
489}
490
491QString Xkb::layoutName() const
492{
493 return layoutName(m_currentLayout);
494}
495
496QString Xkb::layoutShortName(int index) const
497{
498 return m_layoutList.value(index);
499}
500
501void Xkb::updateConsumedModifiers(uint32_t key)
502{
503 Qt::KeyboardModifiers mods = Qt::NoModifier;
504 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_shiftModifier, XKB_CONSUMED_MODE_GTK) == 1) {
505 mods |= Qt::ShiftModifier;
506 }
507 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) {
508 mods |= Qt::AltModifier;
509 }
510 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) {
511 mods |= Qt::ControlModifier;
512 }
513 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) {
514 mods |= Qt::MetaModifier;
515 }
516 m_consumedModifiers = mods;
517}
518
519Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts(uint32_t scanCode) const
520{
521 if (!m_state) {
522 return Qt::NoModifier;
523 }
524 Qt::KeyboardModifiers mods = Qt::NoModifier;
525 if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
526 mods |= Qt::ShiftModifier;
527 }
528 if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
529 mods |= Qt::AltModifier;
530 }
531 if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
532 mods |= Qt::ControlModifier;
533 }
534 if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
535 mods |= Qt::MetaModifier;
536 }
537 if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
538 mods |= Qt::KeypadModifier;
539 }
540
541 Qt::KeyboardModifiers consumedMods = m_consumedModifiers;
542 if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) {
543 // test whether current keysym is a letter
544 // in that case the shift should be removed from the consumed modifiers again
545 // otherwise it would not be possible to trigger e.g. Shift+W as a shortcut
546 // see BUG: 370341
547 if (QChar::isLetter(toQtKey(m_keysym, scanCode, Qt::ControlModifier))) {
548 consumedMods = Qt::KeyboardModifiers();
549 }
550 }
551
552 return mods & ~consumedMods;
553}
554
556{
557 if (!m_state) {
558 return XKB_KEY_NoSymbol;
559 }
560
561 // Workaround because there's some kind of overlap between KEY_ZENKAKUHANKAKU and TLDE
562 // This key is important because some hardware manufacturers use it to indicate touchpad toggling.
563 xkb_keysym_t ret = xkb_state_key_get_one_sym(m_state, key + EVDEV_OFFSET);
564 if (ret == 0 && key == KEY_ZENKAKUHANKAKU) {
565 ret = XKB_KEY_Zenkaku_Hankaku;
566 }
567 return ret;
568}
569
571{
572 if (!m_state || keysym == XKB_KEY_NoSymbol) {
573 return QString();
574 }
575 QByteArray byteArray(7, 0);
576 int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
577 if (ok == -1 || ok == 0) {
578 return QString();
579 }
580 return QString::fromUtf8(byteArray.constData());
581}
582
584 uint32_t scanCode,
585 Qt::KeyboardModifiers modifiers,
586 bool superAsMeta) const
587{
588 // FIXME: passing superAsMeta doesn't have impact due to bug in the Qt function, so handle it below
589 Qt::Key qtKey = Qt::Key(QXkbCommon::keysymToQtKey(keySym, modifiers, m_state, scanCode + EVDEV_OFFSET, superAsMeta));
590
591 // FIXME: workarounds for symbols currently wrong/not mappable via keysymToQtKey()
592 if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) {
593 // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
594 qtKey = Qt::Key_Meta;
595 } else if (qtKey > 0xff && keySym <= 0xff) {
596 // XKB_KEY_mu, XKB_KEY_ydiaeresis go here
597 qtKey = Qt::Key(keySym);
598 }
599 return qtKey;
600}
601
602bool Xkb::shouldKeyRepeat(quint32 key) const
603{
604 if (!m_keymap) {
605 return false;
606 }
607 return xkb_keymap_key_repeats(m_keymap, key + EVDEV_OFFSET) != 0;
608}
609
611{
612 if (!m_keymap || !m_state) {
613 return;
614 }
615 const xkb_layout_index_t numLayouts = xkb_keymap_num_layouts(m_keymap);
616 const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts;
617 switchToLayout(nextLayout);
618}
619
621{
622 if (!m_keymap || !m_state) {
623 return;
624 }
625 const xkb_layout_index_t previousLayout = m_currentLayout == 0 ? numberOfLayouts() - 1 : m_currentLayout - 1;
626 switchToLayout(previousLayout);
627}
628
630{
631 if (!m_keymap || !m_state || layout >= numberOfLayouts()) {
632 return false;
633 }
634 const xkb_mod_mask_t depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
635 const xkb_mod_mask_t latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
636 const xkb_mod_mask_t locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
637 xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, layout);
640 return true;
641}
642
643void Xkb::setModifierLatched(Qt::KeyboardModifier mod, bool latched)
644{
645 xkb_mod_index_t modifier = XKB_MOD_INVALID;
646
647 switch (mod) {
648 case Qt::NoModifier: {
649 break;
650 }
651 case Qt::ShiftModifier: {
652 modifier = m_shiftModifier;
653 break;
654 }
655 case Qt::AltModifier: {
656 modifier = m_altModifier;
657 break;
658 }
659 case Qt::ControlModifier: {
660 modifier = m_controlModifier;
661 break;
662 }
663 case Qt::MetaModifier: {
664 modifier = m_metaModifier;
665 break;
666 }
667 case Qt::GroupSwitchModifier: {
668 // TODO
669 break;
670 }
671 case Qt::KeypadModifier: {
672 break;
673 }
674 case Qt::KeyboardModifierMask: {
675 break;
676 }
677 }
678
679 if (modifier != XKB_MOD_INVALID) {
680 std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.latched};
681 if (mask.size() > modifier) {
682 mask[modifier] = latched;
683 m_modifierState.latched = mask.to_ulong();
684 xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.latched, m_modifierState.locked, 0, 0, m_currentLayout);
685 m_modifierState.latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
686 }
687 }
688}
689
690void Xkb::setModifierLocked(Qt::KeyboardModifier mod, bool locked)
691{
692 xkb_mod_index_t modifier = XKB_MOD_INVALID;
693
694 switch (mod) {
695 case Qt::NoModifier: {
696 break;
697 }
698 case Qt::ShiftModifier: {
699 modifier = m_shiftModifier;
700 break;
701 }
702 case Qt::AltModifier: {
703 modifier = m_altModifier;
704 break;
705 }
706 case Qt::ControlModifier: {
707 modifier = m_controlModifier;
708 break;
709 }
710 case Qt::MetaModifier: {
711 modifier = m_metaModifier;
712 break;
713 }
714 case Qt::GroupSwitchModifier: {
715 // TODO
716 break;
717 }
718 case Qt::KeypadModifier: {
719 break;
720 }
721 case Qt::KeyboardModifierMask: {
722 break;
723 }
724 }
725
726 if (modifier != XKB_MOD_INVALID) {
727 std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};
728 if (mask.size() > modifier) {
729 mask[modifier] = locked;
730 m_modifierState.locked = mask.to_ulong();
731 xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.locked, m_modifierState.locked, 0, 0, m_currentLayout);
732 m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
733 }
734 }
735}
736
737quint32 Xkb::numberOfLayouts() const
738{
739 if (!m_keymap) {
740 return 0;
741 }
742 return xkb_keymap_num_layouts(m_keymap);
743}
744
746{
747 m_seat = QPointer<SeatInterface>(seat);
748}
749
750std::optional<int> Xkb::keycodeFromKeysym(xkb_keysym_t keysym)
751{
752 auto layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
753 const xkb_keycode_t max = xkb_keymap_max_keycode(m_keymap);
754 for (xkb_keycode_t keycode = xkb_keymap_min_keycode(m_keymap); keycode < max; keycode++) {
755 uint levelCount = xkb_keymap_num_levels_for_key(m_keymap, keycode, layout);
756 for (uint currentLevel = 0; currentLevel < levelCount; currentLevel++) {
757 const xkb_keysym_t *syms;
758 uint num_syms = xkb_keymap_key_get_syms_by_level(m_keymap, keycode, layout, currentLevel, &syms);
759 for (uint sym = 0; sym < num_syms; sym++) {
760 if (syms[sym] == keysym) {
761 return {keycode - EVDEV_OFFSET};
762 }
763 }
764 }
765 }
766 return {};
767}
768}
769
770#include "moc_xkb.cpp"
Represents a Seat on the Wayland Display.
Definition seat.h:134
Qt::KeyboardModifiers modifiers() const
Definition xkb.h:165
void setNumLockConfig(const KSharedConfigPtr &config)
Definition xkb.cpp:147
void switchToNextLayout()
Definition xkb.cpp:610
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts(uint32_t scanCode=0) const
Definition xkb.cpp:519
xkb_mod_index_t locked
Definition xkb.h:158
QString toString(xkb_keysym_t keysym)
Definition xkb.cpp:570
QString layoutShortName(int index) const
Definition xkb.cpp:496
void setConfig(const KSharedConfigPtr &config)
Definition xkb.cpp:142
void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
Definition xkb.cpp:393
xkb_mod_index_t depressed
Definition xkb.h:156
void ledsChanged(const LEDs &leds)
void setSeat(SeatInterface *seat)
Definition xkb.cpp:745
xkb_keysym_t toKeysym(uint32_t key)
Definition xkb.cpp:555
~Xkb() override
Definition xkb.cpp:133
xkb_compose_state * state
Definition xkb.h:148
LEDs leds() const
Definition xkb.h:70
void setModifierLocked(Qt::KeyboardModifier mod, bool locked)
Definition xkb.cpp:690
void modifierStateChanged()
quint32 numberOfLayouts() const
Definition xkb.cpp:737
Qt::Key toQtKey(xkb_keysym_t keysym, uint32_t scanCode=0, Qt::KeyboardModifiers modifiers=Qt::KeyboardModifiers(), bool superAsMeta=false) const
Definition xkb.cpp:583
Xkb(bool followLocale1=false)
Definition xkb.cpp:77
void forwardModifiers()
Definition xkb.cpp:472
QString layoutName() const
Definition xkb.cpp:491
void setModifierLatched(Qt::KeyboardModifier mod, bool latched)
Definition xkb.cpp:643
std::optional< int > keycodeFromKeysym(xkb_keysym_t keysym)
Definition xkb.cpp:750
xkb_keymap * keymap() const
Definition xkb.h:75
QByteArray keymapContents() const
Definition xkb.cpp:363
void switchToPreviousLayout()
Definition xkb.cpp:620
void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
Definition xkb.cpp:380
bool shouldKeyRepeat(quint32 key) const
Definition xkb.cpp:602
bool switchToLayout(xkb_layout_index_t layout)
Definition xkb.cpp:629
xkb_mod_index_t latched
Definition xkb.h:157
void reconfigure()
Definition xkb.cpp:152
uint32_t xkb_mod_index_t
uint32_t xkb_keysym_t
uint32_t xkb_layout_index_t
MockInputMethod * inputMethod()
GLenum format
Definition gltexture.cpp:49
constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS
Definition xkb.cpp:74
Options * options
Definition main.cpp:73
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28