10#include "dbusproperties_interface.h"
18#include <KConfigGroup>
21#include <QTemporaryFile>
22#include <QtGui/private/qxkbcommon_p.h>
24#include <xkbcommon/xkbcommon-compose.h>
25#include <xkbcommon/xkbcommon-keysyms.h>
29#include <linux/input-event-codes.h>
33Q_LOGGING_CATEGORY(KWIN_XKB,
"kwin_xkbcommon", QtWarningMsg)
37static const int EVDEV_OFFSET = 8;
38static const char *s_locale1Interface =
"org.freedesktop.locale1";
43static void xkbLogHandler(xkb_context *context, xkb_log_level priority,
const char *
format, va_list args)
46 int length = std::vsnprintf(buf, 1023,
format, args);
47 while (length > 0 && std::isspace(buf[length - 1])) {
54 case XKB_LOG_LEVEL_DEBUG:
55 qCDebug(KWIN_XKB,
"XKB: %.*s", length, buf);
57 case XKB_LOG_LEVEL_INFO:
58 qCInfo(KWIN_XKB,
"XKB: %.*s", length, buf);
60 case XKB_LOG_LEVEL_WARNING:
61 qCWarning(KWIN_XKB,
"XKB: %.*s", length, buf);
63 case XKB_LOG_LEVEL_ERROR:
64 case XKB_LOG_LEVEL_CRITICAL:
66 qCCritical(KWIN_XKB,
"XKB: %.*s", length, buf);
71#if HAVE_XKBCOMMON_NO_SECURE_GETENV
83 , m_controlModifier(0)
90 , m_modifiers(Qt::NoModifier)
91 , m_consumedModifiers(Qt::NoModifier)
92 , m_keysym(XKB_KEY_NoSymbol)
94 , m_followLocale1(followLocale1)
96 qRegisterMetaType<KWin::LEDs>();
98 qCDebug(KWIN_XKB) <<
"Could not create xkb context";
100 xkb_context_set_log_level(m_context, XKB_LOG_LEVEL_DEBUG);
101 xkb_context_set_log_fn(m_context, &xkbLogHandler);
105 QByteArray locale = qgetenv(
"LC_ALL");
106 if (locale.isEmpty()) {
107 locale = qgetenv(
"LC_CTYPE");
109 if (locale.isEmpty()) {
110 locale = qgetenv(
"LANG");
112 if (locale.isEmpty()) {
113 locale = QByteArrayLiteral(
"C");
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);
122 if (m_followLocale1) {
123 bool connected = QDBusConnection::systemBus().connect(s_locale1Interface,
"/org/freedesktop/locale1", QStringLiteral(
"org.freedesktop.DBus.Properties"),
124 QStringLiteral(
"PropertiesChanged"),
128 qCWarning(KWIN_XKB) <<
"Could not connect to org.freedesktop.locale1";
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);
144 m_configGroup = config->group(QStringLiteral(
"Layout"));
149 m_numLockConfig = config;
158 xkb_keymap *
keymap =
nullptr;
159 if (!qEnvironmentVariableIsSet(
"KWIN_XKB_DEFAULT_KEYMAP")) {
160 if (m_followLocale1) {
161 keymap = loadKeymapFromLocale1();
163 keymap = loadKeymapFromConfig();
167 qCDebug(KWIN_XKB) <<
"Could not create xkb keymap from configuration";
168 keymap = loadDefaultKeymap();
173 qCDebug(KWIN_XKB) <<
"Could not create default xkb keymap";
177static bool stringIsEmptyOrNull(
const char *str)
179 return str ==
nullptr || str[0] ==
'\0';
187void Xkb::applyEnvironmentRules(xkb_rule_names &ruleNames)
189 if (stringIsEmptyOrNull(ruleNames.rules)) {
190 ruleNames.rules = getenv(
"XKB_DEFAULT_RULES");
193 if (stringIsEmptyOrNull(ruleNames.model)) {
194 ruleNames.model = getenv(
"XKB_DEFAULT_MODEL");
197 if (stringIsEmptyOrNull(ruleNames.layout)) {
198 ruleNames.layout = getenv(
"XKB_DEFAULT_LAYOUT");
199 ruleNames.variant = getenv(
"XKB_DEFAULT_VARIANT");
202 if (ruleNames.options ==
nullptr) {
203 ruleNames.options = getenv(
"XKB_DEFAULT_OPTIONS");
207xkb_keymap *Xkb::loadKeymapFromConfig()
210 if (!m_configGroup.isValid()) {
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();
218 xkb_rule_names ruleNames = {
220 .model = model.constData(),
221 .layout = layout.constData(),
222 .variant = variant.constData(),
226 if (m_configGroup.readEntry(
"ResetOldOptions",
false)) {
227 ruleNames.options =
options.constData();
230 applyEnvironmentRules(ruleNames);
232 m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(
','));
234 return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
237xkb_keymap *Xkb::loadDefaultKeymap()
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);
245xkb_keymap *Xkb::loadKeymapFromLocale1()
247 OrgFreedesktopDBusPropertiesInterface locale1Properties(s_locale1Interface,
"/org/freedesktop/locale1", QDBusConnection::systemBus(),
this);
248 const QVariantMap properties = locale1Properties.GetAll(s_locale1Interface);
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();
255 xkb_rule_names ruleNames = {
257 .model = model.constData(),
258 .layout = layout.constData(),
259 .variant = variant.constData(),
260 .options =
options.constData(),
263 applyEnvironmentRules(ruleNames);
265 m_layoutList = QString::fromLatin1(ruleNames.layout).split(QLatin1Char(
','));
267 return xkb_keymap_new_from_names(m_context, &ruleNames, XKB_KEYMAP_COMPILE_NO_FLAGS);
270void Xkb::updateKeymap(xkb_keymap *keymap)
275 qCDebug(KWIN_XKB) <<
"Could not create XKB state";
281 bool numLockIsOn, capsLockIsOn;
282 static bool s_startup =
true;
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);
289 xkb_state_unref(m_state);
290 xkb_keymap_unref(m_keymap);
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);
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);
306 m_currentLayout = xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE);
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));
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));
324 if (s_startup || qEnvironmentVariableIsSet(
"KWIN_FORCE_NUM_LOCK_EVALUATION")) {
326 if (m_numLockConfig) {
327 const KConfigGroup config = m_numLockConfig->group(QStringLiteral(
"Keyboard"));
329 const auto setting = config.readEntry(
"NumLock", 2);
331 setLock(m_numModifier, !setting);
335 setLock(m_numModifier, numLockIsOn);
336 setLock(m_capsModifier, capsLockIsOn);
341 if (
auto *inputmethod = kwinApp()->
inputMethod()) {
347void Xkb::createKeymapFile()
350 if (currentKeymap.isEmpty()) {
353 m_seat->keyboard()->setKeymap(currentKeymap);
354 auto *inputmethod = kwinApp()->inputMethod();
358 if (
auto *keyboardGrab = inputmethod->keyboardGrab()) {
359 keyboardGrab->sendKeymap(currentKeymap);
365 if (!m_seat || !m_seat->keyboard()) {
373 UniqueCPtr<char> keymapString(xkb_keymap_get_as_string(m_keymap, XKB_KEYMAP_FORMAT_TEXT_V1));
377 return keymapString.get();
380void Xkb::updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
382 if (!m_keymap || !m_state) {
386 if (xkb_state_update_mask(m_state, modsDepressed, modsLatched, modsLocked, 0, 0, group) == 0) {
395 if (!m_keymap || !m_state) {
398 xkb_state_update_key(m_state, key + EVDEV_OFFSET,
static_cast<xkb_key_direction
>(
state));
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:
406 case XKB_COMPOSE_COMPOSED:
407 m_keysym = xkb_compose_state_get_one_sym(m_compose.state);
410 m_keysym = XKB_KEY_NoSymbol;
418 updateConsumedModifiers(key);
421void Xkb::updateModifiers()
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;
427 if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
428 mods |= Qt::AltModifier;
430 if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
431 mods |= Qt::ControlModifier;
433 if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
434 mods |= Qt::MetaModifier;
436 if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
437 mods |= Qt::KeypadModifier;
443 if (xkb_state_led_index_is_active(m_state, m_numLock) == 1) {
446 if (xkb_state_led_index_is_active(m_state, m_capsLock) == 1) {
449 if (xkb_state_led_index_is_active(m_state, m_scrollLock) == 1) {
452 if (m_leds !=
leds) {
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);
462 if (newLayout != m_currentLayout ||
depressed != m_modifierState.depressed ||
latched != m_modifierState.latched ||
locked != m_modifierState.locked) {
463 m_currentLayout = newLayout;
465 m_modifierState.latched =
latched;
466 m_modifierState.locked =
locked;
474 if (!m_seat || !m_seat->keyboard()) {
477 m_seat->notifyKeyboardModifiers(m_modifierState.depressed,
478 m_modifierState.latched,
479 m_modifierState.locked,
488 return QString::fromLocal8Bit(xkb_keymap_layout_get_name(m_keymap, index));
498 return m_layoutList.value(index);
501void Xkb::updateConsumedModifiers(uint32_t key)
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;
507 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_altModifier, XKB_CONSUMED_MODE_GTK) == 1) {
508 mods |= Qt::AltModifier;
510 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_controlModifier, XKB_CONSUMED_MODE_GTK) == 1) {
511 mods |= Qt::ControlModifier;
513 if (xkb_state_mod_index_is_consumed2(m_state, key + EVDEV_OFFSET, m_metaModifier, XKB_CONSUMED_MODE_GTK) == 1) {
514 mods |= Qt::MetaModifier;
516 m_consumedModifiers = mods;
522 return Qt::NoModifier;
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;
528 if (xkb_state_mod_index_is_active(m_state, m_altModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
529 mods |= Qt::AltModifier;
531 if (xkb_state_mod_index_is_active(m_state, m_controlModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
532 mods |= Qt::ControlModifier;
534 if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) {
535 mods |= Qt::MetaModifier;
537 if (m_keysym >= XKB_KEY_KP_Space && m_keysym <= XKB_KEY_KP_9) {
538 mods |= Qt::KeypadModifier;
541 Qt::KeyboardModifiers consumedMods = m_consumedModifiers;
542 if ((mods & Qt::ShiftModifier) && (consumedMods == Qt::ShiftModifier)) {
547 if (QChar::isLetter(
toQtKey(m_keysym, scanCode, Qt::ControlModifier))) {
548 consumedMods = Qt::KeyboardModifiers();
552 return mods & ~consumedMods;
558 return XKB_KEY_NoSymbol;
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;
572 if (!m_state || keysym == XKB_KEY_NoSymbol) {
575 QByteArray byteArray(7, 0);
576 int ok = xkb_keysym_to_utf8(keysym, byteArray.data(), byteArray.size());
577 if (ok == -1 || ok == 0) {
580 return QString::fromUtf8(byteArray.constData());
585 Qt::KeyboardModifiers modifiers,
586 bool superAsMeta)
const
589 Qt::Key qtKey = Qt::Key(QXkbCommon::keysymToQtKey(keySym,
modifiers, m_state, scanCode + EVDEV_OFFSET, superAsMeta));
592 if (superAsMeta && (qtKey == Qt::Key_Super_L || qtKey == Qt::Key_Super_R)) {
594 qtKey = Qt::Key_Meta;
595 }
else if (qtKey > 0xff && keySym <= 0xff) {
597 qtKey = Qt::Key(keySym);
607 return xkb_keymap_key_repeats(m_keymap, key + EVDEV_OFFSET) != 0;
612 if (!m_keymap || !m_state) {
616 const xkb_layout_index_t nextLayout = (xkb_state_serialize_layout(m_state, XKB_STATE_LAYOUT_EFFECTIVE) + 1) % numLayouts;
622 if (!m_keymap || !m_state) {
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));
648 case Qt::NoModifier: {
651 case Qt::ShiftModifier: {
652 modifier = m_shiftModifier;
655 case Qt::AltModifier: {
656 modifier = m_altModifier;
659 case Qt::ControlModifier: {
660 modifier = m_controlModifier;
663 case Qt::MetaModifier: {
664 modifier = m_metaModifier;
667 case Qt::GroupSwitchModifier: {
671 case Qt::KeypadModifier: {
674 case Qt::KeyboardModifierMask: {
679 if (modifier != XKB_MOD_INVALID) {
680 std::bitset<
sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.latched};
681 if (mask.size() > modifier) {
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));
695 case Qt::NoModifier: {
698 case Qt::ShiftModifier: {
699 modifier = m_shiftModifier;
702 case Qt::AltModifier: {
703 modifier = m_altModifier;
706 case Qt::ControlModifier: {
707 modifier = m_controlModifier;
710 case Qt::MetaModifier: {
711 modifier = m_metaModifier;
714 case Qt::GroupSwitchModifier: {
718 case Qt::KeypadModifier: {
721 case Qt::KeyboardModifierMask: {
726 if (modifier != XKB_MOD_INVALID) {
727 std::bitset<
sizeof(xkb_mod_mask_t) * 8> mask{m_modifierState.locked};
728 if (mask.size() > modifier) {
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));
742 return xkb_keymap_num_layouts(m_keymap);
747 m_seat = QPointer<SeatInterface>(seat);
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++) {
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};
770#include "moc_xkb.cpp"
Represents a Seat on the Wayland Display.
Qt::KeyboardModifiers modifiers() const
void setNumLockConfig(const KSharedConfigPtr &config)
void switchToNextLayout()
Qt::KeyboardModifiers modifiersRelevantForGlobalShortcuts(uint32_t scanCode=0) const
QString toString(xkb_keysym_t keysym)
QString layoutShortName(int index) const
void setConfig(const KSharedConfigPtr &config)
void updateKey(uint32_t key, InputRedirection::KeyboardKeyState state)
xkb_mod_index_t depressed
void ledsChanged(const LEDs &leds)
void setSeat(SeatInterface *seat)
xkb_keysym_t toKeysym(uint32_t key)
xkb_compose_state * state
void setModifierLocked(Qt::KeyboardModifier mod, bool locked)
void modifierStateChanged()
quint32 numberOfLayouts() const
Qt::Key toQtKey(xkb_keysym_t keysym, uint32_t scanCode=0, Qt::KeyboardModifiers modifiers=Qt::KeyboardModifiers(), bool superAsMeta=false) const
Xkb(bool followLocale1=false)
QString layoutName() const
void setModifierLatched(Qt::KeyboardModifier mod, bool latched)
std::optional< int > keycodeFromKeysym(xkb_keysym_t keysym)
xkb_keymap * keymap() const
QByteArray keymapContents() const
void switchToPreviousLayout()
void updateModifiers(uint32_t modsDepressed, uint32_t modsLatched, uint32_t modsLocked, uint32_t group)
bool shouldKeyRepeat(quint32 key) const
bool switchToLayout(xkb_layout_index_t layout)
MockInputMethod * inputMethod()
constexpr xkb_context_flags KWIN_XKB_CONTEXT_FLAGS
std::unique_ptr< T, CDeleter > UniqueCPtr