File indexing completed on 2024-04-28 05:30:43

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2013, 2016, 2017 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "xkb.h"
0010 #include "dbusproperties_interface.h"
0011 #include "inputmethod.h"
0012 #include "utils/c_ptr.h"
0013 #include "utils/common.h"
0014 #include "wayland/inputmethod_v1.h"
0015 #include "wayland/keyboard.h"
0016 #include "wayland/seat.h"
0017 // frameworks
0018 #include <KConfigGroup>
0019 // Qt
0020 #include <QKeyEvent>
0021 #include <QTemporaryFile>
0022 #include <QtGui/private/qxkbcommon_p.h>
0023 // xkbcommon
0024 #include <xkbcommon/xkbcommon-compose.h>
0025 #include <xkbcommon/xkbcommon-keysyms.h>
0026 // system
0027 #include "main.h"
0028 #include <bitset>
0029 #include <linux/input-event-codes.h>
0030 #include <sys/mman.h>
0031 #include <unistd.h>
0032 
0033 Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtWarningMsg)
0034 
0035 /* The offset between KEY_* numbering, and keycodes in the XKB evdev
0036  * dataset. */
0037 static const int EVDEV_OFFSET = 8;
0038 static const char *s_locale1Interface = "org.freedesktop.locale1";
0039 
0040 namespace KWin
0041 {
0042 
0043 static void xkbLogHandler(xkb_context *context, xkb_log_level priority, const char *format, va_list args)
0044 {
0045     char buf[1024];
0046     int length = std::vsnprintf(buf, 1023, format, args);
0047     while (length > 0 && std::isspace(buf[length - 1])) {
0048         --length;
0049     }
0050     if (length <= 0) {
0051         return;
0052     }
0053     switch (priority) {
0054     case XKB_LOG_LEVEL_DEBUG:
0055         qCDebug(KWIN_XKB, "XKB: %.*s", length, buf);
0056         break;
0057     case XKB_LOG_LEVEL_INFO:
0058         qCInfo(KWIN_XKB, "XKB: %.*s", length, buf);
0059         break;
0060     case XKB_LOG_LEVEL_WARNING:
0061         qCWarning(KWIN_XKB, "XKB: %.*s", length, buf);
0062         break;
0063     case XKB_LOG_LEVEL_ERROR:
0064     case XKB_LOG_LEVEL_CRITICAL:
0065     default:
0066         qCCritical(KWIN_XKB, "XKB: %.*s", length, buf);
0067         break;
0068     }
0069 }
0070 
0071 #if HAVE_XKBCOMMON_NO_SECURE_GETENV
0072 constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS = XKB_CONTEXT_NO_SECURE_GETENV;
0073 #else
0074 constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS = XKB_CONTEXT_NO_FLAGS;
0075 #endif
0076 
0077 Xkb::Xkb(bool followLocale1)
0078     : m_context(xkb_context_new(KWIN_XKB_CONTEXT_FLAGS))
0079     , m_keymap(nullptr)
0080     , m_state(nullptr)
0081     , m_shiftModifier(0)
0082     , m_capsModifier(0)
0083     , m_controlModifier(0)
0084     , m_altModifier(0)
0085     , m_metaModifier(0)
0086     , m_numModifier(0)
0087     , m_numLock(0)
0088     , m_capsLock(0)
0089     , m_scrollLock(0)
0090     , m_modifiers(Qt::NoModifier)
0091     , m_consumedModifiers(Qt::NoModifier)
0092     , m_keysym(XKB_KEY_NoSymbol)
0093     , m_leds()
0094     , m_followLocale1(followLocale1)
0095 {
0096     qRegisterMetaType<KWin::LEDs>();
0097     if (!m_context) {
0098         qCDebug(KWIN_XKB) << "Could not create xkb context";
0099     } else {
0100         xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG);
0101         xkb_context_set_log_fn(m_context, &xkbLogHandler);
0102 
0103         // get locale as described in xkbcommon doc
0104         // cannot use QLocale as it drops the modifier part
0105         QByteArray locale = qgetenv("LC_ALL");
0106         if (locale.isEmpty()) {
0107             locale = qgetenv("LC_CTYPE");
0108         }
0109         if (locale.isEmpty()) {
0110             locale = qgetenv("LANG");
0111         }
0112         if (locale.isEmpty()) {
0113             locale = QByteArrayLiteral("C");
0114         }
0115 
0116         m_compose.table = xkb_compose_table_new_from_locale(m_context, locale.constData(), XKB_COMPOSE_COMPILE_NO_FLAGS);
0117         if (m_compose.table) {
0118             m_compose.state = xkb_compose_state_new(m_compose.table, XKB_COMPOSE_STATE_NO_FLAGS);
0119         }
0120     }
0121 
0122     if (m_followLocale1) {
0123         bool connected = QDBusConnection::systemBus().connect(s_locale1Interface, "/org/freedesktop/locale1", QStringLiteral("org.freedesktop.DBus.Properties"),
0124                                                               QStringLiteral("PropertiesChanged"),
0125                                                               this,
0126                                                               SLOT(reconfigure()));
0127         if (!connected) {
0128             qCWarning(KWIN_XKB) << "Could not connect to org.freedesktop.locale1";
0129         }
0130     }
0131 }
0132 
0133 Xkb::~Xkb()
0134 {
0135     xkb_compose_state_unref(m_compose.state);
0136     xkb_compose_table_unref(m_compose.table);
0137     xkb_state_unref(m_state);
0138     xkb_keymap_unref(m_keymap);
0139     xkb_context_unref(m_context);
0140 }
0141 
0142 void Xkb::setConfig(const KSharedConfigPtr &config)
0143 {
0144     m_configGroup = config->group(QStringLiteral("Layout"));
0145 }
0146 
0147 void Xkb::setNumLockConfig(const KSharedConfigPtr &config)
0148 {
0149     m_numLockConfig = config;
0150 }
0151 
0152 void Xkb::reconfigure()
0153 {
0154     if (!m_context) {
0155         return;
0156     }
0157 
0158     xkb_keymap *keymap = nullptr;
0159     if (!qEnvironmentVariableIsSet("KWIN_XKB_DEFAULT_KEYMAP")) {
0160         if (m_followLocale1) {
0161             keymap = loadKeymapFromLocale1();
0162         } else {
0163             keymap = loadKeymapFromConfig();
0164         }
0165     }
0166     if (!keymap) {
0167         qCDebug(KWIN_XKB) << "Could not create xkb keymap from configuration";
0168         keymap = loadDefaultKeymap();
0169     }
0170     if (keymap) {
0171         updateKeymap(keymap);
0172     } else {
0173         qCDebug(KWIN_XKB) << "Could not create default xkb keymap";
0174     }
0175 }
0176 
0177 static bool stringIsEmptyOrNull(const char *str)
0178 {
0179     return str == nullptr || str[0] == '\0';
0180 }
0181 
0182 /**
0183  * libxkbcommon uses secure_getenv to read the XKB_DEFAULT_* variables.
0184  * As kwin_wayland may have the CAP_SET_NICE capability, it returns nullptr
0185  * so we need to do it ourselves (see xkb_context_sanitize_rule_names).
0186  **/
0187 void Xkb::applyEnvironmentRules(xkb_rule_names &ruleNames)
0188 {
0189     if (stringIsEmptyOrNull(ruleNames.rules)) {
0190         ruleNames.rules = getenv("XKB_DEFAULT_RULES");
0191     }
0192 
0193     if (stringIsEmptyOrNull(ruleNames.model)) {
0194         ruleNames.model = getenv("XKB_DEFAULT_MODEL");
0195     }
0196 
0197     if (stringIsEmptyOrNull(ruleNames.layout)) {
0198         ruleNames.layout = getenv("XKB_DEFAULT_LAYOUT");
0199         ruleNames.variant = getenv("XKB_DEFAULT_VARIANT");
0200     }
0201 
0202     if (ruleNames.options == nullptr) {
0203         ruleNames.options = getenv("XKB_DEFAULT_OPTIONS");
0204     }
0205 }
0206 
0207 xkb_keymap *Xkb::loadKeymapFromConfig()
0208 {
0209     // load config
0210     if (!m_configGroup.isValid()) {
0211         return nullptr;
0212     }
0213     const QByteArray model = m_configGroup.readEntry("Model", "pc104").toLatin1();
0214     const QByteArray layout = m_configGroup.readEntry("LayoutList").toLatin1();
0215     const QByteArray variant = m_configGroup.readEntry("VariantList").toLatin1();
0216     const QByteArray options = m_configGroup.readEntry("Options").toLatin1();
0217 
0218     xkb_rule_names ruleNames = {
0219         .rules = nullptr,
0220         .model = model.constData(),
0221         .layout = layout.constData(),
0222         .variant = variant.constData(),
0223         .options = nullptr,
0224     };
0225 
0226     if (m_configGroup.readEntry("ResetOldOptions", false)) {
0227         ruleNames.options = options.constData();
0228     }
0229 
0230     applyEnvironmentRules(ruleNames);
0231 
0232     m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
0233 
0234     return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
0235 }
0236 
0237 xkb_keymap *Xkb::loadDefaultKeymap()
0238 {
0239     xkb_rule_names ruleNames = {};
0240     applyEnvironmentRules(ruleNames);
0241     m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
0242     return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
0243 }
0244 
0245 xkb_keymap *Xkb::loadKeymapFromLocale1()
0246 {
0247     OrgFreedesktopDBusPropertiesInterface locale1Properties(s_locale1Interface, "/org/freedesktop/locale1", QDBusConnection::systemBus(), this);
0248     const QVariantMap properties = locale1Properties.GetAll(s_locale1Interface);
0249 
0250     const QByteArray model = properties["X11Model"].toByteArray();
0251     const QByteArray layout = properties["X11Layout"].toByteArray();
0252     const QByteArray variant = properties["X11Variant"].toByteArray();
0253     const QByteArray options = properties["X11Options"].toByteArray();
0254 
0255     xkb_rule_names ruleNames = {
0256         .rules = nullptr,
0257         .model = model.constData(),
0258         .layout = layout.constData(),
0259         .variant = variant.constData(),
0260         .options = options.constData(),
0261     };
0262 
0263     applyEnvironmentRules(ruleNames);
0264 
0265     m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(','));
0266 
0267     return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
0268 }
0269 
0270 void Xkb::updateKeymap(xkb_keymap *keymap)
0271 {
0272     Q_ASSERT(keymap);
0273     xkb_state *state = xkb_state_new(keymap);
0274     if (!state) {
0275         qCDebug(KWIN_XKB) << "Could not create XKB state";
0276         xkb_keymap_unref(keymap);
0277         return;
0278     }
0279 
0280     // save Locks
0281     bool numLockIsOn, capsLockIsOn;
0282     static bool s_startup = true;
0283     if (!s_startup) {
0284         numLockIsOn = xkb_state_mod_index_is_active(m_state, m_numModifier, XKB_STATE_MODS_LOCKED);
0285         capsLockIsOn = xkb_state_mod_index_is_active(m_state, m_capsModifier, XKB_STATE_MODS_LOCKED);
0286     }
0287 
0288     // now release the old ones
0289     xkb_state_unref(m_state);
0290     xkb_keymap_unref(m_keymap);
0291 
0292     m_keymap = keymap;
0293     m_state = state;
0294 
0295     m_shiftModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_SHIFT);
0296     m_capsModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CAPS);
0297     m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL);
0298     m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT);
0299     m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO);
0300     m_numModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_NUM);
0301 
0302     m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM);
0303     m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS);
0304     m_scrollLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_SCROLL);
0305 
0306     m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
0307 
0308     m_modifierState.depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
0309     m_modifierState.latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
0310     m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
0311 
0312     auto setLock = [this](xkb_mod_index_t modifier, bool value) {
0313         if (modifier != XKB_MOD_INVALID) {
0314             std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};
0315             if (mask.size() > modifier) {
0316                 mask[modifier] = value;
0317                 m_modifierState.locked = mask.to_ulong();
0318                 xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.latched, m_modifierState.locked, 0, 0, m_currentLayout);
0319                 m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
0320             }
0321         }
0322     };
0323 
0324     if (s_startup || qEnvironmentVariableIsSet("KWIN_FORCE_NUM_LOCK_EVALUATION")) {
0325         s_startup = false;
0326         if (m_numLockConfig) {
0327             const KConfigGroup config = m_numLockConfig->group(QStringLiteral("Keyboard"));
0328             // STATE_ON = 0,  STATE_OFF = 1, STATE_UNCHANGED = 2, see plasma-desktop/kcms/keyboard/kcmmisc.h
0329             const auto setting = config.readEntry("NumLock", 2);
0330             if (setting != 2) {
0331                 setLock(m_numModifier, !setting);
0332             }
0333         }
0334     } else {
0335         setLock(m_numModifier, numLockIsOn);
0336         setLock(m_capsModifier, capsLockIsOn);
0337     }
0338 
0339     createKeymapFile();
0340     forwardModifiers();
0341     if (auto *inputmethod = kwinApp()->inputMethod()) {
0342         inputmethod->forwardModifiers(InputMethod::Force);
0343     }
0344     updateModifiers();
0345 }
0346 
0347 void Xkb::createKeymapFile()
0348 {
0349     const auto currentKeymap = keymapContents();
0350     if (currentKeymap.isEmpty()) {
0351         return;
0352     }
0353     m_seat->keyboard()->setKeymap(currentKeymap);
0354     auto *inputmethod = kwinApp()->inputMethod();
0355     if (!inputmethod) {
0356         return;
0357     }
0358     if (auto *keyboardGrab = inputmethod->keyboardGrab()) {
0359         keyboardGrab->sendKeymap(currentKeymap);
0360     }
0361 }
0362 
0363 QByteArray Xkb::keymapContents() const
0364 {
0365     if (!m_seat || !m_seat->keyboard()) {
0366         return {};
0367     }
0368     // TODO: uninstall keymap on server?
0369     if (!m_keymap) {
0370         return {};
0371     }
0372 
0373     UniqueCPtr<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
0374     if (!keymapString) {
0375         return {};
0376     }
0377     return keymapString.get();
0378 }
0379 
0380 void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
0381 {
0382     if (!m_keymap || !m_state) {
0383         return;
0384     }
0385     // Avoid to create a infinite loop between input method and compositor.
0386     if (xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group) == 0) {
0387         return;
0388     }
0389     updateModifiers();
0390     forwardModifiers();
0391 }
0392 
0393 void Xkb::updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
0394 {
0395     if (!m_keymap || !m_state) {
0396         return;
0397     }
0398     xkb_state_update_key(m_state, key + EVDEV_OFFSET, static_cast<xkb_key_direction>(state));
0399     if (state == InputRedirection::KeyboardKeyPressed) {
0400         const auto sym = toKeysym(key);
0401         if (m_compose.state && xkb_compose_state_feed(m_compose.state, sym) == XKB_COMPOSE_FEED_ACCEPTED) {
0402             switch (xkb_compose_state_get_status(m_compose.state)) {
0403             case XKB_COMPOSE_NOTHING:
0404                 m_keysym = sym;
0405                 break;
0406             case XKB_COMPOSE_COMPOSED:
0407                 m_keysym = xkb_compose_state_get_one_sym(m_compose.state);
0408                 break;
0409             default:
0410                 m_keysym = XKB_KEY_NoSymbol;
0411                 break;
0412             }
0413         } else {
0414             m_keysym = sym;
0415         }
0416     }
0417     updateModifiers();
0418     updateConsumedModifiers(key);
0419 }
0420 
0421 void Xkb::updateModifiers()
0422 {
0423     Qt::KeyboardModifiers mods = Qt::NoModifier;
0424     if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1 || xkb_state_mod_index_is_active(m_state, m_capsModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0425         mods |= Qt::ShiftModifier;
0426     }
0427     if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0428         mods |= Qt::AltModifier;
0429     }
0430     if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0431         mods |= Qt::ControlModifier;
0432     }
0433     if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0434         mods |= Qt::MetaModifier;
0435     }
0436     if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
0437         mods |= Qt::KeypadModifier;
0438     }
0439     m_modifiers = mods;
0440 
0441     // update LEDs
0442     LEDs leds;
0443     if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) {
0444         leds = leds | LED::NumLock;
0445     }
0446     if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) {
0447         leds = leds | LED::CapsLock;
0448     }
0449     if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) {
0450         leds = leds | LED::ScrollLock;
0451     }
0452     if (m_leds != leds) {
0453         m_leds = leds;
0454         Q_EMIT ledsChanged(m_leds);
0455     }
0456 
0457     const uint32_t newLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
0458     const uint32_t depressed = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_DEPRESSED);
0459     const uint32_t latched = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_LATCHED);
0460     const uint32_t locked = xkb_state_serialize_mods(m_state, XKB_STATE_MODS_LOCKED);
0461 
0462     if (newLayout != m_currentLayout || depressed != m_modifierState.depressed || latched != m_modifierState.latched || locked != m_modifierState.locked) {
0463         m_currentLayout = newLayout;
0464         m_modifierState.depressed = depressed;
0465         m_modifierState.latched = latched;
0466         m_modifierState.locked = locked;
0467 
0468         Q_EMIT modifierStateChanged();
0469     }
0470 }
0471 
0472 void Xkb::forwardModifiers()
0473 {
0474     if (!m_seat || !m_seat->keyboard()) {
0475         return;
0476     }
0477     m_seat->notifyKeyboardModifiers(m_modifierState.depressed,
0478                                     m_modifierState.latched,
0479                                     m_modifierState.locked,
0480                                     m_currentLayout);
0481 }
0482 
0483 QString Xkb::layoutName(xkb_layout_index_t index) const
0484 {
0485     if (!m_keymap) {
0486         return QString{};
0487     }
0488     return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, index));
0489 }
0490 
0491 QString Xkb::layoutName() const
0492 {
0493     return layoutName(m_currentLayout);
0494 }
0495 
0496 QString Xkb::layoutShortName(int index) const
0497 {
0498     return m_layoutList.value(index);
0499 }
0500 
0501 void Xkb::updateConsumedModifiers(uint32_t key)
0502 {
0503     Qt::KeyboardModifiers mods = Qt::NoModifier;
0504     if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_shiftModifier, XKB_CONSUMED_MODE_GTK) == 1) {
0505         mods |= Qt::ShiftModifier;
0506     }
0507     if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) {
0508         mods |= Qt::AltModifier;
0509     }
0510     if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) {
0511         mods |= Qt::ControlModifier;
0512     }
0513     if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) {
0514         mods |= Qt::MetaModifier;
0515     }
0516     m_consumedModifiers = mods;
0517 }
0518 
0519 Qt::KeyboardModifiers Xkb::modifiersRelevantForGlobalShortcuts(uint32_t scanCode) const
0520 {
0521     if (!m_state) {
0522         return Qt::NoModifier;
0523     }
0524     Qt::KeyboardModifiers mods = Qt::NoModifier;
0525     if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0526         mods |= Qt::ShiftModifier;
0527     }
0528     if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0529         mods |= Qt::AltModifier;
0530     }
0531     if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0532         mods |= Qt::ControlModifier;
0533     }
0534     if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0535         mods |= Qt::MetaModifier;
0536     }
0537     if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
0538         mods |= Qt::KeypadModifier;
0539     }
0540 
0541     Qt::KeyboardModifiers consumedMods = m_consumedModifiers;
0542     if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) {
0543         // test whether current keysym is a letter
0544         // in that case the shift should be removed from the consumed modifiers again
0545         // otherwise it would not be possible to trigger e.g. Shift+W as a shortcut
0546         // see BUG: 370341
0547         if (QChar::isLetter(toQtKey(m_keysym, scanCode, Qt::ControlModifier))) {
0548             consumedMods = Qt::KeyboardModifiers();
0549         }
0550     }
0551 
0552     return mods & ~consumedMods;
0553 }
0554 
0555 Qt::KeyboardModifiers Xkb::modifiersRelevantForTabBox() const
0556 {
0557     if (!m_state) {
0558         return Qt::NoModifier;
0559     }
0560     Qt::KeyboardModifiers mods = Qt::NoModifier;
0561     if (xkb_state_mod_index_is_active(m_state, m_shiftModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0562         mods |= Qt::ShiftModifier;
0563     }
0564     if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0565         mods |= Qt::AltModifier;
0566     }
0567     if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0568         mods |= Qt::ControlModifier;
0569     }
0570     if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
0571         mods |= Qt::MetaModifier;
0572     }
0573     return mods;
0574 }
0575 
0576 xkb_keysym_t Xkb::toKeysym(uint32_t key)
0577 {
0578     if (!m_state) {
0579         return XKB_KEY_NoSymbol;
0580     }
0581 
0582     // Workaround because there's some kind of overlap between KEY_ZENKAKUHANKAKU and TLDE
0583     // This key is important because some hardware manufacturers use it to indicate touchpad toggling.
0584     xkb_keysym_t ret = xkb_state_key_get_one_sym(m_state, key + EVDEV_OFFSET);
0585     if (ret == 0 && key == KEY_ZENKAKUHANKAKU) {
0586         ret = XKB_KEY_Zenkaku_Hankaku;
0587     }
0588     return ret;
0589 }
0590 
0591 QString Xkb::toString(xkb_keysym_t keysym)
0592 {
0593     if (!m_state || keysym == XKB_KEY_NoSymbol) {
0594         return QString();
0595     }
0596     QByteArray byteArray(7, 0);
0597     int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
0598     if (ok == -1 || ok == 0) {
0599         return QString();
0600     }
0601     return QString::fromUtf8(byteArray.constData());
0602 }
0603 
0604 Qt::Key Xkb::toQtKey(xkb_keysym_t keySym,
0605                      uint32_t scanCode,
0606                      Qt::KeyboardModifiers modifiers,
0607                      bool superAsMeta) const
0608 {
0609     // FIXME: passing superAsMeta doesn't have impact due to bug in the Qt function, so handle it below
0610     Qt::Key qtKey = Qt::Key(QXkbCommon::keysymToQtKey(keySym, modifiers, m_state, scanCode + EVDEV_OFFSET, superAsMeta));
0611 
0612     // FIXME: workarounds for symbols currently wrong/not mappable via keysymToQtKey()
0613     if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) {
0614         // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
0615         qtKey = Qt::Key_Meta;
0616     } else if (qtKey > 0xff && keySym <= 0xff) {
0617         // XKB_KEY_mu, XKB_KEY_ydiaeresis go here
0618         qtKey = Qt::Key(keySym);
0619     }
0620     return qtKey;
0621 }
0622 
0623 bool Xkb::shouldKeyRepeat(quint32 key) const
0624 {
0625     if (!m_keymap) {
0626         return false;
0627     }
0628     return xkb_keymap_key_repeats(m_keymap, key + EVDEV_OFFSET) != 0;
0629 }
0630 
0631 void Xkb::switchToNextLayout()
0632 {
0633     if (!m_keymap || !m_state) {
0634         return;
0635     }
0636     const xkb_layout_index_t numLayouts = xkb_keymap_num_layouts(m_keymap);
0637     const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts;
0638     switchToLayout(nextLayout);
0639 }
0640 
0641 void Xkb::switchToPreviousLayout()
0642 {
0643     if (!m_keymap || !m_state) {
0644         return;
0645     }
0646     const xkb_layout_index_t previousLayout = m_currentLayout == 0 ? numberOfLayouts() - 1 : m_currentLayout - 1;
0647     switchToLayout(previousLayout);
0648 }
0649 
0650 bool Xkb::switchToLayout(xkb_layout_index_t layout)
0651 {
0652     if (!m_keymap || !m_state || layout >= numberOfLayouts()) {
0653         return false;
0654     }
0655     const xkb_mod_mask_t depressed = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_DEPRESSED));
0656     const xkb_mod_mask_t latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
0657     const xkb_mod_mask_t locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
0658     xkb_state_update_mask(m_state, depressed, latched, locked, 0, 0, layout);
0659     updateModifiers();
0660     forwardModifiers();
0661     return true;
0662 }
0663 
0664 void Xkb::setModifierLatched(Qt::KeyboardModifier mod, bool latched)
0665 {
0666     xkb_mod_index_t modifier = XKB_MOD_INVALID;
0667 
0668     switch (mod) {
0669     case Qt::NoModifier: {
0670         break;
0671     }
0672     case Qt::ShiftModifier: {
0673         modifier = m_shiftModifier;
0674         break;
0675     }
0676     case Qt::AltModifier: {
0677         modifier = m_altModifier;
0678         break;
0679     }
0680     case Qt::ControlModifier: {
0681         modifier = m_controlModifier;
0682         break;
0683     }
0684     case Qt::MetaModifier: {
0685         modifier = m_metaModifier;
0686         break;
0687     }
0688     case Qt::GroupSwitchModifier: {
0689         // TODO
0690         break;
0691     }
0692     case Qt::KeypadModifier: {
0693         break;
0694     }
0695     case Qt::KeyboardModifierMask: {
0696         break;
0697     }
0698     }
0699 
0700     if (modifier != XKB_MOD_INVALID) {
0701         std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.latched};
0702         if (mask.size() > modifier) {
0703             mask[modifier] = latched;
0704             m_modifierState.latched = mask.to_ulong();
0705             xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.latched, m_modifierState.locked, 0, 0, m_currentLayout);
0706             m_modifierState.latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED));
0707         }
0708     }
0709 }
0710 
0711 void Xkb::setModifierLocked(Qt::KeyboardModifier mod, bool locked)
0712 {
0713     xkb_mod_index_t modifier = XKB_MOD_INVALID;
0714 
0715     switch (mod) {
0716     case Qt::NoModifier: {
0717         break;
0718     }
0719     case Qt::ShiftModifier: {
0720         modifier = m_shiftModifier;
0721         break;
0722     }
0723     case Qt::AltModifier: {
0724         modifier = m_altModifier;
0725         break;
0726     }
0727     case Qt::ControlModifier: {
0728         modifier = m_controlModifier;
0729         break;
0730     }
0731     case Qt::MetaModifier: {
0732         modifier = m_metaModifier;
0733         break;
0734     }
0735     case Qt::GroupSwitchModifier: {
0736         // TODO
0737         break;
0738     }
0739     case Qt::KeypadModifier: {
0740         break;
0741     }
0742     case Qt::KeyboardModifierMask: {
0743         break;
0744     }
0745     }
0746 
0747     if (modifier != XKB_MOD_INVALID) {
0748         std::bitset<sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};
0749         if (mask.size() > modifier) {
0750             mask[modifier] = locked;
0751             m_modifierState.locked = mask.to_ulong();
0752             xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.locked, m_modifierState.locked, 0, 0, m_currentLayout);
0753             m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED));
0754         }
0755     }
0756 }
0757 
0758 quint32 Xkb::numberOfLayouts() const
0759 {
0760     if (!m_keymap) {
0761         return 0;
0762     }
0763     return xkb_keymap_num_layouts(m_keymap);
0764 }
0765 
0766 void Xkb::setSeat(SeatInterface *seat)
0767 {
0768     m_seat = QPointer<SeatInterface>(seat);
0769 }
0770 
0771 std::optional<int> Xkb::keycodeFromKeysym(xkb_keysym_t keysym)
0772 {
0773     auto layout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
0774     const xkb_keycode_t max = xkb_keymap_max_keycode(m_keymap);
0775     for (xkb_keycode_t keycode = xkb_keymap_min_keycode(m_keymap); keycode < max; keycode++) {
0776         uint levelCount = xkb_keymap_num_levels_for_key(m_keymap, keycode, layout);
0777         for (uint currentLevel = 0; currentLevel < levelCount; currentLevel++) {
0778             const xkb_keysym_t *syms;
0779             uint num_syms = xkb_keymap_key_get_syms_by_level(m_keymap, keycode, layout, currentLevel, &syms);
0780             for (uint sym = 0; sym < num_syms; sym++) {
0781                 if (syms[sym] == keysym) {
0782                     return {keycode - EVDEV_OFFSET};
0783                 }
0784             }
0785         }
0786     }
0787     return {};
0788 }
0789 }
0790 
0791 #include "moc_xkb.cpp"