From ebb343a884daacb39fa3dfeac0b9bea64db8171a Mon Sep 17 00:00:00 2001 From: toneengo Date: Sat, 21 Dec 2024 12:12:27 +1030 Subject: [PATCH] remotedesktop: implement remotedesktop portal --- CMakeLists.txt | 5 +- hyprland.portal | 2 +- protocols/virtual-keyboard-unstable-v1.xml | 113 ++++++++++ protocols/wlr-virtual-pointer-unstable-v1.xml | 152 +++++++++++++ src/core/PortalManager.cpp | 34 ++- src/core/PortalManager.hpp | 16 +- src/portals/InputCapture.cpp | 7 +- src/portals/RemoteDesktop.cpp | 207 ++++++++++++++++++ src/portals/RemoteDesktop.hpp | 64 ++++++ src/shared/Eis.cpp | 134 +++++++++++- src/shared/Eis.hpp | 20 +- 11 files changed, 736 insertions(+), 18 deletions(-) create mode 100644 protocols/virtual-keyboard-unstable-v1.xml create mode 100644 protocols/wlr-virtual-pointer-unstable-v1.xml create mode 100644 src/portals/RemoteDesktop.cpp create mode 100644 src/portals/RemoteDesktop.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6486c7e..bb2aaec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,11 +84,12 @@ else() set(HYPRLAND_PROTOCOLS "${CMAKE_SOURCE_DIR}/subprojects/hyprland-protocols") endif() +pkg_search_module(UUID REQUIRED uuid) file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") add_executable(xdg-desktop-portal-hyprland ${SRCFILES}) target_link_libraries( xdg-desktop-portal-hyprland PRIVATE rt PkgConfig::SDBUS Threads::Threads - PkgConfig::deps) + PkgConfig::deps ${UUID_LIBRARIES}) # protocols pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) @@ -127,6 +128,8 @@ protocolwayland() protocolnew("${CMAKE_SOURCE_DIR}/protocols" "wlr-foreign-toplevel-management-unstable-v1" true) protocolnew("${CMAKE_SOURCE_DIR}/protocols" "wlr-screencopy-unstable-v1" true) +protocolnew("${CMAKE_SOURCE_DIR}/protocols" "wlr-virtual-pointer-unstable-v1" true) +protocolnew("${CMAKE_SOURCE_DIR}/protocols" "virtual-keyboard-unstable-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-global-shortcuts-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1" diff --git a/hyprland.portal b/hyprland.portal index cbaafa8..2e7ad8d 100644 --- a/hyprland.portal +++ b/hyprland.portal @@ -1,4 +1,4 @@ [portal] DBusName=org.freedesktop.impl.portal.desktop.hyprland -Interfaces=org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.GlobalShortcuts;org.freedesktop.impl.portal.InputCapture; +Interfaces=org.freedesktop.impl.portal.Screenshot;org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.GlobalShortcuts;org.freedesktop.impl.portal.InputCapture;org.freedesktop.impl.portal.RemoteDesktop; UseIn=wlroots;Hyprland;sway;Wayfire;river; diff --git a/protocols/virtual-keyboard-unstable-v1.xml b/protocols/virtual-keyboard-unstable-v1.xml new file mode 100644 index 0000000..5095c91 --- /dev/null +++ b/protocols/virtual-keyboard-unstable-v1.xml @@ -0,0 +1,113 @@ + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2013 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + Copyright © 2018 Purism SPC + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + The virtual keyboard provides an application with requests which emulate + the behaviour of a physical keyboard. + + This interface can be used by clients on its own to provide raw input + events, or it can accompany the input method protocol. + + + + + Provide a file descriptor to the compositor which can be + memory-mapped to provide a keyboard mapping description. + + Format carries a value from the keymap_format enumeration. + + + + + + + + + + + + + A key was pressed or released. + The time argument is a timestamp with millisecond granularity, with an + undefined base. All requests regarding a single object must share the + same clock. + + Keymap must be set before issuing this request. + + State carries a value from the key_state enumeration. + + + + + + + + + Notifies the compositor that the modifier and/or group state has + changed, and it should update state. + + The client should use wl_keyboard.modifiers event to synchronize its + internal state with seat state. + + Keymap must be set before issuing this request. + + + + + + + + + + + + + + + A virtual keyboard manager allows an application to provide keyboard + input events as if they came from a physical keyboard. + + + + + + + + + Creates a new virtual keyboard associated to a seat. + + If the compositor enables a keyboard to perform arbitrary actions, it + should present an error when an untrusted client requests a new + keyboard. + + + + + + diff --git a/protocols/wlr-virtual-pointer-unstable-v1.xml b/protocols/wlr-virtual-pointer-unstable-v1.xml new file mode 100644 index 0000000..ea243e7 --- /dev/null +++ b/protocols/wlr-virtual-pointer-unstable-v1.xml @@ -0,0 +1,152 @@ + + + + Copyright © 2019 Josef Gajdusek + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This protocol allows clients to emulate a physical pointer device. The + requests are mostly mirror opposites of those specified in wl_pointer. + + + + + + + + + + The pointer has moved by a relative amount to the previous request. + + Values are in the global compositor space. + + + + + + + + + The pointer has moved in an absolute coordinate frame. + + Value of x can range from 0 to x_extent, value of y can range from 0 + to y_extent. + + + + + + + + + + + A button was pressed or released. + + + + + + + + + Scroll and other axis requests. + + + + + + + + + Indicates the set of events that logically belong together. + + + + + + Source information for scroll and other axis. + + + + + + + Stop notification for scroll and other axes. + + + + + + + + Discrete step information for scroll and other axes. + + This event allows the client to extend data normally sent using the axis + event with discrete value. + + + + + + + + + + + + + + + This object allows clients to create individual virtual pointer objects. + + + + + Creates a new virtual pointer. The optional seat is a suggestion to the + compositor. + + + + + + + + + + + + + Creates a new virtual pointer. The seat and the output arguments are + optional. If the seat argument is set, the compositor should assign the + input device to the requested seat. If the output argument is set, the + compositor should map the input device to the requested output. + + + + + + + diff --git a/src/core/PortalManager.cpp b/src/core/PortalManager.cpp index 8e0d266..2aff24f 100644 --- a/src/core/PortalManager.cpp +++ b/src/core/PortalManager.cpp @@ -31,7 +31,7 @@ SOutput::SOutput(SP output_) : output(output_) { y = y_; }); output->setScale([this](CCWlOutput* r, uint32_t factor_) { scale = factor_; }); - output->setDone([](CCWlOutput* r) { g_pPortalManager->m_sPortals.inputCapture->zonesChanged(); }); + output->setDone([](CCWlOutput* r) {}); } CPortalManager::CPortalManager() { @@ -60,6 +60,7 @@ void CPortalManager::onGlobal(uint32_t name, const char* interface, uint32_t ver Debug::log(LOG, " | Got interface: {} (ver {})", INTERFACE, version); + if (INTERFACE == zwlr_screencopy_manager_v1_interface.name && m_sPipewire.loop) { m_sPortals.screencopy = std::make_unique(makeShared( (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwlr_screencopy_manager_v1_interface, version))); @@ -69,9 +70,23 @@ void CPortalManager::onGlobal(uint32_t name, const char* interface, uint32_t ver m_sPortals.globalShortcuts = std::make_unique(makeShared( (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_global_shortcuts_manager_v1_interface, version))); } + + if (m_sPortals.remoteDesktop == nullptr) + m_sPortals.remoteDesktop = std::make_unique(); + if (INTERFACE == hyprland_input_capture_manager_v1_interface.name) m_sPortals.inputCapture = std::make_unique(makeShared( (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_input_capture_manager_v1_interface, version))); + + if (INTERFACE == zwlr_virtual_pointer_manager_v1_interface.name) + m_sPortals.remoteDesktop->registerPointer(makeShared( + (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwlr_virtual_pointer_manager_v1_interface, version))); + + if (INTERFACE == zwp_virtual_keyboard_manager_v1_interface.name) + m_sPortals.remoteDesktop->registerKeyboard(makeShared( + (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwp_virtual_keyboard_manager_v1_interface, version))); + + else if (INTERFACE == hyprland_toplevel_export_manager_v1_interface.name) { m_sWaylandConnection.hyprlandToplevelMgr = makeShared( (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_toplevel_export_manager_v1_interface, version)); @@ -85,6 +100,20 @@ void CPortalManager::onGlobal(uint32_t name, const char* interface, uint32_t ver POUTPUT->id = name; } + else if (INTERFACE == wl_seat_interface.name) { + m_sWaylandConnection.seat = makeShared((wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &wl_seat_interface, version)); + } + + /* + else if (INTERFACE == wl_keyboard_interface.name) { + const auto PKEYBOARD = m_vKeyboards + .emplace_back(std::make_unique(makeShared( + (wl_proxy*)wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &wl_keyboard_interface, version)))) + .get(); + PKEYBOARD->id = name; + } + */ + else if (INTERFACE == zwp_linux_dmabuf_v1_interface.name) { if (version < 4) { Debug::log(ERR, "cannot use linux_dmabuf with ver < 4"); @@ -419,8 +448,9 @@ void CPortalManager::startEventLoop() { m_sPortals.globalShortcuts.reset(); m_sPortals.screencopy.reset(); m_sPortals.screenshot.reset(); - m_sHelpers.toplevel.reset(); m_sPortals.inputCapture.reset(); + m_sPortals.remoteDesktop.reset(); + m_sHelpers.toplevel.reset(); m_pConnection.reset(); pw_loop_destroy(m_sPipewire.loop); diff --git a/src/core/PortalManager.hpp b/src/core/PortalManager.hpp index c10e121..c5dd87f 100644 --- a/src/core/PortalManager.hpp +++ b/src/core/PortalManager.hpp @@ -9,6 +9,7 @@ #include "../portals/Screenshot.hpp" #include "../portals/GlobalShortcuts.hpp" #include "../portals/InputCapture.hpp" +#include "../portals/RemoteDesktop.hpp" #include "../helpers/Timer.hpp" #include "../shared/ToplevelManager.hpp" #include @@ -20,6 +21,9 @@ #include "linux-dmabuf-v1.hpp" #include "wlr-foreign-toplevel-management-unstable-v1.hpp" #include "wlr-screencopy-unstable-v1.hpp" +#include "hyprland-input-capture-v1.hpp" +#include "wlr-virtual-pointer-unstable-v1.hpp" +#include "virtual-keyboard-unstable-v1.hpp" #include "../includes.hpp" #include "../dbusDefines.hpp" @@ -60,6 +64,12 @@ class CPortalManager { SOutput* getOutputFromName(const std::string& name); std::vector> const& getAllOutputs(); + struct { + enum wl_keyboard_keymap_format format; + int32_t fd; + uint32_t size; + } m_sKeymap; + struct { pw_loop* loop = nullptr; } m_sPipewire; @@ -69,6 +79,7 @@ class CPortalManager { std::unique_ptr screenshot; std::unique_ptr globalShortcuts; std::unique_ptr inputCapture; + std::unique_ptr remoteDesktop; } m_sPortals; struct { @@ -77,6 +88,7 @@ class CPortalManager { struct { wl_display* display = nullptr; + SP seat; SP registry; SP hyprlandToplevelMgr; SP linuxDmabuf; @@ -130,8 +142,8 @@ class CPortalManager { std::unique_ptr thread; } m_sTimersThread; - std::unique_ptr m_pConnection; - std::vector> m_vOutputs; + std::unique_ptr m_pConnection; + std::vector> m_vOutputs; std::mutex m_mEventLock; }; diff --git a/src/portals/InputCapture.cpp b/src/portals/InputCapture.cpp index 4372a36..1aa35ec 100644 --- a/src/portals/InputCapture.cpp +++ b/src/portals/InputCapture.cpp @@ -25,7 +25,11 @@ CInputCapturePortal::CInputCapturePortal(SP mgr }); mgr->setKeymap([this](CCHyprlandInputCaptureManagerV1* r, hyprlandInputCaptureManagerV1KeymapFormat format, int32_t fd, uint32_t size) { + Debug::log(LOG, "[input-capture] got keymap"); onKeymap(format == HYPRLAND_INPUT_CAPTURE_MANAGER_V1_KEYMAP_FORMAT_XKB_V1 ? fd : 0, size); + g_pPortalManager->m_sKeymap.format = wl_keyboard_keymap_format(format); + g_pPortalManager->m_sKeymap.fd = fd; + g_pPortalManager->m_sKeymap.size = size; }); mgr->setModifiers([this](CCHyprlandInputCaptureManagerV1* r, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { @@ -128,7 +132,8 @@ dbUasv CInputCapturePortal::onCreateSession(sdbus::ObjectPath requestHandle, sdb session->request = createDBusRequest(requestHandle); session->request->onDestroy = [session]() { session->request.release(); }; - session->eis = std::make_unique("eis-" + sessionId, keymap); + session->eis = std::make_unique("eis-" + sessionId); + session->eis->setKeymap(keymap); sessions.emplace(sessionHandle, session); diff --git a/src/portals/RemoteDesktop.cpp b/src/portals/RemoteDesktop.cpp new file mode 100644 index 0000000..551550f --- /dev/null +++ b/src/portals/RemoteDesktop.cpp @@ -0,0 +1,207 @@ +#include "RemoteDesktop.hpp" +#include "../core/PortalManager.hpp" +#include "../helpers/Log.hpp" +#include "../helpers/MiscFunctions.hpp" + +void CRemoteDesktopPortal::registerPointer(SP mgr) { + m_sState.pointerMgr = mgr; +} + +void CRemoteDesktopPortal::registerKeyboard(SP mgr) { + m_sState.keyboardMgr = mgr; +} + +CRemoteDesktopPortal::CRemoteDesktopPortal() { + Debug::log(LOG, "[remotedesktop] initializing remote desktop portal"); + m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH); + m_pObject + ->addVTable( + sdbus::registerMethod("CreateSession") + .implementedAs([this](sdbus::ObjectPath o1, sdbus::ObjectPath o2, std::string s, std::unordered_map m) { + return onCreateSession(o1, o2, s, m); + }), + sdbus::registerMethod("SelectDevices") + .implementedAs([this](sdbus::ObjectPath o1, sdbus::ObjectPath o2, std::string s, std::unordered_map m) { + return onSelectDevices(o1, o2, s, m); + }), + sdbus::registerMethod("Start") + .implementedAs([this](sdbus::ObjectPath o1, sdbus::ObjectPath o2, std::string s1, std::string s2, std::unordered_map m) { + return onStart(o1, o2, s1, s2, m); + }), + sdbus::registerMethod("NotifyPointerMotion") + .implementedAs([this](sdbus::ObjectPath o, double d1, double d2, std::unordered_map m) { + return onNotifyPointerMotion(o, d1, d2, m); + }), + sdbus::registerMethod("NotifyPointerMotionAbsolute") + .implementedAs([this](sdbus::ObjectPath o, unsigned int u, double d1, double d2, std::unordered_map m) { + return onNotifyPointerMotionAbsolute(o, u, d1, d2, m); + }), + sdbus::registerMethod("NotifyPointerButton") + .implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map m) { + return onNotifyPointerButton(o, i, u, m); + }), + sdbus::registerMethod("NotifyPointerAxis") + .implementedAs([this](sdbus::ObjectPath o, double d1, double d2, std::unordered_map m) { + return onNotifyPointerAxis(o, d1, d2, m); + }), + sdbus::registerMethod("NotifyPointerAxisDiscrete") + .implementedAs([this](sdbus::ObjectPath o, unsigned int u, int i, std::unordered_map m) { + return onNotifyPointerAxisDiscrete(o, u, i, m); + }), + sdbus::registerMethod("NotifyKeyboardKeycode") + .implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map m) { + return onNotifyKeyboardKeycode(o, i, u, m); + }), + sdbus::registerMethod("NotifyKeyboardKeysym") + .implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map m) { + return onNotifyKeyboardKeysym(o, i, u, m); + }), + sdbus::registerMethod("NotifyTouchDown") + .implementedAs([this](sdbus::ObjectPath o, unsigned int u1, unsigned int u2, double d1, double d2, std::unordered_map m) { + return onNotifyTouchDown(o, u1, u2, d1, d2, m); + }), + sdbus::registerMethod("NotifyTouchMotion") + .implementedAs([this](sdbus::ObjectPath o, unsigned int u1, unsigned int u2, double d1, double d2, std::unordered_map m) { + return onNotifyTouchMotion(o, u1, u2, d1, d2, m); + }), + sdbus::registerMethod("NotifyTouchUp") + .implementedAs([this](sdbus::ObjectPath o, unsigned int u, std::unordered_map m) { + return onNotifyTouchUp(o, u, m); + }), + sdbus::registerMethod("ConnectToEIS").implementedAs([this](sdbus::ObjectPath o, std::string s, std::unordered_map m) { + return onConnectToEIS(o, s, m); + }), + sdbus::registerProperty("AvailableDeviceTypes").withGetter([] { return (uint32_t)(1 | 2); }), + sdbus::registerProperty("version").withGetter([] { return (uint32_t)(1); })) + .forInterface(INTERFACE_NAME); + +} + +dbUasv CRemoteDesktopPortal::onCreateSession(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, + std::unordered_map opts) { + Debug::log(LOG, "[remotedesktop] New session:"); + Debug::log(LOG, "[remotedesktop] | {}", requestHandle.c_str()); + Debug::log(LOG, "[remotedesktop] | {}", sessionHandle.c_str()); + Debug::log(LOG, "[remotedesktop] | appid: {}", appID); + + std::shared_ptr PSESSION = std::make_shared(appID, requestHandle, sessionHandle); + + // create objects + PSESSION->session = createDBusSession(sessionHandle); + PSESSION->sessionHandle = sessionHandle; + PSESSION->session->onDestroy = [PSESSION, this]() { + PSESSION->eis->stopServer(); + PSESSION->eis.reset(); + Debug::log(LOG, "[remotedesktop] Session {} destroyed", PSESSION->sessionHandle.c_str()); + PSESSION->session.release(); + m_mSessions.erase(PSESSION->sessionHandle); + }; + + PSESSION->request = createDBusRequest(requestHandle); + PSESSION->requestHandle = requestHandle; + PSESSION->request->onDestroy = [PSESSION]() { PSESSION->request.release(); }; + + PSESSION->pointer = makeShared(m_sState.pointerMgr->sendCreateVirtualPointer(g_pPortalManager->m_sWaylandConnection.seat->resource())); + PSESSION->keyboard = makeShared(m_sState.keyboardMgr->sendCreateVirtualKeyboard(g_pPortalManager->m_sWaylandConnection.seat->resource())); + + const auto& keymap = g_pPortalManager->m_sKeymap; + PSESSION->keyboard->sendKeymap(keymap.format, keymap.fd, keymap.size); + + PSESSION->eis = std::make_unique("eisr-" + std::to_string(m_uSessionCounter++)); + PSESSION->eis->setVirtualPointer(PSESSION->pointer); + PSESSION->eis->setVirtualKeyboard(PSESSION->keyboard); + + m_mSessions.emplace(sessionHandle, PSESSION); + + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onSelectDevices(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, + std::unordered_map opts) { + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onStart(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, std::string parentWindow, + std::unordered_map opts) { + Debug::log(LOG, "[remotedesktop] start request"); + + std::unordered_map results; + results["devices"] = sdbus::Variant{uint32_t{1 | 2}}; + results["clipboard_enabled"] = sdbus::Variant{bool(true)}; + + std::unordered_map restoreData; + results["restore_data"] = sdbus::Variant{sdbus::Struct{"hyprland", 1, sdbus::Variant{restoreData}}}; + results["persist_mode"] = sdbus::Variant{uint32_t{2}}; + + return {0, results}; +} + +dbUasv CRemoteDesktopPortal::onNotifyPointerMotion(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->pointer->sendMotion(0, dx, dy); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyPointerMotionAbsolute(sdbus::ObjectPath sessionHandle, unsigned int stream, double x, double y, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->pointer->sendMotionAbsolute(0, x, y, 1920, 1080); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyPointerButton(sdbus::ObjectPath sessionHandle, int button, unsigned int state, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->pointer->sendButton(0, button, state); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyPointerAxis(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->pointer->sendAxis(0, 0, dy); + PSESSION->pointer->sendAxis(0, 1, dx); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyPointerAxisDiscrete(sdbus::ObjectPath sessionHandle, unsigned int axis, int steps, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->pointer->sendAxisDiscrete(1, 0, axis, steps); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyKeyboardKeycode(sdbus::ObjectPath sessionHandle, int keycode, unsigned int state, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + PSESSION->keyboard->sendKey(1, keycode, state); + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyKeyboardKeysym(sdbus::ObjectPath sessionHandle, int keysym, unsigned int state, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyTouchDown(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y, + std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyTouchMotion(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y, + std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + return {0, {}}; +} + +dbUasv CRemoteDesktopPortal::onNotifyTouchUp(sdbus::ObjectPath sessionHandle, unsigned int slot, std::unordered_map opts) { + const auto PSESSION = m_mSessions[sessionHandle]; + return {0, {}}; +} + +sdbus::UnixFd CRemoteDesktopPortal::onConnectToEIS(sdbus::ObjectPath sessionHandle, std::string appID, std::unordered_map opts) { + Debug::log(LOG, "[remotedesktop] New ConnectToEIS request: {}:", sessionHandle.c_str()); + const auto PSESSION = m_mSessions[sessionHandle]; + if (!PSESSION) return (sdbus::UnixFd)0; + + int sockfd = PSESSION->eis->getFileDescriptor(); + + Debug::log(LOG, "[remotedesktop] Connected to the socket. File descriptor: {}", sockfd); + return (sdbus::UnixFd)sockfd; +} diff --git a/src/portals/RemoteDesktop.hpp b/src/portals/RemoteDesktop.hpp new file mode 100644 index 0000000..8573127 --- /dev/null +++ b/src/portals/RemoteDesktop.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include +#include "wlr-virtual-pointer-unstable-v1.hpp" +#include "virtual-keyboard-unstable-v1.hpp" +#include "../shared/Session.hpp" +#include "../shared/Eis.hpp" +#include "../dbusDefines.hpp" +#include +#include + +class CRemoteDesktopPortal { + public: + CRemoteDesktopPortal(); + void registerPointer(SP mgr); + void registerKeyboard(SP mgr); + + dbUasv onCreateSession(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, + std::unordered_map opts); + dbUasv onSelectDevices(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, + std::unordered_map opts); + dbUasv onStart(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, std::string parentWindow, + std::unordered_map opts); + dbUasv onNotifyPointerMotion(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map opts); + dbUasv onNotifyPointerMotionAbsolute(sdbus::ObjectPath sessionHandle, unsigned int stream, double x, double y, std::unordered_map opts); + dbUasv onNotifyPointerButton(sdbus::ObjectPath sessionHandle, int button, unsigned int state, std::unordered_map opts); + dbUasv onNotifyPointerAxis(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map opts); + dbUasv onNotifyPointerAxisDiscrete(sdbus::ObjectPath sessionHandle, unsigned int axis, int steps, std::unordered_map opts); + dbUasv onNotifyKeyboardKeycode(sdbus::ObjectPath sessionHandle, int keycode, unsigned int state, std::unordered_map opts); + dbUasv onNotifyKeyboardKeysym(sdbus::ObjectPath sessionHandle, int keysym, unsigned int state, std::unordered_map opts); + dbUasv onNotifyTouchDown(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y, + std::unordered_map opts); + dbUasv onNotifyTouchMotion(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y, + std::unordered_map opts); + dbUasv onNotifyTouchUp(sdbus::ObjectPath sessionHandle, unsigned int slot, std::unordered_map opts); + sdbus::UnixFd onConnectToEIS(sdbus::ObjectPath sessionHandle, std::string appID, std::unordered_map opts); + + struct SSession { + std::string appid; + sdbus::ObjectPath requestHandle, sessionHandle; + std::unique_ptr request; + std::unique_ptr session; + + SP pointer; + SP keyboard; + std::unique_ptr eis; + }; + + std::unordered_map> m_mSessions; + + private: + std::priority_queue, std::greater> m_pqFreeIds; + + struct { + SP pointerMgr; + SP keyboardMgr; + } m_sState; + + std::unique_ptr m_pObject; + uint m_uSessionCounter = 0; + + const sdbus::InterfaceName INTERFACE_NAME = sdbus::InterfaceName{"org.freedesktop.impl.portal.RemoteDesktop"}; + const sdbus::ObjectPath OBJECT_PATH = sdbus::ObjectPath{"/org/freedesktop/portal/desktop"}; +}; diff --git a/src/shared/Eis.cpp b/src/shared/Eis.cpp index 6032fba..7807c1e 100644 --- a/src/shared/Eis.cpp +++ b/src/shared/Eis.cpp @@ -6,12 +6,11 @@ #include #include #include +#include -EmulatedInputServer::EmulatedInputServer(std::string socketName, Keymap _keymap) { +EmulatedInputServer::EmulatedInputServer(std::string socketName) { Debug::log(LOG, "[EIS] Init socket: {}", socketName); - keymap = _keymap; - const char* xdg = getenv("XDG_RUNTIME_DIR"); if (xdg) socketPath = std::string(xdg) + "/" + socketName; @@ -61,12 +60,6 @@ int EmulatedInputServer::onEvent(eis_event* e) { eisClient = eis_event_get_client(e); Debug::log(LOG, "[EIS] {} client connected: {}", eis_client_is_sender(eisClient) ? "Sender" : "Receiver", eis_client_get_name(eisClient)); - if (eis_client_is_sender(eisClient)) { - Debug::log(WARN, "[EIS] Unexpected sender client {} connected to input capture session", eis_client_get_name(eisClient)); - eis_client_disconnect(eisClient); - return 0; - } - if (client.handle) { Debug::log(WARN, "[EIS] Unexpected additional client {} connected to input capture session", eis_client_get_name(eisClient)); eis_client_disconnect(eisClient); @@ -80,6 +73,7 @@ int EmulatedInputServer::onEvent(eis_event* e) { seat = eis_client_new_seat(eisClient, "default"); eis_seat_configure_capability(seat, EIS_DEVICE_CAP_POINTER); + eis_seat_configure_capability(seat, EIS_DEVICE_CAP_POINTER_ABSOLUTE); eis_seat_configure_capability(seat, EIS_DEVICE_CAP_BUTTON); eis_seat_configure_capability(seat, EIS_DEVICE_CAP_SCROLL); eis_seat_configure_capability(seat, EIS_DEVICE_CAP_KEYBOARD); @@ -120,6 +114,121 @@ int EmulatedInputServer::onEvent(eis_event* e) { } else Debug::log(WARN, "[EIS] Unknown device to close"); break; + case EIS_EVENT_FRAME: + if (virtualPointer != nullptr) { + virtualPointer->sendFrame(); + } + break; + case EIS_EVENT_DEVICE_START_EMULATING: + device = eis_event_get_device(e); + Debug::log(LOG, "[EIS] Device {} is ready to send events", eis_device_get_name(device)); + break; + case EIS_EVENT_DEVICE_STOP_EMULATING: + device = eis_event_get_device(e); + Debug::log(LOG, "[EIS] Device {} will no longer send events", eis_device_get_name(device)); + break; + case EIS_EVENT_POINTER_MOTION: + if (virtualPointer != nullptr) { + virtualPointer->sendMotion(0, eis_event_pointer_get_dx(e), eis_event_pointer_get_dy(e)); + } + break; + case EIS_EVENT_POINTER_MOTION_ABSOLUTE: + if (virtualPointer != nullptr) { + virtualPointer->sendMotionAbsolute(0, eis_event_pointer_get_absolute_x(e), eis_event_pointer_get_absolute_y(e), screenWidth, screenHeight); + } + break; + case EIS_EVENT_BUTTON_BUTTON: + if (virtualPointer != nullptr) { + virtualPointer->sendButton(0, eis_event_button_get_button(e), eis_event_button_get_is_press(e)); + } + break; + case EIS_EVENT_SCROLL_DELTA: + if (virtualPointer != nullptr) { + virtualPointer->sendAxis(0, 0, eis_event_scroll_get_dy(e)); + virtualPointer->sendAxis(0, 1, eis_event_scroll_get_dx(e)); + } + break; + case EIS_EVENT_SCROLL_STOP: + if (virtualPointer != nullptr) { + if (eis_event_scroll_get_stop_x(e)) + virtualPointer->sendAxisStop(0, 1); + if (eis_event_scroll_get_stop_y(e)) + virtualPointer->sendAxisStop(0, 0); + } + break; + case EIS_EVENT_SCROLL_DISCRETE: + if (virtualPointer != nullptr) { + virtualPointer->sendAxisDiscrete(1, 0, 1, eis_event_scroll_get_discrete_dy(e)); + virtualPointer->sendAxisDiscrete(1, 1, 1, eis_event_scroll_get_discrete_dx(e)); + } + break; + case EIS_EVENT_KEYBOARD_KEY: + { + if (virtualKeyboard != nullptr) { + uint32_t keycode = eis_event_keyboard_get_key(e); + bool pressed = eis_event_keyboard_get_key_is_press(e); + switch (keycode) { + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: + if (pressed) + depressed |= 1; + else + depressed &= ~(1); + break; + case KEY_CAPSLOCK: + if (pressed) { + if (locked & (1 << 1)) + locked &= ~(1 << 1); + else + locked |= 1 << 1; + } + break; + case KEY_LEFTCTRL: + case KEY_RIGHTCTRL: + if (pressed) + depressed |= 1 << 2; + else + depressed &= ~(1 << 2); + break; + case KEY_LEFTALT: + case KEY_RIGHTALT: + if (pressed) + depressed |= 1 << 3; + else + depressed &= ~(1 << 3); + break; + case KEY_NUMLOCK: + if (pressed) { + if (locked & (1 << 4)) + locked &= ~(1 << 4); + else + locked |= 1 << 4; + } + break; + case KEY_LEFTMETA: + case KEY_RIGHTMETA: + if (pressed) + depressed |= 1 << 6; + else + depressed &= ~(1 << 6); + break; + case KEY_SCROLLLOCK: + if (pressed) { + if (locked & (1 << 7)) + locked &= ~(1 << 7); + else + locked |= 1 << 7; + } + break; + default: + virtualKeyboard->sendModifiers(depressed, latched, locked, 3); + virtualKeyboard->sendKey(1, keycode, pressed); + break; + } + } + } + break; + default: return 0; } return 0; @@ -132,6 +241,7 @@ void EmulatedInputServer::ensurePointer(eis_event* event) { eis_device* pointer = eis_seat_new_device(client.seat); eis_device_configure_name(pointer, "captured relative pointer"); eis_device_configure_capability(pointer, EIS_DEVICE_CAP_POINTER); + eis_device_configure_capability(pointer, EIS_DEVICE_CAP_POINTER_ABSOLUTE); eis_device_configure_capability(pointer, EIS_DEVICE_CAP_BUTTON); eis_device_configure_capability(pointer, EIS_DEVICE_CAP_SCROLL); @@ -140,9 +250,15 @@ void EmulatedInputServer::ensurePointer(eis_event* event) { eis_region_set_offset(r, o->x, o->y); eis_region_set_size(r, o->width, o->height); + Debug::log(LOG, "[EIS] REGION TME {} {}", o->width, o->height); eis_region_set_physical_scale(r, o->scale); eis_region_add(r); eis_region_unref(r); + + //#FIXME: #TODO: this doesn't work if there are multiple outputs in getAllOutPuts() + screenWidth = o->width; + screenHeight = o->height; + } eis_device_add(pointer); diff --git a/src/shared/Eis.hpp b/src/shared/Eis.hpp index 3af8181..d69b418 100644 --- a/src/shared/Eis.hpp +++ b/src/shared/Eis.hpp @@ -1,7 +1,11 @@ #pragma once -#include +#include #include +#include "wlr-virtual-pointer-unstable-v1.hpp" +#include "virtual-keyboard-unstable-v1.hpp" +#include +#include "../includes.hpp" struct Keymap { int32_t fd = 0; @@ -13,7 +17,7 @@ struct Keymap { */ class EmulatedInputServer { public: - EmulatedInputServer(std::string socketPath, Keymap keymap); + EmulatedInputServer(std::string socketPath); std::string socketPath; void startEmulating(int activationId); @@ -32,6 +36,9 @@ class EmulatedInputServer { int getFileDescriptor(); + void setVirtualPointer(SP ptr) {virtualPointer = ptr;} + void setVirtualKeyboard(SP kb) {virtualKeyboard = kb; } + void stopServer(); private: @@ -46,6 +53,15 @@ class EmulatedInputServer { eis_device* keyboard = nullptr; } client; + SP virtualPointer = nullptr; + SP virtualKeyboard = nullptr; + uint32_t screenWidth = 0; + uint32_t screenHeight = 0; + + uint32_t depressed = 0; + uint32_t latched = 0; + uint32_t locked = 0; + Keymap keymap; int onEvent(eis_event* e);