fix: preserve locked modifiers on ei backend
This commit is contained in:
parent
0d21f54cb2
commit
f8edc7b0ae
5 changed files with 160 additions and 9 deletions
|
|
@ -97,8 +97,9 @@ bool EiKeyState::fakeCtrlAltDel()
|
|||
|
||||
KeyModifierMask EiKeyState::pollActiveModifiers() const
|
||||
{
|
||||
const auto xkbMask = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_EFFECTIVE);
|
||||
return convertModMask(xkbMask);
|
||||
const auto effectiveMask = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_EFFECTIVE);
|
||||
const auto lockedMask = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED);
|
||||
return convertModMask(effectiveMask) | convertModMask(lockedMask, true);
|
||||
}
|
||||
|
||||
std::int32_t EiKeyState::pollActiveGroup() const
|
||||
|
|
@ -112,7 +113,7 @@ void EiKeyState::pollPressedKeys(KeyButtonSet &) const
|
|||
return;
|
||||
}
|
||||
|
||||
std::uint32_t EiKeyState::convertModMask(xkb_mod_mask_t xkbModMaskIn) const
|
||||
std::uint32_t EiKeyState::convertModMask(xkb_mod_mask_t xkbModMaskIn, bool mapMod2ToNumLock) const
|
||||
{
|
||||
// This is our own modifier mask, not xkb's.
|
||||
std::uint32_t modMaskOut = 0;
|
||||
|
|
@ -170,12 +171,12 @@ std::uint32_t EiKeyState::convertModMask(xkb_mod_mask_t xkbModMaskIn) const
|
|||
modMaskOut |= (1 << kKeyModifierBitAltGr);
|
||||
else if (strcmp(XKB_VMOD_NAME_LEVEL5, name) == 0)
|
||||
modMaskOut |= (1 << kKeyModifierBitLevel5Lock);
|
||||
else if (strcmp(XKB_VMOD_NAME_NUM, name) == 0)
|
||||
else if (strcmp(XKB_VMOD_NAME_NUM, name) == 0 || (mapMod2ToNumLock && strcmp(XKB_MOD_NAME_MOD2, name) == 0))
|
||||
modMaskOut |= (1 << kKeyModifierBitNumLock);
|
||||
else if (strcmp(XKB_VMOD_NAME_SCROLL, name) == 0)
|
||||
modMaskOut |= (1 << kKeyModifierBitScrollLock);
|
||||
else if ((strcmp(XKB_VMOD_NAME_META, name) == 0) || // virtual; the old meta (not the new meta/super/logo key)
|
||||
(strcmp(XKB_MOD_NAME_MOD2, name) == 0) || // spare, sometimes mapped to num lock.
|
||||
(!mapMod2ToNumLock && strcmp(XKB_MOD_NAME_MOD2, name) == 0) || // spare, sometimes mapped to num lock.
|
||||
(strcmp(XKB_MOD_NAME_MOD3, name) == 0) // spare, could be mapped to alt_r, caps lock, scroll lock, etc.
|
||||
)
|
||||
LOG_VERBOSE("modifier mask %s ignored", name);
|
||||
|
|
@ -346,11 +347,17 @@ void EiKeyState::updateXkbState(uint32_t keyval, bool isPressed)
|
|||
|
||||
void EiKeyState::clearStaleModifiers()
|
||||
{
|
||||
// Recreate the XKB state to clear stuck modifiers that happen when
|
||||
// modifier keys are press on client and released on server
|
||||
const auto lockedMods = xkb_state_serialize_mods(m_xkbState, XKB_STATE_MODS_LOCKED);
|
||||
const auto lockedLayout = xkb_state_serialize_layout(m_xkbState, XKB_STATE_LAYOUT_LOCKED);
|
||||
|
||||
// Recreate the XKB state to clear stuck depressed modifiers that happen when
|
||||
// modifier keys are pressed on the client and released on the server. Locked
|
||||
// modifiers are real keyboard state; do not clear NumLock/CapsLock/ScrollLock
|
||||
// during screen transitions.
|
||||
if (m_xkbState) {
|
||||
xkb_state_unref(m_xkbState);
|
||||
}
|
||||
m_xkbState = xkb_state_new(m_xkbKeymap);
|
||||
xkb_state_update_mask(m_xkbState, 0, 0, lockedMods, 0, 0, lockedLayout);
|
||||
}
|
||||
} // namespace deskflow
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ protected:
|
|||
void fakeKey(const Keystroke &keystroke) override;
|
||||
|
||||
private:
|
||||
std::uint32_t convertModMask(xkb_mod_mask_t xkbModMaskIn) const;
|
||||
std::uint32_t convertModMask(xkb_mod_mask_t xkbModMaskIn, bool mapMod2ToNumLock = false) const;
|
||||
void assignGeneratedModifiers(std::uint32_t keycode, KeyMap::KeyItem &item);
|
||||
|
||||
EiScreen *m_screen = nullptr;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,16 @@ elseif(APPLE)
|
|||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/platform"
|
||||
)
|
||||
elseif(UNIX)
|
||||
if(LIBEI_FOUND)
|
||||
create_test(
|
||||
NAME EiKeyStateTests
|
||||
DEPENDS platform
|
||||
LIBS base arch
|
||||
SOURCE EiKeyStateTests.cpp
|
||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/platform"
|
||||
)
|
||||
endif()
|
||||
|
||||
if (BUILD_X11_SUPPORT)
|
||||
create_test(
|
||||
NAME XWindowsClipboardTests
|
||||
|
|
|
|||
109
src/unittests/platform/EiKeyStateTests.cpp
Normal file
109
src/unittests/platform/EiKeyStateTests.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Deskflow Developers
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#include "EiKeyStateTests.h"
|
||||
|
||||
#include "base/EventQueue.h"
|
||||
#include "deskflow/AppUtil.h"
|
||||
#include "deskflow/KeyTypes.h"
|
||||
#include "platform/EiKeyState.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
class TestAppUtil : public AppUtil
|
||||
{
|
||||
public:
|
||||
int run() override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void startNode() override
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::string> getKeyboardLayoutList() override
|
||||
{
|
||||
return {"en"};
|
||||
}
|
||||
|
||||
std::string getCurrentLanguageCode() override
|
||||
{
|
||||
return "en";
|
||||
}
|
||||
};
|
||||
|
||||
const char TestKeymap[] = R"XKB(xkb_keymap {
|
||||
xkb_keycodes "test" {
|
||||
minimum = 8;
|
||||
maximum = 255;
|
||||
<LFSH> = 50;
|
||||
<NMLK> = 77;
|
||||
};
|
||||
xkb_types "test" {
|
||||
type "ONE_LEVEL" {
|
||||
modifiers = none;
|
||||
level_name[Level1] = "Any";
|
||||
};
|
||||
};
|
||||
xkb_compat "test" {
|
||||
interpret Shift_L+AnyOfOrNone(all) {
|
||||
action = SetMods(modifiers=Shift);
|
||||
};
|
||||
interpret Num_Lock+AnyOfOrNone(all) {
|
||||
action = LockMods(modifiers=Mod2);
|
||||
};
|
||||
};
|
||||
xkb_symbols "test" {
|
||||
key <LFSH> { [ Shift_L ] };
|
||||
key <NMLK> { [ Num_Lock ] };
|
||||
modifier_map Shift { <LFSH> };
|
||||
modifier_map Mod2 { <NMLK> };
|
||||
};
|
||||
};)XKB";
|
||||
|
||||
// XKB keycodes for TestKeymap.
|
||||
constexpr std::uint32_t LeftShiftKeycode = 50;
|
||||
constexpr std::uint32_t NumLockKeycode = 77;
|
||||
} // namespace
|
||||
|
||||
void EiKeyStateTests::initTestCase()
|
||||
{
|
||||
m_arch.init();
|
||||
m_log.setFilter(LogLevel::Level::Verbose);
|
||||
}
|
||||
|
||||
void EiKeyStateTests::clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndNumLockPreserved()
|
||||
{
|
||||
TestAppUtil appUtil;
|
||||
EventQueue eventQueue;
|
||||
deskflow::EiKeyState keyState(nullptr, &eventQueue);
|
||||
|
||||
QTemporaryFile keymapFile;
|
||||
QVERIFY(keymapFile.open());
|
||||
const QByteArray keymapData = QByteArray::fromRawData(TestKeymap, sizeof(TestKeymap) - 1);
|
||||
QCOMPARE(keymapFile.write(keymapData), keymapData.size());
|
||||
QVERIFY(keymapFile.flush());
|
||||
keyState.init(keymapFile.handle(), keymapFile.size());
|
||||
|
||||
keyState.updateXkbState(LeftShiftKeycode, true);
|
||||
keyState.updateXkbState(NumLockKeycode, true);
|
||||
keyState.updateXkbState(NumLockKeycode, false);
|
||||
|
||||
QVERIFY((keyState.pollActiveModifiers() & KeyModifierShift) != 0);
|
||||
QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0);
|
||||
|
||||
keyState.clearStaleModifiers();
|
||||
|
||||
QVERIFY((keyState.pollActiveModifiers() & KeyModifierShift) == 0);
|
||||
QVERIFY((keyState.pollActiveModifiers() & KeyModifierNumLock) != 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(EiKeyStateTests)
|
||||
25
src/unittests/platform/EiKeyStateTests.h
Normal file
25
src/unittests/platform/EiKeyStateTests.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Deskflow -- mouse and keyboard sharing utility
|
||||
* SPDX-FileCopyrightText: (C) 2026 Deskflow Developers
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "arch/Arch.h"
|
||||
#include "base/Log.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
class EiKeyStateTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void clearStaleModifiers_shiftDownAndNumLockOn_shiftClearedAndNumLockPreserved();
|
||||
|
||||
private:
|
||||
Arch m_arch;
|
||||
Log m_log;
|
||||
};
|
||||
Loading…
Reference in a new issue