diff --git a/hyprtester/plugin/src/main.cpp b/hyprtester/plugin/src/main.cpp index 17b1e02a8..01ec7b385 100644 --- a/hyprtester/plugin/src/main.cpp +++ b/hyprtester/plugin/src/main.cpp @@ -8,9 +8,13 @@ #include #include #include +#include #include #undef private +#include +using namespace Hyprutils::Utils; + #include "globals.hpp" // Do NOT change this function. @@ -48,11 +52,95 @@ static SDispatchResult snapMove(std::string in) { return {}; } +class CTestKeyboard : public IKeyboard { + public: + static SP create(bool isVirtual) { + auto keeb = SP(new CTestKeyboard()); + keeb->m_self = keeb; + keeb->m_isVirtual = isVirtual; + keeb->m_shareStates = !isVirtual; + return keeb; + } + + virtual bool isVirtual() { + return m_isVirtual; + } + + virtual SP aq() { + return nullptr; + } + + void sendKey(uint32_t key, bool pressed) { + auto event = IKeyboard::SKeyEvent{ + .timeMs = static_cast(Time::millis(Time::steadyNow())), + .keycode = key, + .state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, + }; + updatePressed(event.keycode, pressed); + m_keyboardEvents.key.emit(event); + } + + void destroy() { + m_events.destroy.emit(); + } + + private: + bool m_isVirtual; +}; + +static SDispatchResult vkb(std::string in) { + auto tkb0 = CTestKeyboard::create(false); + auto tkb1 = CTestKeyboard::create(false); + auto vkb0 = CTestKeyboard::create(true); + + g_pInputManager->newKeyboard(tkb0); + g_pInputManager->newKeyboard(tkb1); + g_pInputManager->newKeyboard(vkb0); + + CScopeGuard x([&] { + tkb0->destroy(); + tkb1->destroy(); + vkb0->destroy(); + }); + + const auto& PRESSED = g_pInputManager->getKeysFromAllKBs(); + const uint32_t TESTKEY = 1; + + tkb0->sendKey(TESTKEY, true); + if (!std::ranges::contains(PRESSED, TESTKEY)) { + return { + .success = false, + .error = "Expected pressed key not found", + }; + } + + tkb1->sendKey(TESTKEY, true); + tkb0->sendKey(TESTKEY, false); + if (!std::ranges::contains(PRESSED, TESTKEY)) { + return { + .success = false, + .error = "Expected pressed key not found (kb share state)", + }; + } + + vkb0->sendKey(TESTKEY, true); + tkb1->sendKey(TESTKEY, false); + if (std::ranges::contains(PRESSED, TESTKEY)) { + return { + .success = false, + .error = "Expected released key found in pressed (vkb no share state)", + }; + } + + return {}; +} + APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { PHANDLE = handle; HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:test", ::test); HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:snapmove", ::snapMove); + HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:vkb", ::vkb); return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"}; } diff --git a/hyprtester/src/main.cpp b/hyprtester/src/main.cpp index 6fd42e1aa..2fa27fa8c 100644 --- a/hyprtester/src/main.cpp +++ b/hyprtester/src/main.cpp @@ -225,6 +225,9 @@ int main(int argc, char** argv, char** envp) { NLog::log("{}running plugin test", Colors::YELLOW); EXPECT(testPlugin(), true); + NLog::log("{}running vkb test from plugin", Colors::YELLOW); + EXPECT(testVkb(), true); + // kill hyprland NLog::log("{}dispatching exit", Colors::YELLOW); getFromSocket("/dispatch exit"); diff --git a/hyprtester/src/tests/plugin/plugin.cpp b/hyprtester/src/tests/plugin/plugin.cpp index 94470d6ff..ffcc351ac 100644 --- a/hyprtester/src/tests/plugin/plugin.cpp +++ b/hyprtester/src/tests/plugin/plugin.cpp @@ -19,3 +19,13 @@ bool testPlugin() { } return true; } + +bool testVkb() { + const auto RESPONSE = getFromSocket("/dispatch plugin:test:vkb"); + + if (RESPONSE != "ok") { + NLog::log("{}Vkb tests failed, tests returned:\n{}{}", Colors::RED, Colors::RESET, RESPONSE); + return false; + } + return true; +} diff --git a/hyprtester/src/tests/plugin/plugin.hpp b/hyprtester/src/tests/plugin/plugin.hpp index bd93c0877..1766c66e6 100644 --- a/hyprtester/src/tests/plugin/plugin.hpp +++ b/hyprtester/src/tests/plugin/plugin.hpp @@ -1,3 +1,4 @@ #pragma once -bool testPlugin(); \ No newline at end of file +bool testPlugin(); +bool testVkb(); diff --git a/src/config/ConfigDescriptions.hpp b/src/config/ConfigDescriptions.hpp index 852a86d01..aeff39067 100644 --- a/src/config/ConfigDescriptions.hpp +++ b/src/config/ConfigDescriptions.hpp @@ -676,6 +676,23 @@ inline static const std::vector CONFIG_OPTIONS = { .data = SConfigOptionDescription::SBoolData{true}, }, + /* + * input:virtualkeyboard: + */ + + SConfigOptionDescription{ + .value = "input:virtualkeyboard:share_states", + .description = "Unify key down states and modifier states with other keyboards", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + SConfigOptionDescription{ + .value = "input:virtualkeyboard:release_pressed_on_close", + .description = "Release all pressed keys by virtual keyboard on close.", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{false}, + }, + /* * input:tablet: */ @@ -1150,6 +1167,12 @@ inline static const std::vector CONFIG_OPTIONS = { .type = CONFIG_OPTION_BOOL, .data = SConfigOptionDescription::SBoolData{false}, }, + SConfigOptionDescription{ + .value = "misc:name_vk_after_proc", + .description = "Name virtual keyboards after the processes that create them. E.g. /usr/bin/fcitx5 will have hl-virtual-keyboard-fcitx5.", + .type = CONFIG_OPTION_BOOL, + .data = SConfigOptionDescription::SBoolData{true}, + }, SConfigOptionDescription{ .value = "misc:always_follow_on_dnd", .description = "Will make mouse focus follow the mouse when drag and dropping. Recommended to leave it enabled, especially for people using focus follows mouse at 0.", diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index d49add0e1..337682dce 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -473,6 +473,7 @@ CConfigManager::CConfigManager() { registerConfigVar("misc:vrr", Hyprlang::INT{0}); registerConfigVar("misc:mouse_move_enables_dpms", Hyprlang::INT{0}); registerConfigVar("misc:key_press_enables_dpms", Hyprlang::INT{0}); + registerConfigVar("misc:name_vk_after_proc", Hyprlang::INT{1}); registerConfigVar("misc:always_follow_on_dnd", Hyprlang::INT{1}); registerConfigVar("misc:layers_hog_keyboard_focus", Hyprlang::INT{1}); registerConfigVar("misc:animate_manual_resizes", Hyprlang::INT{0}); @@ -662,6 +663,8 @@ CConfigManager::CConfigManager() { registerConfigVar("input:touchdevice:transform", Hyprlang::INT{-1}); registerConfigVar("input:touchdevice:output", {"[[Auto]]"}); registerConfigVar("input:touchdevice:enabled", Hyprlang::INT{1}); + registerConfigVar("input:virtualkeyboard:share_states", Hyprlang::INT{0}); + registerConfigVar("input:virtualkeyboard:release_pressed_on_close", Hyprlang::INT{0}); registerConfigVar("input:tablet:transform", Hyprlang::INT{0}); registerConfigVar("input:tablet:output", {STRVAL_EMPTY}); registerConfigVar("input:tablet:region_position", Hyprlang::VEC2{0, 0}); @@ -797,6 +800,8 @@ CConfigManager::CConfigManager() { m_config->addSpecialConfigValue("device", "flip_y", Hyprlang::INT{0}); // only for touchpads m_config->addSpecialConfigValue("device", "drag_3fg", Hyprlang::INT{0}); // only for touchpads m_config->addSpecialConfigValue("device", "keybinds", Hyprlang::INT{1}); // enable/disable keybinds + m_config->addSpecialConfigValue("device", "share_states", Hyprlang::INT{0}); // only for virtualkeyboards + m_config->addSpecialConfigValue("device", "release_pressed_on_close", Hyprlang::INT{0}); // only for virtualkeyboards m_config->addSpecialCategory("monitorv2", {.key = "output"}); m_config->addSpecialConfigValue("monitorv2", "disabled", Hyprlang::INT{0}); diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 7cdd8ac09..cec410873 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -1342,7 +1342,7 @@ static std::string switchXKBLayoutRequest(eHyprCtlOutputFormat format, std::stri } return result.empty() ? "ok" : result; } else { - auto k = std::ranges::find_if(g_pInputManager->m_keyboards, [&](const auto& other) { return other->m_hlName == g_pInputManager->deviceNameToInternalString(KB); }); + auto k = std::ranges::find_if(g_pInputManager->m_keyboards, [&](const auto& other) { return other->m_hlName == deviceNameToInternalString(KB); }); if (k == g_pInputManager->m_keyboards.end()) return "device not found"; diff --git a/src/devices/IKeyboard.cpp b/src/devices/IKeyboard.cpp index 89ad8c164..0a22eb6ea 100644 --- a/src/devices/IKeyboard.cpp +++ b/src/devices/IKeyboard.cpp @@ -378,19 +378,6 @@ bool IKeyboard::updateModifiersState() { } void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) { - - const auto contains = std::ranges::find(m_pressedXKB, xkbKey) != m_pressedXKB.end(); - - if (contains && pressed) - return; - if (!contains && !pressed) - return; - - if (contains) - std::erase(m_pressedXKB, xkbKey); - else - m_pressedXKB.emplace_back(xkbKey); - xkb_state_update_key(m_xkbState, xkbKey, pressed ? XKB_KEY_DOWN : XKB_KEY_UP); if (updateModifiersState()) { @@ -405,3 +392,27 @@ void IKeyboard::updateXkbStateWithKey(uint32_t xkbKey, bool pressed) { }); } } + +bool IKeyboard::updatePressed(uint32_t key, bool pressed) { + const auto contains = getPressed(key); + + if (contains && pressed) + return false; + if (!contains && !pressed) + return false; + + if (contains) + std::erase(m_pressed, key); + else + m_pressed.emplace_back(key); + + return true; +} + +bool IKeyboard::getPressed(uint32_t key) { + return std::ranges::contains(m_pressed, key); +} + +bool IKeyboard::shareStates() { + return m_shareStates; +} diff --git a/src/devices/IKeyboard.hpp b/src/devices/IKeyboard.hpp index 25a7efd13..f4fb667b4 100644 --- a/src/devices/IKeyboard.hpp +++ b/src/devices/IKeyboard.hpp @@ -24,10 +24,13 @@ enum eKeyboardModifiers { class IKeyboard : public IHID { public: virtual ~IKeyboard(); - virtual uint32_t getCapabilities(); - virtual eHIDType getType(); - virtual bool isVirtual() = 0; - virtual SP aq() = 0; + virtual uint32_t getCapabilities(); + virtual eHIDType getType(); + virtual bool isVirtual() = 0; + virtual wl_client* getClient() { + return nullptr; + }; + virtual SP aq() = 0; struct SKeyEvent { uint32_t timeMs = 0; @@ -73,6 +76,8 @@ class IKeyboard : public IHID { bool updateModifiersState(); // rets whether changed void updateXkbStateWithKey(uint32_t xkbKey, bool pressed); void updateKeymapFD(); + bool getPressed(uint32_t key); + bool shareStates(); bool m_active = false; bool m_enabled = true; @@ -118,5 +123,9 @@ class IKeyboard : public IHID { private: void clearManuallyAllocd(); - std::vector m_pressedXKB; + std::vector m_pressed; + + protected: + bool updatePressed(uint32_t key, bool pressed); + bool m_shareStates = true; }; diff --git a/src/devices/Keyboard.cpp b/src/devices/Keyboard.cpp index 9ce37e6b4..80f76c17c 100644 --- a/src/devices/Keyboard.cpp +++ b/src/devices/Keyboard.cpp @@ -29,13 +29,16 @@ CKeyboard::CKeyboard(SP keeb) : m_keyboard(keeb) { }); m_listeners.key = keeb->events.key.listen([this](const Aquamarine::IKeyboard::SKeyEvent& event) { + const auto UPDATED = updatePressed(event.key, event.pressed); + m_keyboardEvents.key.emit(SKeyEvent{ .timeMs = event.timeMs, .keycode = event.key, .state = event.pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, }); - updateXkbStateWithKey(event.key + 8, event.pressed); + if (UPDATED) + updateXkbStateWithKey(event.key + 8, event.pressed); }); m_listeners.modifiers = keeb->events.modifiers.listen([this] { diff --git a/src/devices/VirtualKeyboard.cpp b/src/devices/VirtualKeyboard.cpp index ea21a22b7..97b7626a1 100644 --- a/src/devices/VirtualKeyboard.cpp +++ b/src/devices/VirtualKeyboard.cpp @@ -1,6 +1,8 @@ #include "VirtualKeyboard.hpp" #include "../defines.hpp" #include "../protocols/VirtualKeyboard.hpp" +#include "../config/ConfigManager.hpp" +#include SP CVirtualKeyboard::create(SP keeb) { SP pKeeb = SP(new CVirtualKeyboard(keeb)); @@ -19,7 +21,10 @@ CVirtualKeyboard::CVirtualKeyboard(SP keeb_) : m_key m_events.destroy.emit(); }); - m_listeners.key = keeb_->m_events.key.listen([this](const auto& event) { m_keyboardEvents.key.emit(event); }); + m_listeners.key = keeb_->m_events.key.listen([this](const auto& event) { + updatePressed(event.keycode, event.state == WL_KEYBOARD_KEY_STATE_PRESSED); + m_keyboardEvents.key.emit(event); + }); m_listeners.modifiers = keeb_->m_events.modifiers.listen([this](const SModifiersEvent& event) { updateModifiers(event.depressed, event.latched, event.locked, event.group); m_keyboardEvents.modifiers.emit(SModifiersEvent{ @@ -39,7 +44,8 @@ CVirtualKeyboard::CVirtualKeyboard(SP keeb_) : m_key m_keyboardEvents.keymap.emit(event); }); - m_deviceName = keeb_->m_name; + m_deviceName = keeb_->m_name; + m_shareStates = g_pConfigManager->getDeviceInt(m_deviceName, "share_states", "input:virtualkeyboard:share_states"); } bool CVirtualKeyboard::isVirtual() { diff --git a/src/devices/VirtualKeyboard.hpp b/src/devices/VirtualKeyboard.hpp index 05e0a2552..a0783d93a 100644 --- a/src/devices/VirtualKeyboard.hpp +++ b/src/devices/VirtualKeyboard.hpp @@ -11,7 +11,7 @@ class CVirtualKeyboard : public IKeyboard { virtual bool isVirtual(); virtual SP aq(); - wl_client* getClient(); + virtual wl_client* getClient(); private: CVirtualKeyboard(SP keeb); diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp index 90383ca50..721855fd7 100644 --- a/src/helpers/MiscFunctions.cpp +++ b/src/helpers/MiscFunctions.cpp @@ -876,3 +876,55 @@ bool isNvidiaDriverVersionAtLeast(int threshold) { return driverMajor >= threshold; } + +std::expected binaryNameForWlClient(wl_client* client) { + if (!client) + return std::unexpected("client unknown"); + + pid_t pid = 0; + wl_client_get_credentials(client, &pid, nullptr, nullptr); + + return binaryNameForPid(pid); +} + +std::expected binaryNameForPid(pid_t pid) { + if (pid <= 0) + return std::unexpected("No pid for client"); + +#if defined(KERN_PROC_PATHNAME) + int mib[] = { + CTL_KERN, +#if defined(__NetBSD__) + KERN_PROC_ARGS, + pid, + KERN_PROC_PATHNAME, +#else + KERN_PROC, + KERN_PROC_PATHNAME, + pid, +#endif + }; + u_int miblen = sizeof(mib) / sizeof(mib[0]); + char exe[PATH_MAX] = "/nonexistent"; + size_t sz = sizeof(exe); + sysctl(mib, miblen, &exe, &sz, NULL, 0); + std::string path = exe; +#else + std::string path = std::format("/proc/{}/exe", (uint64_t)pid); +#endif + std::error_code ec; + + std::string fullPath = std::filesystem::canonical(path, ec); + + if (ec) + return std::unexpected("canonical failed"); + + return fullPath; +} + +std::string deviceNameToInternalString(std::string in) { + std::ranges::replace(in, ' ', '-'); + std::ranges::replace(in, '\n', '-'); + std::ranges::transform(in, in.begin(), ::tolower); + return in; +} diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp index f361313bc..8e507b6b1 100644 --- a/src/helpers/MiscFunctions.hpp +++ b/src/helpers/MiscFunctions.hpp @@ -19,27 +19,30 @@ struct SWorkspaceIDName { std::string name; }; -std::string absolutePath(const std::string&, const std::string&); -std::string escapeJSONStrings(const std::string& str); -bool isDirection(const std::string&); -bool isDirection(const char&); -SWorkspaceIDName getWorkspaceIDNameFromString(const std::string&); -std::optional cleanCmdForWorkspace(const std::string&, std::string); -float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); -void logSystemInfo(); -std::string execAndGet(const char*); -int64_t getPPIDof(int64_t pid); -std::expected configStringToInt(const std::string&); -Vector2D configStringToVector2D(const std::string&); -std::optional getPlusMinusKeywordResult(std::string in, float relative); -double normalizeAngleRad(double ang); -std::vector getBacktrace(); -void throwError(const std::string& err); -bool envEnabled(const std::string& env); -Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len); -bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr); -float stringToPercentage(const std::string& VALUE, const float REL); -bool isNvidiaDriverVersionAtLeast(int threshold); +std::string absolutePath(const std::string&, const std::string&); +std::string escapeJSONStrings(const std::string& str); +bool isDirection(const std::string&); +bool isDirection(const char&); +SWorkspaceIDName getWorkspaceIDNameFromString(const std::string&); +std::optional cleanCmdForWorkspace(const std::string&, std::string); +float vecToRectDistanceSquared(const Vector2D& vec, const Vector2D& p1, const Vector2D& p2); +void logSystemInfo(); +std::string execAndGet(const char*); +int64_t getPPIDof(int64_t pid); +std::expected configStringToInt(const std::string&); +Vector2D configStringToVector2D(const std::string&); +std::optional getPlusMinusKeywordResult(std::string in, float relative); +double normalizeAngleRad(double ang); +std::vector getBacktrace(); +void throwError(const std::string& err); +bool envEnabled(const std::string& env); +Hyprutils::OS::CFileDescriptor allocateSHMFile(size_t len); +bool allocateSHMFilePair(size_t size, Hyprutils::OS::CFileDescriptor& rw_fd_ptr, Hyprutils::OS::CFileDescriptor& ro_fd_ptr); +float stringToPercentage(const std::string& VALUE, const float REL); +bool isNvidiaDriverVersionAtLeast(int threshold); +std::expected binaryNameForWlClient(wl_client* client); +std::expected binaryNameForPid(pid_t pid); +std::string deviceNameToInternalString(std::string in); template [[deprecated("use std::format instead")]] std::string getFormat(std::format_string fmt, Args&&... args) { diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp index d9b2bc1f0..27b6f4b9e 100644 --- a/src/managers/KeybindManager.cpp +++ b/src/managers/KeybindManager.cpp @@ -457,7 +457,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { if (handleInternalKeybinds(internalKeysym)) return false; - const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); + const auto MODS = g_pInputManager->getModsFromAllKBs(); m_timeLastMs = e.timeMs; m_lastCode = KEYCODE; @@ -515,7 +515,7 @@ bool CKeybindManager::onKeyEvent(std::any event, SP pKeyboard) { } bool CKeybindManager::onAxisEvent(const IPointer::SAxisEvent& e) { - const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); + const auto MODS = g_pInputManager->getModsFromAllKBs(); static auto PDELAY = CConfigValue("binds:scroll_event_delay"); @@ -546,7 +546,7 @@ bool CKeybindManager::onAxisEvent(const IPointer::SAxisEvent& e) { } bool CKeybindManager::onMouseEvent(const IPointer::SButtonEvent& e) { - const auto MODS = g_pInputManager->accumulateModsFromAllKBs(); + const auto MODS = g_pInputManager->getModsFromAllKBs(); bool suppressEvent = false; @@ -2454,7 +2454,7 @@ SDispatchResult CKeybindManager::pass(std::string regexp) { g_pSeatManager->setPointerFocus(PWINDOW->m_wlSurface->resource(), {1, 1}); } - g_pSeatManager->sendKeyboardMods(g_pInputManager->accumulateModsFromAllKBs(), 0, 0, 0); + g_pSeatManager->sendKeyboardMods(g_pInputManager->getModsFromAllKBs(), 0, 0, 0); if (g_pKeybindManager->m_passPressed == 1) { if (g_pKeybindManager->m_lastCode != 0) diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 762172387..67752e2aa 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -11,7 +11,11 @@ #include "../managers/HookSystemManager.hpp" #include "wlr-layer-shell-unstable-v1.hpp" #include +#include #include +#include + +using namespace Hyprutils::Utils; CSeatManager::CSeatManager() { m_listeners.newSeatResource = PROTO::seat->m_events.newSeatResource.listen([this](const auto& resource) { onNewSeatResource(resource); }); @@ -136,6 +140,18 @@ void CSeatManager::setKeyboardFocus(SP surf) { return; } + wl_array keys; + wl_array_init(&keys); + CScopeGuard x([&keys] { wl_array_release(&keys); }); + + const auto& PRESSED = g_pInputManager->getKeysFromAllKBs(); + static_assert(std::is_same_v::value_type, uint32_t>, "Element type different from keycode type uint32_t"); + + const auto PRESSEDARRSIZE = PRESSED.size() * sizeof(uint32_t); + const auto PKEYS = wl_array_add(&keys, PRESSEDARRSIZE); + if (PKEYS) + memcpy(PKEYS, PRESSED.data(), PRESSEDARRSIZE); + auto client = surf->client(); for (auto const& r : m_seatResources | std::views::reverse) { if (r->resource->client() != client) @@ -146,7 +162,7 @@ void CSeatManager::setKeyboardFocus(SP surf) { if (!k) continue; - k->sendEnter(surf); + k->sendEnter(surf, &keys); k->sendMods(m_keyboard->m_modifiersState.depressed, m_keyboard->m_modifiersState.latched, m_keyboard->m_modifiersState.locked, m_keyboard->m_modifiersState.group); } } diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp index 6c23c4932..cbb4c3cc1 100644 --- a/src/managers/input/InputManager.cpp +++ b/src/managers/input/InputManager.cpp @@ -39,6 +39,7 @@ #include "../../managers/permissions/DynamicPermissionManager.hpp" #include "../../helpers/time/Time.hpp" +#include "../../helpers/MiscFunctions.hpp" #include @@ -931,6 +932,14 @@ Vector2D CInputManager::getMouseCoordsInternal() { return g_pPointerManager->position(); } +void CInputManager::newKeyboard(SP keeb) { + const auto PNEWKEYBOARD = m_keyboards.emplace_back(keeb); + + setupKeyboard(PNEWKEYBOARD); + + Debug::log(LOG, "New keyboard created, pointers Hypr: {:x}", (uintptr_t)PNEWKEYBOARD.get()); +} + void CInputManager::newKeyboard(SP keyboard) { const auto PNEWKEYBOARD = m_keyboards.emplace_back(CKeyboard::create(keyboard)); @@ -1398,18 +1407,36 @@ void CInputManager::onKeyboardKey(const IKeyboard::SKeyEvent& event, SPisVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); + const auto IME = m_relay.m_inputMethod.lock(); + const bool HASIME = IME && IME->hasGrab(); + const bool USEIME = HASIME && !DISALLOWACTION; + const auto EMAP = std::unordered_map{{"keyboard", pKeyboard}, {"event", event}}; EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP); - bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(event, pKeyboard); + bool passEvent = DISALLOWACTION; + + if (!DISALLOWACTION) + passEvent = g_pKeybindManager->onKeyEvent(event, pKeyboard); if (passEvent) { - const auto IME = m_relay.m_inputMethod.lock(); - - if (IME && IME->hasGrab() && !DISALLOWACTION) { + if (USEIME) { IME->setKeyboard(pKeyboard); IME->sendKey(event.timeMs, event.keycode, event.state); } else { + const auto PRESSED = shareKeyFromAllKBs(event.keycode, event.state == WL_KEYBOARD_KEY_STATE_PRESSED); + const auto CONTAINS = std::ranges::contains(m_pressed, event.keycode); + + if (CONTAINS && PRESSED) + return; + if (!CONTAINS && !PRESSED) + return; + + if (CONTAINS) + std::erase(m_pressed, event.keycode); + else + m_pressed.emplace_back(event.keycode); + g_pSeatManager->setKeyboard(pKeyboard); g_pSeatManager->sendKeyboardKey(event.timeMs, event.keycode, event.state); } @@ -1424,10 +1451,9 @@ void CInputManager::onKeyboardMod(SP pKeyboard) { const bool DISALLOWACTION = pKeyboard->isVirtual() && shouldIgnoreVirtualKeyboard(pKeyboard); - const auto ALLMODS = accumulateModsFromAllKBs(); - - auto MODS = pKeyboard->m_modifiersState; - MODS.depressed = ALLMODS; + auto MODS = pKeyboard->m_modifiersState; + const auto ALLMODS = shareModsFromAllKBs(MODS.depressed); + MODS.depressed = ALLMODS; const auto IME = m_relay.m_inputMethod.lock(); @@ -1437,6 +1463,7 @@ void CInputManager::onKeyboardMod(SP pKeyboard) { } else { g_pSeatManager->setKeyboard(pKeyboard); g_pSeatManager->sendKeyboardMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group); + m_lastMods = MODS.depressed; } updateKeyboardsLeds(pKeyboard); @@ -1457,9 +1484,9 @@ bool CInputManager::shouldIgnoreVirtualKeyboard(SP pKeyboard) { if (!pKeyboard->isVirtual()) return false; - CVirtualKeyboard* vk = (CVirtualKeyboard*)pKeyboard.get(); + auto client = pKeyboard->getClient(); - return !pKeyboard || (!m_relay.m_inputMethod.expired() && m_relay.m_inputMethod->grabClient() == vk->getClient()); + return !pKeyboard || (client && !m_relay.m_inputMethod.expired() && m_relay.m_inputMethod->grabClient() == client); } void CInputManager::refocus() { @@ -1564,11 +1591,45 @@ void CInputManager::updateCapabilities() { m_capabilities = caps; } -uint32_t CInputManager::accumulateModsFromAllKBs() { +const std::vector& CInputManager::getKeysFromAllKBs() { + return m_pressed; +} - uint32_t finalMask = 0; +uint32_t CInputManager::getModsFromAllKBs() { + return m_lastMods; +} + +bool CInputManager::shareKeyFromAllKBs(uint32_t key, bool pressed) { + bool finalState = pressed; + + if (finalState) + return finalState; for (auto const& kb : m_keyboards) { + if (!kb->shareStates()) + continue; + + if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) + continue; + + if (!kb->m_enabled) + continue; + + const bool PRESSED = kb->getPressed(key); + if (PRESSED) + return PRESSED; + } + + return finalState; +} + +uint32_t CInputManager::shareModsFromAllKBs(uint32_t depressed) { + uint32_t finalMask = depressed; + + for (auto const& kb : m_keyboards) { + if (!kb->shareStates()) + continue; + if (kb->isVirtual() && shouldIgnoreVirtualKeyboard(kb)) continue; @@ -1751,13 +1812,6 @@ void CInputManager::unsetCursorImage() { restoreCursorIconToApp(); } -std::string CInputManager::deviceNameToInternalString(std::string in) { - std::ranges::replace(in, ' ', '-'); - std::ranges::replace(in, '\n', '-'); - std::ranges::transform(in, in.begin(), ::tolower); - return in; -} - std::string CInputManager::getNameForNewDevice(std::string internalName) { auto proposedNewName = deviceNameToInternalString(internalName); diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp index 1696dd0a8..73a0dc482 100644 --- a/src/managers/input/InputManager.hpp +++ b/src/managers/input/InputManager.hpp @@ -94,6 +94,7 @@ class CInputManager { void onKeyboardKey(const IKeyboard::SKeyEvent&, SP); void onKeyboardMod(SP); + void newKeyboard(SP); void newKeyboard(SP); void newVirtualKeyboard(SP); void newMouse(SP); @@ -185,7 +186,8 @@ class CInputManager { CInputMethodRelay m_relay; // for shared mods - uint32_t accumulateModsFromAllKBs(); + const std::vector& getKeysFromAllKBs(); + uint32_t getModsFromAllKBs(); // for virtual keyboards: whether we should respect them as normal ones bool shouldIgnoreVirtualKeyboard(SP); @@ -194,7 +196,6 @@ class CInputManager { void setCursorImageUntilUnset(std::string); void unsetCursorImage(); - std::string deviceNameToInternalString(std::string); std::string getNameForNewDevice(std::string); void releaseAllMouseButtons(); @@ -305,6 +306,11 @@ class CInputManager { uint32_t accumulatedScroll = 0; } m_scrollWheelState; + bool shareKeyFromAllKBs(uint32_t key, bool pressed); + uint32_t shareModsFromAllKBs(uint32_t depressed); + std::vector m_pressed; + uint32_t m_lastMods = 0; + friend class CKeybindManager; friend class CWLSurface; }; diff --git a/src/managers/permissions/DynamicPermissionManager.cpp b/src/managers/permissions/DynamicPermissionManager.cpp index 235336b7a..2f4690fc5 100644 --- a/src/managers/permissions/DynamicPermissionManager.cpp +++ b/src/managers/permissions/DynamicPermissionManager.cpp @@ -2,10 +2,9 @@ #include "DynamicPermissionManager.hpp" #include #include -#include -#include #include "../../Compositor.hpp" #include "../../config/ConfigValue.hpp" +#include "../../helpers/MiscFunctions.hpp" #include using namespace Hyprutils::String; @@ -76,48 +75,6 @@ static const char* specialPidToString(eSpecialPidTypes type) { } } -static std::expected binaryNameForPid(pid_t pid) { - if (pid <= 0) - return std::unexpected("No pid for client"); - -#if defined(KERN_PROC_PATHNAME) - int mib[] = { - CTL_KERN, -#if defined(__NetBSD__) - KERN_PROC_ARGS, - pid, - KERN_PROC_PATHNAME, -#else - KERN_PROC, - KERN_PROC_PATHNAME, - pid, -#endif - }; - u_int miblen = sizeof(mib) / sizeof(mib[0]); - char exe[PATH_MAX] = "/nonexistent"; - size_t sz = sizeof(exe); - sysctl(mib, miblen, &exe, &sz, NULL, 0); - std::string path = exe; -#else - std::string path = std::format("/proc/{}/exe", (uint64_t)pid); -#endif - std::error_code ec; - - std::string fullPath = std::filesystem::canonical(path, ec); - - if (ec) - return std::unexpected("canonical failed"); - - return fullPath; -} - -static std::expected binaryNameForWlClient(wl_client* client) { - pid_t pid = 0; - wl_client_get_credentials(client, &pid, nullptr, nullptr); - - return binaryNameForPid(pid); -} - void CDynamicPermissionManager::clearConfigPermissions() { std::erase_if(m_rules, [](const auto& e) { return e->m_source == PERMISSION_RULE_SOURCE_CONFIG; }); } diff --git a/src/protocols/VirtualKeyboard.cpp b/src/protocols/VirtualKeyboard.cpp index 7512f9bd3..3dc15d83c 100644 --- a/src/protocols/VirtualKeyboard.cpp +++ b/src/protocols/VirtualKeyboard.cpp @@ -1,23 +1,42 @@ #include "VirtualKeyboard.hpp" +#include #include +#include "../config/ConfigValue.hpp" +#include "../config/ConfigManager.hpp" #include "../devices/IKeyboard.hpp" #include "../helpers/time/Time.hpp" +#include "../helpers/MiscFunctions.hpp" using namespace Hyprutils::OS; +static std::string virtualKeyboardNameForWlClient(wl_client* client) { + std::string name = "hl-virtual-keyboard"; + + static auto PVKNAMEPROC = CConfigValue("misc:name_vk_after_proc"); + if (!*PVKNAMEPROC) + return name; + + name += "-"; + const auto CLIENTNAME = binaryNameForWlClient(client); + if (CLIENTNAME.has_value()) { + const auto PATH = std::filesystem::path(CLIENTNAME.value()); + if (PATH.has_filename()) { + const auto FILENAME = PATH.filename(); + const auto NAME = deviceNameToInternalString(FILENAME); + name += NAME; + return name; + } + } + + name += "unknown"; + return name; +} + CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP resource_) : m_resource(resource_) { if UNLIKELY (!good()) return; - m_resource->setDestroy([this](CZwpVirtualKeyboardV1* r) { - releasePressed(); - m_events.destroy.emit(); - PROTO::virtualKeyboard->destroyResource(this); - }); - m_resource->setOnDestroy([this](CZwpVirtualKeyboardV1* r) { - releasePressed(); - m_events.destroy.emit(); - PROTO::virtualKeyboard->destroyResource(this); - }); + m_resource->setDestroy([this](CZwpVirtualKeyboardV1* r) { destroy(); }); + m_resource->setOnDestroy([this](CZwpVirtualKeyboardV1* r) { destroy(); }); m_resource->setKey([this](CZwpVirtualKeyboardV1* r, uint32_t timeMs, uint32_t key, uint32_t state) { if UNLIKELY (!m_hasKeymap) { @@ -31,7 +50,7 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP .state = (wl_keyboard_key_state)state, }); - const bool CONTAINS = std::ranges::find(m_pressed, key) != m_pressed.end(); + const bool CONTAINS = std::ranges::contains(m_pressed, key); if (state && !CONTAINS) m_pressed.emplace_back(key); else if (!state && CONTAINS) @@ -88,7 +107,7 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP xkb_context_unref(xkbContext); }); - m_name = "hl-virtual-keyboard"; + m_name = virtualKeyboardNameForWlClient(resource_->client()); } CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() { @@ -115,6 +134,14 @@ void CVirtualKeyboardV1Resource::releasePressed() { m_pressed.clear(); } +void CVirtualKeyboardV1Resource::destroy() { + const auto RELEASEPRESSED = g_pConfigManager->getDeviceInt(m_name, "release_pressed_on_close", "input:virtualkeyboard:release_pressed_on_close"); + if (RELEASEPRESSED) + releasePressed(); + m_events.destroy.emit(); + PROTO::virtualKeyboard->destroyResource(this); +} + CVirtualKeyboardProtocol::CVirtualKeyboardProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { ; } diff --git a/src/protocols/VirtualKeyboard.hpp b/src/protocols/VirtualKeyboard.hpp index c601c0fb5..1ff9f161f 100644 --- a/src/protocols/VirtualKeyboard.hpp +++ b/src/protocols/VirtualKeyboard.hpp @@ -30,6 +30,7 @@ class CVirtualKeyboardV1Resource { SP m_resource; void releasePressed(); + void destroy(); bool m_hasKeymap = false; diff --git a/src/protocols/core/Seat.cpp b/src/protocols/core/Seat.cpp index 53123abe9..8aa2f36a0 100644 --- a/src/protocols/core/Seat.cpp +++ b/src/protocols/core/Seat.cpp @@ -305,8 +305,14 @@ CWLKeyboardResource::CWLKeyboardResource(SP resource_, SPm_keyboard.lock()); repeatInfo(g_pSeatManager->m_keyboard->m_repeatRate, g_pSeatManager->m_keyboard->m_repeatDelay); - if (g_pSeatManager->m_state.keyboardFocus && g_pSeatManager->m_state.keyboardFocus->client() == m_resource->client()) - sendEnter(g_pSeatManager->m_state.keyboardFocus.lock()); + if (g_pSeatManager->m_state.keyboardFocus && g_pSeatManager->m_state.keyboardFocus->client() == m_resource->client()) { + wl_array keys; + wl_array_init(&keys); + + sendEnter(g_pSeatManager->m_state.keyboardFocus.lock(), &keys); + + wl_array_release(&keys); + } } bool CWLKeyboardResource::good() { @@ -334,7 +340,9 @@ void CWLKeyboardResource::sendKeymap(SP keyboard) { m_resource->sendKeymap(format, fd.get(), size); } -void CWLKeyboardResource::sendEnter(SP surface) { +void CWLKeyboardResource::sendEnter(SP surface, wl_array* keys) { + ASSERT(keys); + if (!m_owner || m_currentSurface == surface || !surface->getResource()->resource()) return; @@ -351,12 +359,7 @@ void CWLKeyboardResource::sendEnter(SP surface) { m_currentSurface = surface; m_listeners.destroySurface = surface->m_events.destroy.listen([this] { sendLeave(); }); - wl_array arr; - wl_array_init(&arr); - - m_resource->sendEnter(g_pSeatManager->nextSerial(m_owner.lock()), surface->getResource().get(), &arr); - - wl_array_release(&arr); + m_resource->sendEnter(g_pSeatManager->nextSerial(m_owner.lock()), surface->getResource().get(), keys); } void CWLKeyboardResource::sendLeave() { diff --git a/src/protocols/core/Seat.hpp b/src/protocols/core/Seat.hpp index 0c14e52a1..8b8f6004c 100644 --- a/src/protocols/core/Seat.hpp +++ b/src/protocols/core/Seat.hpp @@ -12,6 +12,7 @@ #include #include "../WaylandProtocol.hpp" #include +#include #include "wayland.hpp" #include "../../helpers/signal/Signal.hpp" #include "../../helpers/math/Math.hpp" @@ -104,7 +105,7 @@ class CWLKeyboardResource { bool good(); void sendKeymap(SP keeb); - void sendEnter(SP surface); + void sendEnter(SP surface, wl_array* keys); void sendLeave(); void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state); void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);