File indexing completed on 2024-11-10 04:58:08
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"