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