From 3b8c78145a0055a772a38aabdb09009ce3c42cec Mon Sep 17 00:00:00 2001 From: dec05eba <66856951+dec05eba@users.noreply.github.com> Date: Tue, 23 Jul 2024 14:01:00 +0200 Subject: [PATCH] global-shortcuts: fix crash by ignoring duplicate shortcuts (#241) * global shortcuts: fix crash by ignoring duplicate shortcuts hyprland_global_shortcuts_manager_v1_register_shortcut will cause an error if the (app_id, shortcut_id) combination has already been registered. Ignore shortcuts that have already been registered. When a shortcut is registered again it should also overwrite the session for the keybind to make the shortcut work after restarting an application, otherwise the key can't be used again. * Show warning if the shortcut has already been registered * Remove {} around short ifs --- src/portals/GlobalShortcuts.cpp | 97 +++++++++++++++++---------------- src/portals/GlobalShortcuts.hpp | 4 ++ 2 files changed, 55 insertions(+), 46 deletions(-) diff --git a/src/portals/GlobalShortcuts.cpp b/src/portals/GlobalShortcuts.cpp index 37712aa..903cd0f 100644 --- a/src/portals/GlobalShortcuts.cpp +++ b/src/portals/GlobalShortcuts.cpp @@ -32,6 +32,49 @@ CGlobalShortcutsPortal::SSession* CGlobalShortcutsPortal::getSession(sdbus::Obje return nullptr; } +SKeybind* CGlobalShortcutsPortal::getShortcutById(const std::string& appID, const std::string& shortcutId) { + for (auto& s : m_vSessions) { + if (s->appid != appID) + continue; + + for (auto& keybind : s->keybinds) { + if (keybind->id == shortcutId) + return keybind.get(); + } + } + + return nullptr; +} + +SKeybind* CGlobalShortcutsPortal::registerShortcut(SSession* session, const DBusShortcut& shortcut) { + std::string id = shortcut.get<0>(); + std::unordered_map data = shortcut.get<1>(); + std::string description; + + for (auto& [k, v] : data) { + if (k == "description") + description = v.get(); + else + Debug::log(LOG, "[globalshortcuts] unknown shortcut data type {}", k); + } + + auto* PSHORTCUT = getShortcutById(session->appid, id); + if (PSHORTCUT) + Debug::log(WARN, "[globalshortcuts] shortcut {} already registered for appid {}", id, session->appid); + else { + PSHORTCUT = session->keybinds.emplace_back(std::make_unique()).get(); + PSHORTCUT->shortcut = + hyprland_global_shortcuts_manager_v1_register_shortcut(m_sState.manager, id.c_str(), session->appid.c_str(), description.c_str(), ""); + hyprland_global_shortcut_v1_add_listener(PSHORTCUT->shortcut, &shortcutListener, PSHORTCUT); + } + + PSHORTCUT->id = std::move(id); + PSHORTCUT->description = std::move(description); + PSHORTCUT->session = session; + + return PSHORTCUT; +} + void CGlobalShortcutsPortal::onCreateSession(sdbus::MethodCall& call) { sdbus::ObjectPath requestHandle, sessionHandle; @@ -61,30 +104,10 @@ void CGlobalShortcutsPortal::onCreateSession(sdbus::MethodCall& call) { if (k == "shortcuts") { PSESSION->registered = true; - std::vector>> shortcuts; - - shortcuts = v.get>>>(); + std::vector shortcuts = v.get>(); for (auto& s : shortcuts) { - const auto PSHORTCUT = PSESSION->keybinds.emplace_back(std::make_unique()).get(); - - PSHORTCUT->id = s.get<0>(); - - std::unordered_map data = s.get<1>(); - - for (auto& [k, v] : data) { - if (k == "description") { - PSHORTCUT->description = v.get(); - } else { - Debug::log(LOG, "[globalshortcuts] unknown shortcut data type {}", k); - } - } - - PSHORTCUT->shortcut = - hyprland_global_shortcuts_manager_v1_register_shortcut(m_sState.manager, PSHORTCUT->id.c_str(), PSESSION->appid.c_str(), PSHORTCUT->description.c_str(), ""); - hyprland_global_shortcut_v1_add_listener(PSHORTCUT->shortcut, &shortcutListener, PSHORTCUT); - - PSHORTCUT->session = PSESSION; + registerShortcut(PSESSION, s); } Debug::log(LOG, "[globalshortcuts] registered {} shortcuts", shortcuts.size()); @@ -105,8 +128,8 @@ void CGlobalShortcutsPortal::onBindShortcuts(sdbus::MethodCall& call) { Debug::log(LOG, "[globalshortcuts] Bind keys:"); Debug::log(LOG, "[globalshortcuts] | {}", sessionHandle.c_str()); - std::vector>> shortcuts; - std::vector>> shortcutsToReturn; + std::vector shortcuts; + std::vector shortcutsToReturn; call >> shortcuts; const auto PSESSION = getSession(sessionHandle); @@ -119,25 +142,7 @@ void CGlobalShortcutsPortal::onBindShortcuts(sdbus::MethodCall& call) { PSESSION->registered = true; for (auto& s : shortcuts) { - const auto PSHORTCUT = PSESSION->keybinds.emplace_back(std::make_unique()).get(); - - PSHORTCUT->id = s.get<0>(); - - std::unordered_map data = s.get<1>(); - - for (auto& [k, v] : data) { - if (k == "description") { - PSHORTCUT->description = v.get(); - } else { - Debug::log(LOG, "[globalshortcuts] unknown shortcut data type {}", k); - } - } - - PSHORTCUT->shortcut = - hyprland_global_shortcuts_manager_v1_register_shortcut(m_sState.manager, PSHORTCUT->id.c_str(), PSESSION->appid.c_str(), PSHORTCUT->description.c_str(), ""); - hyprland_global_shortcut_v1_add_listener(PSHORTCUT->shortcut, &shortcutListener, PSHORTCUT); - - PSHORTCUT->session = PSESSION; + const auto* PSHORTCUT = registerShortcut(PSESSION, s); std::unordered_map shortcutData; shortcutData["description"] = PSHORTCUT->description; @@ -172,7 +177,7 @@ void CGlobalShortcutsPortal::onListShortcuts(sdbus::MethodCall& call) { return; } - std::vector>> shortcuts; + std::vector shortcuts; for (auto& s : PSESSION->keybinds) { std::unordered_map opts; @@ -211,7 +216,7 @@ CGlobalShortcutsPortal::CGlobalShortcutsPortal(hyprland_global_shortcuts_manager void CGlobalShortcutsPortal::onActivated(SKeybind* pKeybind, uint64_t time) { const auto PSESSION = (CGlobalShortcutsPortal::SSession*)pKeybind->session; - Debug::log(TRACE, "[gs] Session {} called activated on {}", (void*)PSESSION, pKeybind->id); + Debug::log(TRACE, "[gs] Session {} called activated on {}", PSESSION->sessionHandle.c_str(), pKeybind->id); auto signal = m_pObject->createSignal(INTERFACE_NAME, "Activated"); signal << PSESSION->sessionHandle; @@ -225,7 +230,7 @@ void CGlobalShortcutsPortal::onActivated(SKeybind* pKeybind, uint64_t time) { void CGlobalShortcutsPortal::onDeactivated(SKeybind* pKeybind, uint64_t time) { const auto PSESSION = (CGlobalShortcutsPortal::SSession*)pKeybind->session; - Debug::log(TRACE, "[gs] Session {} called deactivated on {}", (void*)PSESSION, pKeybind->id); + Debug::log(TRACE, "[gs] Session {} called deactivated on {}", PSESSION->sessionHandle.c_str(), pKeybind->id); auto signal = m_pObject->createSignal(INTERFACE_NAME, "Deactivated"); signal << PSESSION->sessionHandle; diff --git a/src/portals/GlobalShortcuts.hpp b/src/portals/GlobalShortcuts.hpp index 0d4d987..baf7a09 100644 --- a/src/portals/GlobalShortcuts.hpp +++ b/src/portals/GlobalShortcuts.hpp @@ -41,7 +41,11 @@ class CGlobalShortcutsPortal { std::unique_ptr m_pObject; + using DBusShortcut = sdbus::Struct>; + SSession* getSession(sdbus::ObjectPath& path); + SKeybind* getShortcutById(const std::string& appID, const std::string& shortcutId); + SKeybind* registerShortcut(SSession* session, const DBusShortcut& shortcut); const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.GlobalShortcuts"; const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop";