diff --git a/autotests/integration/keyboard_layout_test.cpp b/autotests/integration/keyboard_layout_test.cpp --- a/autotests/integration/keyboard_layout_test.cpp +++ b/autotests/integration/keyboard_layout_test.cpp @@ -60,6 +60,7 @@ void testVirtualDesktopPolicy(); void testWindowPolicy(); void testApplicationPolicy(); + void testNumLock(); private: void reconfigureLayouts(); @@ -83,6 +84,7 @@ kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); kwinApp()->setKxkbConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + kwinApp()->setInputConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); kwinApp()->start(); QVERIFY(workspaceCreatedSpy.wait()); @@ -457,5 +459,45 @@ QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)")); } +void KeyboardLayoutTest::testNumLock() +{ + auto xkb = input()->keyboard()->xkb(); + // by default not set + QVERIFY(!xkb->modifiers().testFlag(Qt::KeypadModifier)); + quint32 timestamp = 0; + kwinApp()->platform()->keyboardKeyPressed(KEY_NUMLOCK, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_NUMLOCK, timestamp++); + // now it should be on + QVERIFY(xkb->modifiers().testFlag(Qt::KeypadModifier)); + // and back to off + kwinApp()->platform()->keyboardKeyPressed(KEY_NUMLOCK, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_NUMLOCK, timestamp++); + QVERIFY(!xkb->modifiers().testFlag(Qt::KeypadModifier)); + + // let's reconfigure to enable through config + auto group = kwinApp()->inputConfig()->group("Keyboard"); + group.writeEntry("NumLock", 0); + group.sync(); + xkb->reconfigure(); + // now it should be on + QVERIFY(xkb->modifiers().testFlag(Qt::KeypadModifier)); + // pressing should result in it being off + kwinApp()->platform()->keyboardKeyPressed(KEY_NUMLOCK, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_NUMLOCK, timestamp++); + QVERIFY(!xkb->modifiers().testFlag(Qt::KeypadModifier)); + + // pressing again should enable it + kwinApp()->platform()->keyboardKeyPressed(KEY_NUMLOCK, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_NUMLOCK, timestamp++); + QVERIFY(xkb->modifiers().testFlag(Qt::KeypadModifier)); + + // now reconfigure to disable on load + group.writeEntry("NumLock", 1); + group.sync(); + xkb->reconfigure(); + QVERIFY(!xkb->modifiers().testFlag(Qt::KeypadModifier)); +} + + WAYLANDTEST_MAIN(KeyboardLayoutTest) #include "keyboard_layout_test.moc" diff --git a/keyboard_input.cpp b/keyboard_input.cpp --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -111,6 +111,7 @@ Q_ASSERT(!m_inited); m_inited = true; const auto config = kwinApp()->kxkbConfig(); + m_xkb->setNumLockConfig(kwinApp()->inputConfig()); m_xkb->setConfig(config); m_input->installInputEventSpy(new KeyStateChangedSpy(m_input)); diff --git a/xkb.h b/xkb.h --- a/xkb.h +++ b/xkb.h @@ -58,6 +58,9 @@ void setConfig(KSharedConfigPtr config) { m_config = config; } + void setNumLockConfig(KSharedConfigPtr config) { + m_numLockConfig = config; + } void reconfigure(); void installKeymap(int fd, uint32_t size); @@ -130,6 +133,7 @@ xkb_mod_index_t m_controlModifier; xkb_mod_index_t m_altModifier; xkb_mod_index_t m_metaModifier; + xkb_mod_index_t m_numModifier; xkb_led_index_t m_numLock; xkb_led_index_t m_capsLock; xkb_led_index_t m_scrollLock; @@ -144,13 +148,20 @@ } m_compose; LEDs m_leds; KSharedConfigPtr m_config; + KSharedConfigPtr m_numLockConfig; struct { xkb_mod_index_t depressed = 0; xkb_mod_index_t latched = 0; xkb_mod_index_t locked = 0; } m_modifierState; + enum class Ownership { + Server, + Client + }; + Ownership m_ownership = Ownership::Server; + QPointer m_seat; }; diff --git a/xkb.cpp b/xkb.cpp --- a/xkb.cpp +++ b/xkb.cpp @@ -34,6 +34,7 @@ // system #include #include +#include Q_LOGGING_CATEGORY(KWIN_XKB, "kwin_xkbcommon", QtCriticalMsg) @@ -75,6 +76,7 @@ , m_controlModifier(0) , m_altModifier(0) , m_metaModifier(0) + , m_numModifier(0) , m_numLock(0) , m_capsLock(0) , m_scrollLock(0) @@ -214,6 +216,7 @@ qCDebug(KWIN_XKB) << "Could not map keymap from file"; return; } + m_ownership = Ownership::Client; updateKeymap(keymap); } @@ -238,6 +241,7 @@ m_controlModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_CTRL); m_altModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_ALT); m_metaModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_LOGO); + m_numModifier = xkb_keymap_mod_get_index(m_keymap, XKB_MOD_NAME_NUM); m_numLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_NUM); m_capsLock = xkb_keymap_led_get_index(m_keymap, XKB_LED_NAME_CAPS); @@ -249,8 +253,25 @@ m_modifierState.latched = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LATCHED)); m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)); + if (m_ownership == Ownership::Server && m_numModifier != XKB_MOD_INVALID && m_numLockConfig) { + const KConfigGroup config = m_numLockConfig->group("Keyboard"); + // STATE_ON = 0, STATE_OFF = 1, STATE_UNCHANGED = 2, see plasma-desktop/kcms/keyboard/kcmmisc.h + const auto setting = config.readEntry("NumLock", 2); + const bool numLockIsOn = xkb_state_mod_index_is_active(m_state, m_numModifier, XKB_STATE_MODS_LOCKED); + if ((setting == 0 && !numLockIsOn) || (setting == 1 && numLockIsOn)) { + std::bitset mask{m_modifierState.locked}; + if (mask.size() > m_numModifier) { + mask[m_numModifier] = (setting == 0); + m_modifierState.locked = mask.to_ulong(); + xkb_state_update_mask(m_state, m_modifierState.depressed, m_modifierState.latched, m_modifierState.locked, 0, 0, m_currentLayout); + m_modifierState.locked = xkb_state_serialize_mods(m_state, xkb_state_component(XKB_STATE_MODS_LOCKED)); + } + } + } + createKeymapFile(); forwardModifiers(); + updateModifiers(); } void Xkb::createKeymapFile() @@ -344,6 +365,9 @@ if (xkb_state_mod_index_is_active(m_state, m_metaModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { mods |= Qt::MetaModifier; } + if (xkb_state_mod_index_is_active(m_state, m_numModifier, XKB_STATE_MODS_EFFECTIVE) == 1) { + mods |= Qt::KeypadModifier; + } m_modifiers = mods; // update LEDs