From f8a847109e82b3da83336b543ddd709660a9e8e4 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 2 Sep 2023 19:36:57 +0200 Subject: [PATCH] add gs --- src/core/PortalManager.cpp | 4 + src/core/PortalManager.hpp | 4 +- src/portals/GlobalShortcuts.cpp | 265 ++++++++++++++++++++++++++++++++ src/portals/GlobalShortcuts.hpp | 46 ++++++ src/portals/Screencopy.cpp | 4 +- 5 files changed, 320 insertions(+), 3 deletions(-) create mode 100644 src/portals/GlobalShortcuts.cpp create mode 100644 src/portals/GlobalShortcuts.hpp diff --git a/src/core/PortalManager.cpp b/src/core/PortalManager.cpp index 6fcfb61..74b08ad 100644 --- a/src/core/PortalManager.cpp +++ b/src/core/PortalManager.cpp @@ -203,6 +203,10 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t if (INTERFACE == zwlr_screencopy_manager_v1_interface.name && m_sPipewire.loop) m_sPortals.screencopy = std::make_unique((zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, version)); + if (INTERFACE == hyprland_global_shortcuts_manager_v1_interface.name) + m_sPortals.globalShortcuts = std::make_unique( + (hyprland_global_shortcuts_manager_v1*)wl_registry_bind(registry, name, &hyprland_global_shortcuts_manager_v1_interface, version)); + else if (INTERFACE == hyprland_toplevel_export_manager_v1_interface.name) m_sWaylandConnection.hyprlandToplevelMgr = wl_registry_bind(registry, name, &hyprland_toplevel_export_manager_v1_interface, version); diff --git a/src/core/PortalManager.hpp b/src/core/PortalManager.hpp index e3908a8..fc3e15f 100644 --- a/src/core/PortalManager.hpp +++ b/src/core/PortalManager.hpp @@ -5,6 +5,7 @@ #include #include "../portals/Screencopy.hpp" +#include "../portals/GlobalShortcuts.hpp" #include "../helpers/Timer.hpp" #include "../shared/ToplevelManager.hpp" #include @@ -41,7 +42,8 @@ class CPortalManager { } m_sPipewire; struct { - std::unique_ptr screencopy; + std::unique_ptr screencopy; + std::unique_ptr globalShortcuts; } m_sPortals; struct { diff --git a/src/portals/GlobalShortcuts.cpp b/src/portals/GlobalShortcuts.cpp new file mode 100644 index 0000000..920e3d9 --- /dev/null +++ b/src/portals/GlobalShortcuts.cpp @@ -0,0 +1,265 @@ +#include "GlobalShortcuts.hpp" +#include "../core/PortalManager.hpp" +#include "../helpers/Log.hpp" + +// wayland + +static void handleActivated(void* data, hyprland_global_shortcut_v1* hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + const auto PKEYBIND = (SKeybind*)data; + + g_pPortalManager->m_sPortals.globalShortcuts->onActivated(PKEYBIND, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo)); +} + +static void handleDeactivated(void* data, hyprland_global_shortcut_v1* hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + const auto PKEYBIND = (SKeybind*)data; + + g_pPortalManager->m_sPortals.globalShortcuts->onDeactivated(PKEYBIND, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo)); +} + +static const hyprland_global_shortcut_v1_listener shortcutListener = { + .pressed = handleActivated, + .released = handleDeactivated, +}; + +// + +static void onCloseRequest(sdbus::MethodCall& call, CGlobalShortcutsPortal::SSession* sess) { + Debug::log(TRACE, "[globalshortcuts] Close Request {}", (void*)sess); + + if (!sess || !sess->request) + return; + + auto r = call.createReply(); + r.send(); + + sess->request.release(); +} + +static void onCloseSession(sdbus::MethodCall& call, CGlobalShortcutsPortal::SSession* sess) { + Debug::log(TRACE, "[globalshortcuts] Close Session {}", (void*)sess); + + if (!sess || !sess->session) + return; + + auto r = call.createReply(); + r.send(); + + sess->session.release(); +} + +CGlobalShortcutsPortal::SSession* CGlobalShortcutsPortal::getSession(sdbus::ObjectPath& path) { + for (auto& s : m_vSessions) { + if (s->sessionHandle == path) + return s.get(); + } + + return nullptr; +} + +void CGlobalShortcutsPortal::onCreateSession(sdbus::MethodCall& call) { + sdbus::ObjectPath requestHandle, sessionHandle; + + call >> requestHandle; + call >> sessionHandle; + + std::string appID; + call >> appID; + + Debug::log(LOG, "[globalshortcuts] New session:"); + Debug::log(LOG, "[globalshortcuts] | {}", requestHandle.c_str()); + Debug::log(LOG, "[globalshortcuts] | {}", sessionHandle.c_str()); + Debug::log(LOG, "[globalshortcuts] | appid: {}", appID); + + const auto PSESSION = m_vSessions.emplace_back(std::make_unique(appID, requestHandle, sessionHandle)).get(); + + // create objects + PSESSION->request = sdbus::createObject(*g_pPortalManager->getConnection(), requestHandle); + PSESSION->session = sdbus::createObject(*g_pPortalManager->getConnection(), sessionHandle); + + PSESSION->request->registerMethod("org.freedesktop.impl.portal.Request", "Close", "", "", [PSESSION](sdbus::MethodCall c) { onCloseRequest(c, PSESSION); }); + PSESSION->session->registerMethod("org.freedesktop.impl.portal.Session", "Close", "", "", [PSESSION](sdbus::MethodCall c) { onCloseSession(c, PSESSION); }); + + PSESSION->request->finishRegistration(); + PSESSION->session->finishRegistration(); + + std::unordered_map opts; + call >> opts; + + for (auto& [k, v] : opts) { + if (k == "shortcuts") { + PSESSION->registered = true; + + std::vector>> shortcuts; + + 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; + } + + Debug::log(LOG, "[globalshortcuts] registered {} shortcuts", shortcuts.size()); + } + } + + auto reply = call.createReply(); + reply << (uint32_t)0; + reply << std::unordered_map{}; + reply.send(); +} + +void CGlobalShortcutsPortal::onBindShortcuts(sdbus::MethodCall& call) { + sdbus::ObjectPath sessionHandle, requestHandle; + call >> requestHandle; + call >> sessionHandle; + + Debug::log(LOG, "[globalshortcuts] Bind keys:"); + Debug::log(LOG, "[globalshortcuts] | {}", sessionHandle.c_str()); + + std::vector>> shortcuts; + std::vector>> shortcutsToReturn; + call >> shortcuts; + + const auto PSESSION = getSession(sessionHandle); + + if (!PSESSION) { + Debug::log(ERR, "[globalshortcuts] No session?"); + return; + } + + 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; + + std::unordered_map shortcutData; + shortcutData["description"] = PSHORTCUT->description; + shortcutData["trigger_description"] = ""; + shortcutsToReturn.push_back({PSHORTCUT->id, shortcutData}); + } + + Debug::log(LOG, "[globalshortcuts] registered {} shortcuts", shortcuts.size()); + + auto reply = call.createReply(); + + std::unordered_map data; + data["shortcuts"] = shortcutsToReturn; + + reply << (uint32_t)0; + reply << data; + reply.send(); +} + +void CGlobalShortcutsPortal::onListShortcuts(sdbus::MethodCall& call) { + sdbus::ObjectPath sessionHandle, requestHandle; + call >> requestHandle; + call >> sessionHandle; + + Debug::log(LOG, "[globalshortcuts] List keys:"); + Debug::log(LOG, "[globalshortcuts] | {}", sessionHandle.c_str()); + + const auto PSESSION = getSession(sessionHandle); + + if (!PSESSION) { + Debug::log(ERR, "[globalshortcuts] No session?"); + return; + } + + std::vector>> shortcuts; + + for (auto& s : PSESSION->keybinds) { + std::unordered_map opts; + opts["description"] = s->description; + opts["trigger_description"] = ""; + shortcuts.push_back({s->id, opts}); + } + + auto reply = call.createReply(); + + std::unordered_map data; + data["shortcuts"] = shortcuts; + + reply << (uint32_t)0; + reply << data; + reply.send(); +} + +CGlobalShortcutsPortal::CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr) { + m_sState.manager = mgr; + + m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH); + + m_pObject->registerMethod(INTERFACE_NAME, "CreateSession", "oosa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onCreateSession(c); }); + m_pObject->registerMethod(INTERFACE_NAME, "BindShortcuts", "ooa(sa{sv})sa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onBindShortcuts(c); }); + m_pObject->registerMethod(INTERFACE_NAME, "ListShortcuts", "oo", "ua{sv}", [&](sdbus::MethodCall c) { onListShortcuts(c); }); + m_pObject->registerSignal(INTERFACE_NAME, "Activated", "osta{sv}"); + m_pObject->registerSignal(INTERFACE_NAME, "Deactivated", "osta{sv}"); + m_pObject->registerSignal(INTERFACE_NAME, "ShortcutsChanged", "oa(sa{sv})"); + + m_pObject->finishRegistration(); + + Debug::log(LOG, "[globalshortcuts] registered"); +} + +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); + + auto signal = m_pObject->createSignal(INTERFACE_NAME, "Activated"); + signal << PSESSION->sessionHandle; + signal << pKeybind->id; + signal << time; + signal << std::unordered_map{}; + + m_pObject->emitSignal(signal); +} + +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); + + auto signal = m_pObject->createSignal(INTERFACE_NAME, "Deactivated"); + signal << PSESSION->sessionHandle; + signal << pKeybind->id; + signal << time; + signal << std::unordered_map{}; + + m_pObject->emitSignal(signal); +} \ No newline at end of file diff --git a/src/portals/GlobalShortcuts.hpp b/src/portals/GlobalShortcuts.hpp new file mode 100644 index 0000000..5019cc5 --- /dev/null +++ b/src/portals/GlobalShortcuts.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +struct SKeybind { + std::string id, description, preferredTrigger; + hyprland_global_shortcut_v1* shortcut = nullptr; + void* session = nullptr; +}; + +class CGlobalShortcutsPortal { + public: + CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr); + + void onCreateSession(sdbus::MethodCall& call); + void onBindShortcuts(sdbus::MethodCall& call); + void onListShortcuts(sdbus::MethodCall& call); + + void onActivated(SKeybind* pKeybind, uint64_t time); + void onDeactivated(SKeybind* pKeybind, uint64_t time); + + struct SSession { + std::string appid; + sdbus::ObjectPath requestHandle, sessionHandle; + std::unique_ptr request, session; + + bool registered = false; + + std::vector> keybinds; + }; + + std::vector> m_vSessions; + + private: + struct { + hyprland_global_shortcuts_manager_v1* manager; + } m_sState; + + std::unique_ptr m_pObject; + + SSession* getSession(sdbus::ObjectPath& path); + + const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.GlobalShortcuts"; + const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop"; +}; \ No newline at end of file diff --git a/src/portals/Screencopy.cpp b/src/portals/Screencopy.cpp index 6870093..84f67f1 100644 --- a/src/portals/Screencopy.cpp +++ b/src/portals/Screencopy.cpp @@ -280,7 +280,7 @@ static const hyprland_toplevel_export_frame_v1_listener hyprlandFrameListener = // --------------------------------------------------------- // -void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { +static void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { Debug::log(TRACE, "[screencopy] Close Request {}", (void*)sess); if (!sess || !sess->request) @@ -292,7 +292,7 @@ void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) sess->request.release(); } -void onCloseSession(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { +static void onCloseSession(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { Debug::log(TRACE, "[screencopy] Close Session {}", (void*)sess); if (!sess || !sess->session)