remotedesktop: implement remotedesktop portal

This commit is contained in:
toneengo 2024-12-21 12:12:27 +10:30
parent 7c381c7cab
commit ebb343a884
11 changed files with 736 additions and 18 deletions

View file

@ -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"

View file

@ -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;

View file

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="virtual_keyboard_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwp_virtual_keyboard_v1" version="1">
<description summary="virtual keyboard">
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.
</description>
<request name="keymap">
<description summary="keyboard mapping">
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.
</description>
<arg name="format" type="uint" summary="keymap format"/>
<arg name="fd" type="fd" summary="keymap file descriptor"/>
<arg name="size" type="uint" summary="keymap size, in bytes"/>
</request>
<enum name="error">
<entry name="no_keymap" value="0" summary="No keymap was set"/>
</enum>
<request name="key">
<description summary="key event">
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.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="key" type="uint" summary="key that produced the event"/>
<arg name="state" type="uint" summary="physical state of the key"/>
</request>
<request name="modifiers">
<description summary="modifier and group state">
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.
</description>
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
<arg name="group" type="uint" summary="keyboard layout"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual keyboard keyboard object"/>
</request>
</interface>
<interface name="zwp_virtual_keyboard_manager_v1" version="1">
<description summary="virtual keyboard manager">
A virtual keyboard manager allows an application to provide keyboard
input events as if they came from a physical keyboard.
</description>
<enum name="error">
<entry name="unauthorized" value="0" summary="client not authorized to use the interface"/>
</enum>
<request name="create_virtual_keyboard">
<description summary="Create a new virtual 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.
</description>
<arg name="seat" type="object" interface="wl_seat"/>
<arg name="id" type="new_id" interface="zwp_virtual_keyboard_v1"/>
</request>
</interface>
</protocol>

View file

@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_virtual_pointer_unstable_v1">
<copyright>
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.
</copyright>
<interface name="zwlr_virtual_pointer_v1" version="2">
<description summary="virtual pointer">
This protocol allows clients to emulate a physical pointer device. The
requests are mostly mirror opposites of those specified in wl_pointer.
</description>
<enum name="error">
<entry name="invalid_axis" value="0"
summary="client sent invalid axis enumeration value" />
<entry name="invalid_axis_source" value="1"
summary="client sent invalid axis source enumeration value" />
</enum>
<request name="motion">
<description summary="pointer relative motion event">
The pointer has moved by a relative amount to the previous request.
Values are in the global compositor space.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="dx" type="fixed" summary="displacement on the x-axis"/>
<arg name="dy" type="fixed" summary="displacement on the y-axis"/>
</request>
<request name="motion_absolute">
<description summary="pointer absolute motion event">
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.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="x" type="uint" summary="position on the x-axis"/>
<arg name="y" type="uint" summary="position on the y-axis"/>
<arg name="x_extent" type="uint" summary="extent of the x-axis"/>
<arg name="y_extent" type="uint" summary="extent of the y-axis"/>
</request>
<request name="button">
<description summary="button event">
A button was pressed or released.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="button" type="uint" summary="button that produced the event"/>
<arg name="state" type="uint" enum="wl_pointer.button_state" summary="physical state of the button"/>
</request>
<request name="axis">
<description summary="axis event">
Scroll and other axis requests.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/>
<arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/>
</request>
<request name="frame">
<description summary="end of a pointer event sequence">
Indicates the set of events that logically belong together.
</description>
</request>
<request name="axis_source">
<description summary="axis source event">
Source information for scroll and other axis.
</description>
<arg name="axis_source" type="uint" enum="wl_pointer.axis_source" summary="source of the axis event"/>
</request>
<request name="axis_stop">
<description summary="axis stop event">
Stop notification for scroll and other axes.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="the axis stopped with this event"/>
</request>
<request name="axis_discrete">
<description summary="axis click event">
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.
</description>
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
<arg name="axis" type="uint" enum="wl_pointer.axis" summary="axis type"/>
<arg name="value" type="fixed" summary="length of vector in touchpad coordinates"/>
<arg name="discrete" type="int" summary="number of steps"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual pointer object"/>
</request>
</interface>
<interface name="zwlr_virtual_pointer_manager_v1" version="2">
<description summary="virtual pointer manager">
This object allows clients to create individual virtual pointer objects.
</description>
<request name="create_virtual_pointer">
<description summary="Create a new virtual pointer">
Creates a new virtual pointer. The optional seat is a suggestion to the
compositor.
</description>
<arg name="seat" type="object" interface="wl_seat" allow-null="true"/>
<arg name="id" type="new_id" interface="zwlr_virtual_pointer_v1"/>
</request>
<request name="destroy" type="destructor" since="1">
<description summary="destroy the virtual pointer manager"/>
</request>
<!-- Version 2 additions -->
<request name="create_virtual_pointer_with_output" since="2">
<description summary="Create a new virtual pointer">
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.
</description>
<arg name="seat" type="object" interface="wl_seat" allow-null="true"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="id" type="new_id" interface="zwlr_virtual_pointer_v1"/>
</request>
</interface>
</protocol>

View file

@ -31,7 +31,7 @@ SOutput::SOutput(SP<CCWlOutput> 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<CScreencopyPortal>(makeShared<CCZwlrScreencopyManagerV1>(
(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<CGlobalShortcutsPortal>(makeShared<CCHyprlandGlobalShortcutsManagerV1>(
(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<CRemoteDesktopPortal>();
if (INTERFACE == hyprland_input_capture_manager_v1_interface.name)
m_sPortals.inputCapture = std::make_unique<CInputCapturePortal>(makeShared<CCHyprlandInputCaptureManagerV1>(
(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<CCZwlrVirtualPointerManagerV1>(
(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<CCZwpVirtualKeyboardManagerV1>(
(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<CCHyprlandToplevelExportManagerV1>(
(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<CCWlSeat>((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<SKeyboard>(makeShared<CCWlKeyboard>(
(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);

View file

@ -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 <gbm.h>
@ -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<std::unique_ptr<SOutput>> 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<CScreenshotPortal> screenshot;
std::unique_ptr<CGlobalShortcutsPortal> globalShortcuts;
std::unique_ptr<CInputCapturePortal> inputCapture;
std::unique_ptr<CRemoteDesktopPortal> remoteDesktop;
} m_sPortals;
struct {
@ -77,6 +88,7 @@ class CPortalManager {
struct {
wl_display* display = nullptr;
SP<CCWlSeat> seat;
SP<CCWlRegistry> registry;
SP<CCHyprlandToplevelExportManagerV1> hyprlandToplevelMgr;
SP<CCZwpLinuxDmabufV1> linuxDmabuf;
@ -130,8 +142,8 @@ class CPortalManager {
std::unique_ptr<std::thread> thread;
} m_sTimersThread;
std::unique_ptr<sdbus::IConnection> m_pConnection;
std::vector<std::unique_ptr<SOutput>> m_vOutputs;
std::unique_ptr<sdbus::IConnection> m_pConnection;
std::vector<std::unique_ptr<SOutput>> m_vOutputs;
std::mutex m_mEventLock;
};

View file

@ -25,7 +25,11 @@ CInputCapturePortal::CInputCapturePortal(SP<CCHyprlandInputCaptureManagerV1> 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<EmulatedInputServer>("eis-" + sessionId, keymap);
session->eis = std::make_unique<EmulatedInputServer>("eis-" + sessionId);
session->eis->setKeymap(keymap);
sessions.emplace(sessionHandle, session);

View file

@ -0,0 +1,207 @@
#include "RemoteDesktop.hpp"
#include "../core/PortalManager.hpp"
#include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp"
void CRemoteDesktopPortal::registerPointer(SP<CCZwlrVirtualPointerManagerV1> mgr) {
m_sState.pointerMgr = mgr;
}
void CRemoteDesktopPortal::registerKeyboard(SP<CCZwpVirtualKeyboardManagerV1> 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<std::string, sdbus::Variant> m) {
return onCreateSession(o1, o2, s, m);
}),
sdbus::registerMethod("SelectDevices")
.implementedAs([this](sdbus::ObjectPath o1, sdbus::ObjectPath o2, std::string s, std::unordered_map<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> m) {
return onStart(o1, o2, s1, s2, m);
}),
sdbus::registerMethod("NotifyPointerMotion")
.implementedAs([this](sdbus::ObjectPath o, double d1, double d2, std::unordered_map<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> m) {
return onNotifyPointerMotionAbsolute(o, u, d1, d2, m);
}),
sdbus::registerMethod("NotifyPointerButton")
.implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map<std::string, sdbus::Variant> m) {
return onNotifyPointerButton(o, i, u, m);
}),
sdbus::registerMethod("NotifyPointerAxis")
.implementedAs([this](sdbus::ObjectPath o, double d1, double d2, std::unordered_map<std::string, sdbus::Variant> m) {
return onNotifyPointerAxis(o, d1, d2, m);
}),
sdbus::registerMethod("NotifyPointerAxisDiscrete")
.implementedAs([this](sdbus::ObjectPath o, unsigned int u, int i, std::unordered_map<std::string, sdbus::Variant> m) {
return onNotifyPointerAxisDiscrete(o, u, i, m);
}),
sdbus::registerMethod("NotifyKeyboardKeycode")
.implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map<std::string, sdbus::Variant> m) {
return onNotifyKeyboardKeycode(o, i, u, m);
}),
sdbus::registerMethod("NotifyKeyboardKeysym")
.implementedAs([this](sdbus::ObjectPath o, int i, unsigned int u, std::unordered_map<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> m) {
return onNotifyTouchMotion(o, u1, u2, d1, d2, m);
}),
sdbus::registerMethod("NotifyTouchUp")
.implementedAs([this](sdbus::ObjectPath o, unsigned int u, std::unordered_map<std::string, sdbus::Variant> m) {
return onNotifyTouchUp(o, u, m);
}),
sdbus::registerMethod("ConnectToEIS").implementedAs([this](sdbus::ObjectPath o, std::string s, std::unordered_map<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<SSession> PSESSION = std::make_shared<SSession>(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<CCZwlrVirtualPointerV1>(m_sState.pointerMgr->sendCreateVirtualPointer(g_pPortalManager->m_sWaylandConnection.seat->resource()));
PSESSION->keyboard = makeShared<CCZwpVirtualKeyboardV1>(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<EmulatedInputServer>("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<std::string, sdbus::Variant> opts) {
return {0, {}};
}
dbUasv CRemoteDesktopPortal::onStart(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, std::string parentWindow,
std::unordered_map<std::string, sdbus::Variant> opts) {
Debug::log(LOG, "[remotedesktop] start request");
std::unordered_map<std::string, sdbus::Variant> results;
results["devices"] = sdbus::Variant{uint32_t{1 | 2}};
results["clipboard_enabled"] = sdbus::Variant{bool(true)};
std::unordered_map<std::string, sdbus::Variant> restoreData;
results["restore_data"] = sdbus::Variant{sdbus::Struct<std::string, uint32_t, sdbus::Variant>{"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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> 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<std::string, sdbus::Variant> opts) {
const auto PSESSION = m_mSessions[sessionHandle];
return {0, {}};
}
dbUasv CRemoteDesktopPortal::onNotifyTouchUp(sdbus::ObjectPath sessionHandle, unsigned int slot, std::unordered_map<std::string, sdbus::Variant> opts) {
const auto PSESSION = m_mSessions[sessionHandle];
return {0, {}};
}
sdbus::UnixFd CRemoteDesktopPortal::onConnectToEIS(sdbus::ObjectPath sessionHandle, std::string appID, std::unordered_map<std::string, sdbus::Variant> 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;
}

View file

@ -0,0 +1,64 @@
#pragma once
#include <sdbus-c++/sdbus-c++.h>
#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 <libei-1.0/libeis.h>
#include <queue>
class CRemoteDesktopPortal {
public:
CRemoteDesktopPortal();
void registerPointer(SP<CCZwlrVirtualPointerManagerV1> mgr);
void registerKeyboard(SP<CCZwpVirtualKeyboardManagerV1> mgr);
dbUasv onCreateSession(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID,
std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onSelectDevices(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID,
std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onStart(sdbus::ObjectPath requestHandle, sdbus::ObjectPath sessionHandle, std::string appID, std::string parentWindow,
std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyPointerMotion(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyPointerMotionAbsolute(sdbus::ObjectPath sessionHandle, unsigned int stream, double x, double y, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyPointerButton(sdbus::ObjectPath sessionHandle, int button, unsigned int state, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyPointerAxis(sdbus::ObjectPath sessionHandle, double dx, double dy, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyPointerAxisDiscrete(sdbus::ObjectPath sessionHandle, unsigned int axis, int steps, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyKeyboardKeycode(sdbus::ObjectPath sessionHandle, int keycode, unsigned int state, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyKeyboardKeysym(sdbus::ObjectPath sessionHandle, int keysym, unsigned int state, std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyTouchDown(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y,
std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyTouchMotion(sdbus::ObjectPath sessionHandle, unsigned int stream, unsigned int slot, double x, double y,
std::unordered_map<std::string, sdbus::Variant> opts);
dbUasv onNotifyTouchUp(sdbus::ObjectPath sessionHandle, unsigned int slot, std::unordered_map<std::string, sdbus::Variant> opts);
sdbus::UnixFd onConnectToEIS(sdbus::ObjectPath sessionHandle, std::string appID, std::unordered_map<std::string, sdbus::Variant> opts);
struct SSession {
std::string appid;
sdbus::ObjectPath requestHandle, sessionHandle;
std::unique_ptr<SDBusRequest> request;
std::unique_ptr<SDBusSession> session;
SP<CCZwlrVirtualPointerV1> pointer;
SP<CCZwpVirtualKeyboardV1> keyboard;
std::unique_ptr<EmulatedInputServer> eis;
};
std::unordered_map<std::string, std::shared_ptr<SSession>> m_mSessions;
private:
std::priority_queue<int, std::vector<int>, std::greater<int>> m_pqFreeIds;
struct {
SP<CCZwlrVirtualPointerManagerV1> pointerMgr;
SP<CCZwpVirtualKeyboardManagerV1> keyboardMgr;
} m_sState;
std::unique_ptr<sdbus::IObject> 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"};
};

View file

@ -6,12 +6,11 @@
#include <libeis.h>
#include <sys/mman.h>
#include <unistd.h>
#include <linux/input-event-codes.h>
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);

View file

@ -1,7 +1,11 @@
#pragma once
#include <libeis.h>
#include <libei-1.0/libeis.h>
#include <string>
#include "wlr-virtual-pointer-unstable-v1.hpp"
#include "virtual-keyboard-unstable-v1.hpp"
#include <memory>
#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<CCZwlrVirtualPointerV1> ptr) {virtualPointer = ptr;}
void setVirtualKeyboard(SP<CCZwpVirtualKeyboardV1> kb) {virtualKeyboard = kb; }
void stopServer();
private:
@ -46,6 +53,15 @@ class EmulatedInputServer {
eis_device* keyboard = nullptr;
} client;
SP<CCZwlrVirtualPointerV1> virtualPointer = nullptr;
SP<CCZwpVirtualKeyboardV1> 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);