aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rwxr-xr-xCMakeLists.txt1
-rw-r--r--protocols/meson.build1
-rw-r--r--protocols/wlr-virtual-pointer-unstable-v1.xml152
-rw-r--r--src/Compositor.cpp4
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/events/Devices.cpp8
-rw-r--r--src/helpers/WLClasses.hpp15
-rw-r--r--src/includes.hpp2
-rw-r--r--src/managers/ProtocolManager.cpp2
-rw-r--r--src/managers/input/InputManager.cpp36
-rw-r--r--src/managers/input/InputManager.hpp5
-rw-r--r--src/protocols/VirtualPointer.cpp165
-rw-r--r--src/protocols/VirtualPointer.hpp57
13 files changed, 428 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d485977c..f2939040 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -262,6 +262,7 @@ protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-uns
protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
protocolNew("protocols/virtual-keyboard-unstable-v1.xml" "virtual-keyboard-unstable-v1" true)
+protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer-unstable-v1" true)
protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true)
protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
diff --git a/protocols/meson.build b/protocols/meson.build
index 57bca3d1..38b973af 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -40,6 +40,7 @@ new_protocols = [
['wlr-output-power-management-unstable-v1.xml'],
['input-method-unstable-v2.xml'],
['virtual-keyboard-unstable-v1.xml'],
+ ['wlr-virtual-pointer-unstable-v1.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
diff --git a/protocols/wlr-virtual-pointer-unstable-v1.xml b/protocols/wlr-virtual-pointer-unstable-v1.xml
new file mode 100644
index 00000000..ea243e7c
--- /dev/null
+++ b/protocols/wlr-virtual-pointer-unstable-v1.xml
@@ -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>
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 80e61a25..3f53f6c3 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -245,8 +245,6 @@ void CCompositor::initServer() {
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
- m_sWLRVirtPtrMgr = wlr_virtual_pointer_manager_v1_create(m_sWLDisplay);
-
m_sWRLDRMLeaseMgr = wlr_drm_lease_v1_manager_create(m_sWLDisplay, m_sWLRBackend);
if (!m_sWRLDRMLeaseMgr) {
Debug::log(INFO, "Failed to create wlr_drm_lease_v1_manager");
@@ -305,7 +303,6 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sWLROutputLayout->events.change, &Events::listen_change, m_sWLROutputLayout, "OutputLayout");
addWLSignal(&m_sWLROutputMgr->events.apply, &Events::listen_outputMgrApply, m_sWLROutputMgr, "OutputMgr");
addWLSignal(&m_sWLROutputMgr->events.test, &Events::listen_outputMgrTest, m_sWLROutputMgr, "OutputMgr");
- addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
if (m_sWRLDRMLeaseMgr)
@@ -346,7 +343,6 @@ void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_change);
removeWLSignal(&Events::listen_outputMgrApply);
removeWLSignal(&Events::listen_outputMgrTest);
- removeWLSignal(&Events::listen_newVirtPtr);
removeWLSignal(&Events::listen_RendererDestroy);
if (m_sWRLDRMLeaseMgr)
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 175688c7..02fd6311 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -60,7 +60,6 @@ class CCompositor {
wlr_egl* m_sWLREGL;
int m_iDRMFD;
wlr_server_decoration_manager* m_sWLRServerDecoMgr;
- wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
diff --git a/src/events/Devices.cpp b/src/events/Devices.cpp
index bf72d89f..ec0b14c4 100644
--- a/src/events/Devices.cpp
+++ b/src/events/Devices.cpp
@@ -93,14 +93,6 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
g_pInputManager->updateCapabilities();
}
-void Events::listener_newVirtPtr(wl_listener* listener, void* data) {
- const auto EV = (wlr_virtual_pointer_v1_new_pointer_event*)data;
- const auto POINTER = EV->new_pointer;
- const auto DEVICE = &POINTER->pointer.base;
-
- g_pInputManager->newMouse(DEVICE, true);
-}
-
void Events::listener_destroyMouse(void* owner, void* data) {
const auto PMOUSE = (SMouse*)owner;
diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp
index 6b155e83..7eaebead 100644
--- a/src/helpers/WLClasses.hpp
+++ b/src/helpers/WLClasses.hpp
@@ -12,6 +12,7 @@
class CMonitor;
class CVirtualKeyboard;
+class CVirtualPointer;
struct SRenderData {
CMonitor* pMonitor;
@@ -104,13 +105,19 @@ struct SKeyboard {
};
struct SMouse {
- wlr_input_device* mouse = nullptr;
+ wlr_input_device* mouse = nullptr;
- std::string name = "";
+ std::string name = "";
+
+ bool virt = false;
+
+ bool connected = false; // means connected to the cursor
- bool virt = false;
+ WP<CVirtualPointer> virtualPointer;
- bool connected = false; // means connected to the cursor
+ struct {
+ CHyprSignalListener destroyMouse;
+ } listeners;
DYNLISTENER(destroyMouse);
diff --git a/src/includes.hpp b/src/includes.hpp
index 052c973e..73611fb2 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -67,7 +67,6 @@ extern "C" {
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_damage_ring.h>
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
-#include <wlr/types/wlr_virtual_pointer_v1.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <wlr/util/edges.h>
@@ -79,6 +78,7 @@ extern "C" {
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/interfaces/wlr_keyboard.h>
+#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/types/wlr_xdg_foreign_registry.h>
#include <wlr/types/wlr_xdg_foreign_v1.h>
#include <wlr/types/wlr_xdg_foreign_v2.h>
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index 122e2ff1..8ec2a35f 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -21,6 +21,7 @@
#include "../protocols/SessionLock.hpp"
#include "../protocols/InputMethodV2.hpp"
#include "../protocols/VirtualKeyboard.hpp"
+#include "../protocols/VirtualPointer.hpp"
CProtocolManager::CProtocolManager() {
@@ -45,6 +46,7 @@ CProtocolManager::CProtocolManager() {
PROTO::sessionLock = std::make_unique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
PROTO::virtualKeyboard = std::make_unique<CVirtualKeyboardProtocol>(&zwp_virtual_keyboard_manager_v1_interface, 1, "VirtualKeyboard");
+ PROTO::virtualPointer = std::make_unique<CVirtualPointerProtocol>(&zwlr_virtual_pointer_manager_v1_interface, 2, "VirtualPointer");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index e3c1b463..58b5caa3 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -12,6 +12,7 @@
#include "../../protocols/SessionLock.hpp"
#include "../../protocols/InputMethodV2.hpp"
#include "../../protocols/VirtualKeyboard.hpp"
+#include "../../protocols/VirtualPointer.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
@@ -40,6 +41,8 @@ CInputManager::CInputManager() {
m_sListeners.newIdleInhibitor = PROTO::idleInhibit->events.newIdleInhibitor.registerListener([this](std::any data) { this->newIdleInhibitor(data); });
m_sListeners.newVirtualKeyboard =
PROTO::virtualKeyboard->events.newKeyboard.registerListener([this](std::any data) { this->newVirtualKeyboard(std::any_cast<SP<CVirtualKeyboard>>(data)); });
+ m_sListeners.newVirtualMouse =
+ PROTO::virtualPointer->events.newPointer.registerListener([this](std::any data) { this->newVirtualMouse(std::any_cast<SP<CVirtualPointer>>(data)); });
}
CInputManager::~CInputManager() {
@@ -968,12 +971,41 @@ void CInputManager::applyConfigToKeyboard(SKeyboard* pKeyboard) {
pKeyboard->keyboard->name);
}
-void CInputManager::newMouse(wlr_input_device* mouse, bool virt) {
+void CInputManager::newVirtualMouse(SP<CVirtualPointer> mouse) {
+ const auto PMOUSE = &m_lMice.emplace_back();
+
+ PMOUSE->mouse = &mouse->wlr()->base;
+ PMOUSE->virtualPointer = mouse;
+ PMOUSE->virt = true;
+ try {
+ PMOUSE->name = getNameForNewDevice(mouse->wlr()->base.name);
+ } catch (std::exception& e) {
+ Debug::log(ERR, "Mouse had no name???"); // logic error
+ }
+
+ wlr_cursor_attach_input_device(g_pCompositor->m_sWLRCursor, &mouse->wlr()->base);
+
+ PMOUSE->connected = true;
+
+ setPointerConfigs();
+
+ PMOUSE->hyprListener_destroyMouse.initCallback(&mouse->wlr()->base.events.destroy, &Events::listener_destroyMouse, PMOUSE, "Mouse");
+
+ // TODO: this pointer pass sucks.
+ PMOUSE->listeners.destroyMouse = mouse->events.destroy.registerListener([this, PMOUSE](std::any data) { destroyMouse(PMOUSE->mouse); });
+
+ g_pCompositor->m_sSeat.mouse = PMOUSE;
+
+ m_tmrLastCursorMovement.reset();
+
+ Debug::log(LOG, "New virtual mouse created, pointer WLR: {:x}", (uintptr_t)mouse->wlr());
+}
+
+void CInputManager::newMouse(wlr_input_device* mouse) {
m_lMice.emplace_back();
const auto PMOUSE = &m_lMice.back();
PMOUSE->mouse = mouse;
- PMOUSE->virt = virt;
try {
PMOUSE->name = getNameForNewDevice(mouse->name);
} catch (std::exception& e) {
diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp
index 27551249..6a338088 100644
--- a/src/managers/input/InputManager.hpp
+++ b/src/managers/input/InputManager.hpp
@@ -12,6 +12,7 @@ class CPointerConstraint;
class CWindow;
class CIdleInhibitor;
class CVirtualKeyboard;
+class CVirtualPointer;
enum eClickBehaviorMode {
CLICKMODE_DEFAULT = 0,
@@ -79,7 +80,8 @@ class CInputManager {
void newKeyboard(wlr_input_device*);
void newVirtualKeyboard(SP<CVirtualKeyboard>);
- void newMouse(wlr_input_device*, bool virt = false);
+ void newMouse(wlr_input_device*);
+ void newVirtualMouse(SP<CVirtualPointer>);
void newTouchDevice(wlr_input_device*);
void newSwitch(wlr_input_device*);
void destroyTouchDevice(STouchDevice*);
@@ -201,6 +203,7 @@ class CInputManager {
CHyprSignalListener setCursorShape;
CHyprSignalListener newIdleInhibitor;
CHyprSignalListener newVirtualKeyboard;
+ CHyprSignalListener newVirtualMouse;
} m_sListeners;
bool m_bCursorImageOverridden = false;
diff --git a/src/protocols/VirtualPointer.cpp b/src/protocols/VirtualPointer.cpp
new file mode 100644
index 00000000..287e7bad
--- /dev/null
+++ b/src/protocols/VirtualPointer.cpp
@@ -0,0 +1,165 @@
+#include "VirtualPointer.hpp"
+
+#define LOGM PROTO::virtualPointer->protoLog
+
+static const wlr_pointer_impl pointerImpl = {
+ .name = "virtual-pointer-v1",
+};
+
+CVirtualPointer::CVirtualPointer(SP<CZwlrVirtualPointerV1> resource_) : resource(resource_) {
+ if (!good())
+ return;
+
+ resource->setDestroy([this](CZwlrVirtualPointerV1* r) {
+ events.destroy.emit();
+ PROTO::virtualPointer->destroyResource(this);
+ });
+ resource->setOnDestroy([this](CZwlrVirtualPointerV1* r) {
+ events.destroy.emit();
+ PROTO::virtualPointer->destroyResource(this);
+ });
+
+ wlr_pointer_init(&pointer, &pointerImpl, "CVirtualPointer");
+
+ resource->setMotion([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, wl_fixed_t dx, wl_fixed_t dy) {
+ wlr_pointer_motion_event event = {
+ .pointer = &pointer,
+ .time_msec = timeMs,
+ .delta_x = wl_fixed_to_double(dx),
+ .delta_y = wl_fixed_to_double(dy),
+ .unaccel_dx = wl_fixed_to_double(dx),
+ .unaccel_dy = wl_fixed_to_double(dy),
+ };
+ wl_signal_emit_mutable(&pointer.events.motion, &event);
+ });
+
+ resource->setMotionAbsolute([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t x, uint32_t y, uint32_t xExtent, uint32_t yExtent) {
+ if (!xExtent || !yExtent)
+ return;
+
+ wlr_pointer_motion_absolute_event event = {
+ .pointer = &pointer,
+ .time_msec = timeMs,
+ .x = (double)x / xExtent,
+ .y = (double)y / yExtent,
+ };
+ wl_signal_emit_mutable(&pointer.events.motion_absolute, &event);
+ });
+
+ resource->setButton([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t button, uint32_t state) {
+ struct wlr_pointer_button_event event = {
+ .pointer = &pointer,
+ .time_msec = timeMs,
+ .button = button,
+ .state = (wl_pointer_button_state)state,
+ };
+ wl_signal_emit_mutable(&pointer.events.button, &event);
+ });
+
+ resource->setAxis([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value) {
+ if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis");
+ return;
+ }
+
+ axis = axis_;
+ axisEvents[axis] = wlr_pointer_axis_event{.pointer = &pointer, .time_msec = timeMs, .orientation = (wl_pointer_axis)axis, .delta = wl_fixed_to_double(value)};
+ });
+
+ resource->setFrame([this](CZwlrVirtualPointerV1* r) {
+ for (auto& e : axisEvents) {
+ if (!e.pointer)
+ continue;
+ wl_signal_emit_mutable(&pointer.events.axis, &e);
+ e.pointer = nullptr;
+ }
+
+ wl_signal_emit_mutable(&pointer.events.frame, &pointer);
+ });
+
+ resource->setAxisSource([this](CZwlrVirtualPointerV1* r, uint32_t source) { axisEvents[axis].source = (wl_pointer_axis_source)source; });
+
+ resource->setAxisStop([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_) {
+ if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis");
+ return;
+ }
+
+ axis = axis_;
+ axisEvents[axis].pointer = &pointer;
+ axisEvents[axis].time_msec = timeMs;
+ axisEvents[axis].orientation = (wl_pointer_axis)axis;
+ axisEvents[axis].delta = 0;
+ axisEvents[axis].delta_discrete = 0;
+ });
+
+ resource->setAxisDiscrete([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value, int32_t discrete) {
+ if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
+ r->error(ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, "Invalid axis");
+ return;
+ }
+
+ axis = axis_;
+ axisEvents[axis].pointer = &pointer;
+ axisEvents[axis].time_msec = timeMs;
+ axisEvents[axis].orientation = (wl_pointer_axis)axis;
+ axisEvents[axis].delta = wl_fixed_to_double(value);
+ axisEvents[axis].delta_discrete = discrete * 120;
+ });
+}
+
+CVirtualPointer::~CVirtualPointer() {
+ wlr_pointer_finish(&pointer);
+ events.destroy.emit();
+}
+
+bool CVirtualPointer::good() {
+ return resource->resource();
+}
+
+wlr_pointer* CVirtualPointer::wlr() {
+ return &pointer;
+}
+
+wl_client* CVirtualPointer::client() {
+ return resource->client();
+}
+
+CVirtualPointerProtocol::CVirtualPointerProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CVirtualPointerProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CZwlrVirtualPointerManagerV1>(client, ver, id)).get();
+ RESOURCE->setOnDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
+ RESOURCE->setDestroy([this](CZwlrVirtualPointerManagerV1* p) { this->onManagerResourceDestroy(p->resource()); });
+
+ RESOURCE->setCreateVirtualPointer([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) { this->onCreatePointer(pMgr, seat, id); });
+ RESOURCE->setCreateVirtualPointerWithOutput([this](CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, wl_resource* output, uint32_t id) {
+ LOGM(WARN, "TODO: CreateWithOutput is not supported yet. Ignoring for now.");
+ this->onCreatePointer(pMgr, seat, id);
+ });
+}
+
+void CVirtualPointerProtocol::onManagerResourceDestroy(wl_resource* res) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
+}
+
+void CVirtualPointerProtocol::destroyResource(CVirtualPointer* pointer) {
+ std::erase_if(m_vPointers, [&](const auto& other) { return other.get() == pointer; });
+}
+
+void CVirtualPointerProtocol::onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id) {
+
+ const auto RESOURCE = m_vPointers.emplace_back(std::make_shared<CVirtualPointer>(std::make_shared<CZwlrVirtualPointerV1>(pMgr->client(), pMgr->version(), id)));
+
+ if (!RESOURCE->good()) {
+ pMgr->noMemory();
+ m_vPointers.pop_back();
+ return;
+ }
+
+ LOGM(LOG, "New VPointer at id {}", id);
+
+ events.newPointer.emit(RESOURCE);
+} \ No newline at end of file
diff --git a/src/protocols/VirtualPointer.hpp b/src/protocols/VirtualPointer.hpp
new file mode 100644
index 00000000..4528b491
--- /dev/null
+++ b/src/protocols/VirtualPointer.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <cstdint>
+#include <array>
+#include "WaylandProtocol.hpp"
+#include "wlr-virtual-pointer-unstable-v1.hpp"
+#include "../helpers/signal/Signal.hpp"
+
+class CVirtualPointer {
+ public:
+ CVirtualPointer(SP<CZwlrVirtualPointerV1> resource_);
+ ~CVirtualPointer();
+
+ struct {
+ CSignal destroy;
+ } events;
+
+ bool good();
+ wlr_pointer* wlr();
+ wl_client* client();
+
+ private:
+ SP<CZwlrVirtualPointerV1> resource;
+ wlr_pointer pointer;
+
+ uint32_t axis = 0;
+
+ std::array<wlr_pointer_axis_event, 2> axisEvents;
+};
+
+class CVirtualPointerProtocol : public IWaylandProtocol {
+ public:
+ CVirtualPointerProtocol(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
+
+ struct {
+ CSignal newPointer; // SP<CVirtualPointer>
+ } events;
+
+ private:
+ void onManagerResourceDestroy(wl_resource* res);
+ void destroyResource(CVirtualPointer* pointer);
+ void onCreatePointer(CZwlrVirtualPointerManagerV1* pMgr, wl_resource* seat, uint32_t id);
+
+ //
+ std::vector<UP<CZwlrVirtualPointerManagerV1>> m_vManagers;
+ std::vector<SP<CVirtualPointer>> m_vPointers;
+
+ friend class CVirtualPointer;
+};
+
+namespace PROTO {
+ inline UP<CVirtualPointerProtocol> virtualPointer;
+};