aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorVaxry <[email protected]>2024-04-26 23:55:41 +0100
committerVaxry <[email protected]>2024-04-27 03:17:04 +0100
commit25aec3ac8ce65ed224f025f8f6dfef73780577a4 (patch)
tree9fa63de1670bd2f535f3bc237800fc6b7a81f646
parentf94264928a8ab4da8759d4ded25a46af44451d38 (diff)
downloadHyprland-25aec3ac8ce65ed224f025f8f6dfef73780577a4.tar.gz
Hyprland-25aec3ac8ce65ed224f025f8f6dfef73780577a4.zip
pointer-constraints: move to new impl
-rwxr-xr-xCMakeLists.txt4
-rw-r--r--flake.lock6
-rw-r--r--protocols/meson.build2
-rw-r--r--protocols/pointer-constraints-unstable-v1.xml339
-rw-r--r--src/Compositor.cpp5
-rw-r--r--src/Compositor.hpp1
-rw-r--r--src/desktop/Constraint.cpp142
-rw-r--r--src/desktop/Constraint.hpp44
-rw-r--r--src/desktop/WLSurface.cpp13
-rw-r--r--src/desktop/WLSurface.hpp23
-rw-r--r--src/events/Devices.cpp15
-rw-r--r--src/events/Events.hpp2
-rw-r--r--src/helpers/Region.cpp2
-rw-r--r--src/helpers/Region.hpp2
-rw-r--r--src/includes.hpp1
-rw-r--r--src/managers/ProtocolManager.cpp3
-rw-r--r--src/managers/input/InputManager.cpp19
-rw-r--r--src/managers/input/InputManager.hpp31
-rw-r--r--src/protocols/PointerConstraints.cpp260
-rw-r--r--src/protocols/PointerConstraints.hpp80
20 files changed, 404 insertions, 590 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dfda4d41..16419981 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -109,7 +109,7 @@ pkg_check_modules(deps REQUIRED IMPORTED_TARGET
wayland-server wayland-client wayland-cursor wayland-protocols
cairo pango pangocairo pixman-1
libdrm libinput hwdata libseat libdisplay-info libliftoff libudev gbm
- hyprwayland-scanner>=0.3.1 hyprlang>=0.3.2 hyprcursor>=0.1.7
+ hyprwayland-scanner>=0.3.2 hyprlang>=0.3.2 hyprcursor>=0.1.7
)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
@@ -250,7 +250,6 @@ target_link_libraries(Hyprland
)
protocol("protocols/idle.xml" "idle" true)
-protocol("protocols/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" true)
protocol("protocols/tablet-unstable-v2.xml" "tablet-unstable-v2" true)
protocol("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true)
protocol("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
@@ -275,6 +274,7 @@ protocolNew("staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml"
protocolNew("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml" "pointer-gestures-unstable-v1" false)
protocolNew("unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml" "keyboard-shortcuts-inhibit-unstable-v1" false)
protocolNew("unstable/text-input/text-input-unstable-v3.xml" "text-input-unstable-v3" false)
+protocolNew("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml" "pointer-constraints-unstable-v1" false)
# tools
add_subdirectory(hyprctl)
diff --git a/flake.lock b/flake.lock
index 3dbdf755..04b4f6f8 100644
--- a/flake.lock
+++ b/flake.lock
@@ -82,11 +82,11 @@
]
},
"locked": {
- "lastModified": 1713989318,
- "narHash": "sha256-WSsEQQxZQ+bsAWRhi1iXvP8sxgRyNtY3X1V3CfFdP5Q=",
+ "lastModified": 1714171579,
+ "narHash": "sha256-eaWDIvt8ufUKKz3Lc2a3PyemLJG1m9RYlF+HP3hWbaw=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
- "rev": "1cfe2d26a82ce794fd33ec06fa022e68501c5a45",
+ "rev": "126dad854f22fe30e6b82cd21808e76903d90ac5",
"type": "github"
},
"original": {
diff --git a/protocols/meson.build b/protocols/meson.build
index 7556fffc..d5f2af64 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -30,7 +30,6 @@ protocols = [
['wlr-layer-shell-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
['wlr-screencopy-unstable-v1.xml'],
- ['pointer-constraints-unstable-v1.xml'],
['tablet-unstable-v2.xml'],
['idle.xml'],
[hl_protocol_dir, 'protocols/hyprland-toplevel-export-v1.xml'],
@@ -52,6 +51,7 @@ new_protocols = [
[wl_protocol_dir, 'unstable/pointer-gestures/pointer-gestures-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/keyboard-shortcuts-inhibit/keyboard-shortcuts-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/text-input/text-input-unstable-v3.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
]
wl_protos_src = []
diff --git a/protocols/pointer-constraints-unstable-v1.xml b/protocols/pointer-constraints-unstable-v1.xml
deleted file mode 100644
index efd64b66..00000000
--- a/protocols/pointer-constraints-unstable-v1.xml
+++ /dev/null
@@ -1,339 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="pointer_constraints_unstable_v1">
-
- <copyright>
- Copyright © 2014 Jonas Ådahl
- Copyright © 2015 Red Hat Inc.
-
- 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>
-
- <description summary="protocol for constraining pointer motions">
- This protocol specifies a set of interfaces used for adding constraints to
- the motion of a pointer. Possible constraints include confining pointer
- motions to a given region, or locking it to its current position.
-
- In order to constrain the pointer, a client must first bind the global
- interface "wp_pointer_constraints" which, if a compositor supports pointer
- constraints, is exposed by the registry. Using the bound global object, the
- client uses the request that corresponds to the type of constraint it wants
- to make. See wp_pointer_constraints for more details.
-
- Warning! The protocol described in this file is experimental and backward
- incompatible changes may be made. Backward compatible changes may be added
- together with the corresponding interface version bump. Backward
- incompatible changes are done by bumping the version number in the protocol
- and interface names and resetting the interface version. Once the protocol
- is to be declared stable, the 'z' prefix and the version number in the
- protocol and interface names are removed and the interface version number is
- reset.
- </description>
-
- <interface name="zwp_pointer_constraints_v1" version="1">
- <description summary="constrain the movement of a pointer">
- The global interface exposing pointer constraining functionality. It
- exposes two requests: lock_pointer for locking the pointer to its
- position, and confine_pointer for locking the pointer to a region.
-
- The lock_pointer and confine_pointer requests create the objects
- wp_locked_pointer and wp_confined_pointer respectively, and the client can
- use these objects to interact with the lock.
-
- For any surface, only one lock or confinement may be active across all
- wl_pointer objects of the same seat. If a lock or confinement is requested
- when another lock or confinement is active or requested on the same surface
- and with any of the wl_pointer objects of the same seat, an
- 'already_constrained' error will be raised.
- </description>
-
- <enum name="error">
- <description summary="wp_pointer_constraints error values">
- These errors can be emitted in response to wp_pointer_constraints
- requests.
- </description>
- <entry name="already_constrained" value="1"
- summary="pointer constraint already requested on that surface"/>
- </enum>
-
- <enum name="lifetime">
- <description summary="constraint lifetime">
- These values represent different lifetime semantics. They are passed
- as arguments to the factory requests to specify how the constraint
- lifetimes should be managed.
- </description>
- <entry name="oneshot" value="1">
- <description summary="the pointer constraint is defunct once deactivated">
- A oneshot pointer constraint will never reactivate once it has been
- deactivated. See the corresponding deactivation event
- (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
- details.
- </description>
- </entry>
- <entry name="persistent" value="2">
- <description summary="the pointer constraint may reactivate">
- A persistent pointer constraint may again reactivate once it has
- been deactivated. See the corresponding deactivation event
- (wp_locked_pointer.unlocked and wp_confined_pointer.unconfined) for
- details.
- </description>
- </entry>
- </enum>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the pointer constraints manager object">
- Used by the client to notify the server that it will no longer use this
- pointer constraints object.
- </description>
- </request>
-
- <request name="lock_pointer">
- <description summary="lock pointer to a position">
- The lock_pointer request lets the client request to disable movements of
- the virtual pointer (i.e. the cursor), effectively locking the pointer
- to a position. This request may not take effect immediately; in the
- future, when the compositor deems implementation-specific constraints
- are satisfied, the pointer lock will be activated and the compositor
- sends a locked event.
-
- The protocol provides no guarantee that the constraints are ever
- satisfied, and does not require the compositor to send an error if the
- constraints cannot ever be satisfied. It is thus possible to request a
- lock that will never activate.
-
- There may not be another pointer constraint of any kind requested or
- active on the surface for any of the wl_pointer objects of the seat of
- the passed pointer when requesting a lock. If there is, an error will be
- raised. See general pointer lock documentation for more details.
-
- The intersection of the region passed with this request and the input
- region of the surface is used to determine where the pointer must be
- in order for the lock to activate. It is up to the compositor whether to
- warp the pointer or require some kind of user interaction for the lock
- to activate. If the region is null the surface input region is used.
-
- A surface may receive pointer focus without the lock being activated.
-
- The request creates a new object wp_locked_pointer which is used to
- interact with the lock as well as receive updates about its state. See
- the the description of wp_locked_pointer for further information.
-
- Note that while a pointer is locked, the wl_pointer objects of the
- corresponding seat will not emit any wl_pointer.motion events, but
- relative motion events will still be emitted via wp_relative_pointer
- objects of the same seat. wl_pointer.axis and wl_pointer.button events
- are unaffected.
- </description>
- <arg name="id" type="new_id" interface="zwp_locked_pointer_v1"/>
- <arg name="surface" type="object" interface="wl_surface"
- summary="surface to lock pointer to"/>
- <arg name="pointer" type="object" interface="wl_pointer"
- summary="the pointer that should be locked"/>
- <arg name="region" type="object" interface="wl_region" allow-null="true"
- summary="region of surface"/>
- <arg name="lifetime" type="uint" enum="lifetime" summary="lock lifetime"/>
- </request>
-
- <request name="confine_pointer">
- <description summary="confine pointer to a region">
- The confine_pointer request lets the client request to confine the
- pointer cursor to a given region. This request may not take effect
- immediately; in the future, when the compositor deems implementation-
- specific constraints are satisfied, the pointer confinement will be
- activated and the compositor sends a confined event.
-
- The intersection of the region passed with this request and the input
- region of the surface is used to determine where the pointer must be
- in order for the confinement to activate. It is up to the compositor
- whether to warp the pointer or require some kind of user interaction for
- the confinement to activate. If the region is null the surface input
- region is used.
-
- The request will create a new object wp_confined_pointer which is used
- to interact with the confinement as well as receive updates about its
- state. See the the description of wp_confined_pointer for further
- information.
- </description>
- <arg name="id" type="new_id" interface="zwp_confined_pointer_v1"/>
- <arg name="surface" type="object" interface="wl_surface"
- summary="surface to lock pointer to"/>
- <arg name="pointer" type="object" interface="wl_pointer"
- summary="the pointer that should be confined"/>
- <arg name="region" type="object" interface="wl_region" allow-null="true"
- summary="region of surface"/>
- <arg name="lifetime" type="uint" enum="lifetime" summary="confinement lifetime"/>
- </request>
- </interface>
-
- <interface name="zwp_locked_pointer_v1" version="1">
- <description summary="receive relative pointer motion events">
- The wp_locked_pointer interface represents a locked pointer state.
-
- While the lock of this object is active, the wl_pointer objects of the
- associated seat will not emit any wl_pointer.motion events.
-
- This object will send the event 'locked' when the lock is activated.
- Whenever the lock is activated, it is guaranteed that the locked surface
- will already have received pointer focus and that the pointer will be
- within the region passed to the request creating this object.
-
- To unlock the pointer, send the destroy request. This will also destroy
- the wp_locked_pointer object.
-
- If the compositor decides to unlock the pointer the unlocked event is
- sent. See wp_locked_pointer.unlock for details.
-
- When unlocking, the compositor may warp the cursor position to the set
- cursor position hint. If it does, it will not result in any relative
- motion events emitted via wp_relative_pointer.
-
- If the surface the lock was requested on is destroyed and the lock is not
- yet activated, the wp_locked_pointer object is now defunct and must be
- destroyed.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the locked pointer object">
- Destroy the locked pointer object. If applicable, the compositor will
- unlock the pointer.
- </description>
- </request>
-
- <request name="set_cursor_position_hint">
- <description summary="set the pointer cursor position hint">
- Set the cursor position hint relative to the top left corner of the
- surface.
-
- If the client is drawing its own cursor, it should update the position
- hint to the position of its own cursor. A compositor may use this
- information to warp the pointer upon unlock in order to avoid pointer
- jumps.
-
- The cursor position hint is double buffered. The new hint will only take
- effect when the associated surface gets it pending state applied. See
- wl_surface.commit for details.
- </description>
- <arg name="surface_x" type="fixed"
- summary="surface-local x coordinate"/>
- <arg name="surface_y" type="fixed"
- summary="surface-local y coordinate"/>
- </request>
-
- <request name="set_region">
- <description summary="set a new lock region">
- Set a new region used to lock the pointer.
-
- The new lock region is double-buffered. The new lock region will
- only take effect when the associated surface gets its pending state
- applied. See wl_surface.commit for details.
-
- For details about the lock region, see wp_locked_pointer.
- </description>
- <arg name="region" type="object" interface="wl_region" allow-null="true"
- summary="region of surface"/>
- </request>
-
- <event name="locked">
- <description summary="lock activation event">
- Notification that the pointer lock of the seat's pointer is activated.
- </description>
- </event>
-
- <event name="unlocked">
- <description summary="lock deactivation event">
- Notification that the pointer lock of the seat's pointer is no longer
- active. If this is a oneshot pointer lock (see
- wp_pointer_constraints.lifetime) this object is now defunct and should
- be destroyed. If this is a persistent pointer lock (see
- wp_pointer_constraints.lifetime) this pointer lock may again
- reactivate in the future.
- </description>
- </event>
- </interface>
-
- <interface name="zwp_confined_pointer_v1" version="1">
- <description summary="confined pointer object">
- The wp_confined_pointer interface represents a confined pointer state.
-
- This object will send the event 'confined' when the confinement is
- activated. Whenever the confinement is activated, it is guaranteed that
- the surface the pointer is confined to will already have received pointer
- focus and that the pointer will be within the region passed to the request
- creating this object. It is up to the compositor to decide whether this
- requires some user interaction and if the pointer will warp to within the
- passed region if outside.
-
- To unconfine the pointer, send the destroy request. This will also destroy
- the wp_confined_pointer object.
-
- If the compositor decides to unconfine the pointer the unconfined event is
- sent. The wp_confined_pointer object is at this point defunct and should
- be destroyed.
- </description>
-
- <request name="destroy" type="destructor">
- <description summary="destroy the confined pointer object">
- Destroy the confined pointer object. If applicable, the compositor will
- unconfine the pointer.
- </description>
- </request>
-
- <request name="set_region">
- <description summary="set a new confine region">
- Set a new region used to confine the pointer.
-
- The new confine region is double-buffered. The new confine region will
- only take effect when the associated surface gets its pending state
- applied. See wl_surface.commit for details.
-
- If the confinement is active when the new confinement region is applied
- and the pointer ends up outside of newly applied region, the pointer may
- warped to a position within the new confinement region. If warped, a
- wl_pointer.motion event will be emitted, but no
- wp_relative_pointer.relative_motion event.
-
- The compositor may also, instead of using the new region, unconfine the
- pointer.
-
- For details about the confine region, see wp_confined_pointer.
- </description>
- <arg name="region" type="object" interface="wl_region" allow-null="true"
- summary="region of surface"/>
- </request>
-
- <event name="confined">
- <description summary="pointer confined">
- Notification that the pointer confinement of the seat's pointer is
- activated.
- </description>
- </event>
-
- <event name="unconfined">
- <description summary="pointer unconfined">
- Notification that the pointer confinement of the seat's pointer is no
- longer active. If this is a oneshot pointer confinement (see
- wp_pointer_constraints.lifetime) this object is now defunct and should
- be destroyed. If this is a persistent pointer confinement (see
- wp_pointer_constraints.lifetime) this pointer confinement may again
- reactivate in the future.
- </description>
- </event>
- </interface>
-
-</protocol>
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index 3d441a95..3e9cf24b 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -14,6 +14,7 @@
#include <ranges>
#include "helpers/VarList.hpp"
#include "protocols/FractionalScale.hpp"
+#include "protocols/PointerConstraints.hpp"
int handleCritSignal(int signo, void* data) {
Debug::log(LOG, "Hyprland received signal {}", signo);
@@ -211,8 +212,6 @@ void CCompositor::initServer() {
m_sWLROutputMgr = wlr_output_manager_v1_create(m_sWLDisplay);
- m_sWLRPointerConstraints = wlr_pointer_constraints_v1_create(m_sWLDisplay);
-
m_sWLRVKeyboardMgr = wlr_virtual_keyboard_manager_v1_create(m_sWLDisplay);
m_sWLRVirtPtrMgr = wlr_virtual_pointer_manager_v1_create(m_sWLDisplay);
@@ -281,7 +280,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_sWLRPointerConstraints->events.new_constraint, &Events::listen_newConstraint, m_sWLRPointerConstraints, "PointerConstraints");
addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
addWLSignal(&m_sWLRVKeyboardMgr->events.new_virtual_keyboard, &Events::listen_newVirtualKeyboard, m_sWLRVKeyboardMgr, "VKeyboardMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
@@ -328,7 +326,6 @@ void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_change);
removeWLSignal(&Events::listen_outputMgrApply);
removeWLSignal(&Events::listen_outputMgrTest);
- removeWLSignal(&Events::listen_newConstraint);
removeWLSignal(&Events::listen_newVirtPtr);
removeWLSignal(&Events::listen_newVirtualKeyboard);
removeWLSignal(&Events::listen_RendererDestroy);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index c0845664..34086466 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -62,7 +62,6 @@ class CCompositor {
wlr_presentation* m_sWLRPresentation;
wlr_egl* m_sWLREGL;
int m_iDRMFD;
- wlr_pointer_constraints_v1* m_sWLRPointerConstraints;
wlr_server_decoration_manager* m_sWLRServerDecoMgr;
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
wlr_tablet_manager_v2* m_sWLRTabletManager;
diff --git a/src/desktop/Constraint.cpp b/src/desktop/Constraint.cpp
deleted file mode 100644
index 428c8745..00000000
--- a/src/desktop/Constraint.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-#include "Constraint.hpp"
-#include "WLSurface.hpp"
-#include "../Compositor.hpp"
-#include "../config/ConfigValue.hpp"
-
-CConstraint::CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner) : m_pOwner(owner), m_pConstraint(constraint) {
- RASSERT(!constraint->data, "CConstraint: attempted to duplicate ownership");
-
- constraint->data = this;
- initSignals();
-
- m_vCursorPosOnActivate = g_pInputManager->getMouseCoordsInternal();
-
- g_pInputManager->m_vConstraints.push_back(this);
-
- if (g_pCompositor->m_pLastFocus == m_pOwner->wlr())
- activate();
-}
-
-CConstraint::~CConstraint() {
- std::erase(g_pInputManager->m_vConstraints, this);
-}
-
-static void onConstraintDestroy(void* owner, void* data) {
- const auto CONSTRAINT = (CConstraint*)owner;
- CONSTRAINT->onDestroy();
-}
-
-static void onConstraintSetRegion(void* owner, void* data) {
- const auto CONSTRAINT = (CConstraint*)owner;
- CONSTRAINT->onSetRegion();
-}
-
-void CConstraint::initSignals() {
- hyprListener_setConstraintRegion.initCallback(&m_pConstraint->events.set_region, ::onConstraintSetRegion, this, "CConstraint");
- hyprListener_destroyConstraint.initCallback(&m_pConstraint->events.destroy, ::onConstraintDestroy, this, "CConstraint");
-}
-
-void CConstraint::onDestroy() {
- hyprListener_setConstraintRegion.removeCallback();
- hyprListener_destroyConstraint.removeCallback();
-
- if (active() && isLocked())
- g_pCompositor->warpCursorTo(logicPositionHint(), true);
-
- // this is us
- m_pOwner->m_pConstraint.reset();
-}
-
-void CConstraint::onSetRegion() {
- if (!m_bActive)
- return;
-
- m_rRegion.set(&m_pConstraint->region);
- m_vPositionHint = m_rRegion.closestPoint(m_vPositionHint);
- g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss
-}
-
-void CConstraint::onCommit() {
- if (!m_bActive)
- return;
-
- const auto COMMITTED = m_pConstraint->current.committed;
-
- if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
- static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
-
- m_bHintSet = true;
-
- float scale = 1.f;
- const auto PWINDOW = m_pOwner->getWindow();
- if (PWINDOW) {
- const auto ISXWL = PWINDOW->m_bIsX11;
- scale = ISXWL && *PXWLFORCESCALEZERO ? PWINDOW->m_fX11SurfaceScaledBy : 1.f;
- }
-
- m_vPositionHint = {m_pConstraint->current.cursor_hint.x / scale, m_pConstraint->current.cursor_hint.y / scale};
- g_pInputManager->simulateMouseMovement();
- }
-
- if (COMMITTED & WLR_POINTER_CONSTRAINT_V1_STATE_REGION)
- onSetRegion();
-}
-
-CRegion CConstraint::logicConstraintRegion() {
- CRegion rg = m_rRegion;
- const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
- const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
- rg.translate(CONSTRAINTPOS);
- return rg;
-}
-
-CWLSurface* CConstraint::owner() {
- return m_pOwner;
-}
-
-bool CConstraint::isLocked() {
- return m_pConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
-}
-
-Vector2D CConstraint::logicPositionHint() {
- const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
- const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
-
- return m_bHintSet ? CONSTRAINTPOS + m_vPositionHint : m_vCursorPosOnActivate;
-}
-
-void CConstraint::deactivate() {
- if (!m_bActive)
- return;
-
- m_bActive = false;
-
- if (isLocked())
- g_pCompositor->warpCursorTo(logicPositionHint(), true);
-
- if (m_pConstraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT)
- m_bDead = true;
-
- wlr_pointer_constraint_v1_send_deactivated(m_pConstraint);
-}
-
-void CConstraint::activate() {
- if (m_bActive || m_bDead)
- return;
-
- m_bActive = true;
-
- // TODO: hack, probably not a super duper great idea
- if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != m_pOwner->wlr()) {
- const auto SURFBOX = m_pOwner->getSurfaceBoxGlobal();
- const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
- wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, m_pOwner->wlr(), LOCAL.x, LOCAL.y);
- }
-
- g_pCompositor->warpCursorTo(logicPositionHint(), true);
- wlr_pointer_constraint_v1_send_activated(m_pConstraint);
-}
-
-bool CConstraint::active() {
- return m_bActive;
-}
diff --git a/src/desktop/Constraint.hpp b/src/desktop/Constraint.hpp
deleted file mode 100644
index 34690217..00000000
--- a/src/desktop/Constraint.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma once
-
-#include "../includes.hpp"
-#include "../helpers/Region.hpp"
-#include "../helpers/WLListener.hpp"
-
-class CWLSurface;
-
-class CConstraint {
- public:
- CConstraint(wlr_pointer_constraint_v1* constraint, CWLSurface* owner);
- ~CConstraint();
-
- void onCommit();
- void onDestroy();
- void onSetRegion();
- CRegion logicConstraintRegion();
- bool isLocked();
- Vector2D logicPositionHint();
-
- void deactivate();
- void activate();
- bool active();
-
- CWLSurface* owner();
-
- private:
- bool m_bActive = false;
- CWLSurface* m_pOwner = nullptr;
- wlr_pointer_constraint_v1* m_pConstraint;
-
- CRegion m_rRegion;
- bool m_bHintSet = false;
- Vector2D m_vPositionHint = {-1, -1};
- Vector2D m_vCursorPosOnActivate = {-1, -1};
-
- // for oneshot constraints that have been activated once
- bool m_bDead = false;
-
- DYNLISTENER(destroyConstraint);
- DYNLISTENER(setConstraintRegion);
-
- void initSignals();
-}; \ No newline at end of file
diff --git a/src/desktop/WLSurface.cpp b/src/desktop/WLSurface.cpp
index b102c5ce..4c2f6c84 100644
--- a/src/desktop/WLSurface.cpp
+++ b/src/desktop/WLSurface.cpp
@@ -103,6 +103,8 @@ void CWLSurface::destroy() {
if (!m_pWLRSurface)
return;
+ events.destroy.emit();
+
m_pConstraint.reset();
hyprListener_destroy.removeCallback();
@@ -182,15 +184,14 @@ std::optional<CBox> CWLSurface::getSurfaceBoxGlobal() {
return {};
}
-void CWLSurface::appendConstraint(wlr_pointer_constraint_v1* constraint) {
- m_pConstraint = std::make_unique<CConstraint>(constraint, this);
+void CWLSurface::appendConstraint(std::weak_ptr<CPointerConstraint> constraint) {
+ m_pConstraint = constraint;
}
void CWLSurface::onCommit() {
- if (m_pConstraint)
- m_pConstraint->onCommit();
+ ;
}
-CConstraint* CWLSurface::constraint() {
- return m_pConstraint.get();
+std::shared_ptr<CPointerConstraint> CWLSurface::constraint() {
+ return m_pConstraint.lock();
}
diff --git a/src/desktop/WLSurface.hpp b/src/desktop/WLSurface.hpp
index a47b2bdd..d018d11f 100644
--- a/src/desktop/WLSurface.hpp
+++ b/src/desktop/WLSurface.hpp
@@ -2,12 +2,13 @@
#include "../defines.hpp"
#include "../helpers/Region.hpp"
-#include "Constraint.hpp"
+#include "../helpers/signal/Signal.hpp"
class CWindow;
struct SLayerSurface;
class CSubsurface;
class CPopup;
+class CPointerConstraint;
class CWLSurface {
public:
@@ -42,9 +43,9 @@ class CWLSurface {
CSubsurface* getSubsurface();
// desktop components misc utils
- std::optional<CBox> getSurfaceBoxGlobal();
- void appendConstraint(wlr_pointer_constraint_v1* constraint);
- CConstraint* constraint();
+ std::optional<CBox> getSurfaceBoxGlobal();
+ void appendConstraint(std::weak_ptr<CPointerConstraint> constraint);
+ std::shared_ptr<CPointerConstraint> constraint();
// allow stretching. Useful for plugins.
bool m_bFillIgnoreSmall = false;
@@ -84,6 +85,10 @@ class CWLSurface {
// used by the alpha-modifier protocol
float m_pAlphaModifier = 1.F;
+ struct {
+ CSignal destroy;
+ } events;
+
private:
bool m_bInert = true;
@@ -95,14 +100,14 @@ class CWLSurface {
CSubsurface* m_pSubsurfaceOwner = nullptr;
//
- std::unique_ptr<CConstraint> m_pConstraint;
+ std::weak_ptr<CPointerConstraint> m_pConstraint;
- void destroy();
- void init();
- bool desktopComponent();
+ void destroy();
+ void init();
+ bool desktopComponent();
DYNLISTENER(destroy);
DYNLISTENER(commit);
- friend class CConstraint;
+ friend class CPointerConstraint;
}; \ No newline at end of file
diff --git a/src/events/Devices.cpp b/src/events/Devices.cpp
index de5de7f8..43433ba2 100644
--- a/src/events/Devices.cpp
+++ b/src/events/Devices.cpp
@@ -93,21 +93,6 @@ void Events::listener_newInput(wl_listener* listener, void* data) {
g_pInputManager->updateCapabilities();
}
-void Events::listener_newConstraint(wl_listener* listener, void* data) {
- const auto PCONSTRAINT = (wlr_pointer_constraint_v1*)data;
-
- Debug::log(LOG, "New mouse constraint at {:x}", (uintptr_t)PCONSTRAINT);
-
- const auto SURFACE = CWLSurface::surfaceFromWlr(PCONSTRAINT->surface);
-
- if (!SURFACE) {
- Debug::log(ERR, "Refusing a constraint from an unassigned wl_surface {:x}", (uintptr_t)PCONSTRAINT->surface);
- return;
- }
-
- SURFACE->appendConstraint(PCONSTRAINT);
-}
-
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;
diff --git a/src/events/Events.hpp b/src/events/Events.hpp
index 69ca82d0..2f52f11d 100644
--- a/src/events/Events.hpp
+++ b/src/events/Events.hpp
@@ -65,8 +65,6 @@ namespace Events {
DYNLISTENFUNC(keyboardMod);
DYNLISTENFUNC(keyboardDestroy);
- LISTENER(newConstraint);
-
// Various
LISTENER(requestMouse);
LISTENER(requestSetSel);
diff --git a/src/helpers/Region.cpp b/src/helpers/Region.cpp
index 502bc198..bcbf672d 100644
--- a/src/helpers/Region.cpp
+++ b/src/helpers/Region.cpp
@@ -8,7 +8,7 @@ CRegion::CRegion() {
pixman_region32_init(&m_rRegion);
}
-CRegion::CRegion(pixman_region32_t* ref) {
+CRegion::CRegion(const pixman_region32_t* const ref) {
pixman_region32_init(&m_rRegion);
pixman_region32_copy(&m_rRegion, ref);
}
diff --git a/src/helpers/Region.hpp b/src/helpers/Region.hpp
index 7ae334f7..27f460f4 100644
--- a/src/helpers/Region.hpp
+++ b/src/helpers/Region.hpp
@@ -11,7 +11,7 @@ class CRegion {
/* Create an empty region */
CRegion();
/* Create from a reference. Copies, does not own. */
- CRegion(pixman_region32_t* ref);
+ CRegion(const pixman_region32_t* const ref);
/* Create from a box */
CRegion(double x, double y, double w, double h);
/* Create from a wlr_box */
diff --git a/src/includes.hpp b/src/includes.hpp
index 1935a268..7f679f97 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -80,7 +80,6 @@ extern "C" {
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_texture.h>
-#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/types/wlr_xdg_foreign_registry.h>
#include <wlr/types/wlr_xdg_foreign_v1.h>
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index 7d93d46a..cffc6c83 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -14,6 +14,7 @@
#include "../protocols/ForeignToplevelWlr.hpp"
#include "../protocols/ShortcutsInhibit.hpp"
#include "../protocols/TextInputV3.hpp"
+#include "../protocols/PointerConstraints.hpp"
#include "tearing-control-v1.hpp"
#include "fractional-scale-v1.hpp"
@@ -29,6 +30,7 @@
#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
#include "keyboard-shortcuts-inhibit-unstable-v1.hpp"
#include "text-input-unstable-v3.hpp"
+#include "pointer-constraints-unstable-v1.hpp"
CProtocolManager::CProtocolManager() {
@@ -46,6 +48,7 @@ CProtocolManager::CProtocolManager() {
PROTO::foreignToplevelWlr = std::make_unique<CForeignToplevelWlrProtocol>(&zwlr_foreign_toplevel_manager_v1_interface, 3, "ForeignToplevelWlr");
PROTO::shortcutsInhibit = std::make_unique<CKeyboardShortcutsInhibitProtocol>(&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1, "ShortcutsInhibit");
PROTO::textInputV3 = std::make_unique<CTextInputV3Protocol>(&zwp_text_input_manager_v3_interface, 1, "TextInputV3");
+ PROTO::constraints = std::make_unique<CPointerConstraintsProtocol>(&zwp_pointer_constraints_v1_interface, 1, "PointerConstraints");
// 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 b5d6bb5c..b930838b 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -7,6 +7,7 @@
#include "../../protocols/CursorShape.hpp"
#include "../../protocols/IdleInhibit.hpp"
#include "../../protocols/RelativePointer.hpp"
+#include "../../protocols/PointerConstraints.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
@@ -193,7 +194,7 @@ void CInputManager::mouseMoveUnified(uint32_t time, bool refocus) {
return;
} else
- Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT);
+ Debug::log(ERR, "BUG THIS: Null SURF/CONSTRAINT in mouse refocus. Ignoring constraints. {:x} {:x}", (uintptr_t)SURF, (uintptr_t)CONSTRAINT.get());
}
// update stuff
@@ -1298,16 +1299,26 @@ void CInputManager::unconstrainMouse() {
return;
for (auto& c : m_vConstraints) {
- if (!c->active())
+ const auto C = c.lock();
+
+ if (!C)
+ continue;
+
+ if (!C->isActive())
continue;
- c->deactivate();
+ C->deactivate();
}
}
bool CInputManager::isConstrained() {
for (auto& c : m_vConstraints) {
- if (!c->active() || c->owner()->wlr() != g_pCompositor->m_pLastFocus)
+ const auto C = c.lock();
+
+ if (!C)
+ continue;
+
+ if (!C->isActive() || C->owner()->wlr() != g_pCompositor->m_pLastFocus)
continue;
return true;
diff --git a/src/managers/input/InputManager.hpp b/src/managers/input/InputManager.hpp
index bb5d1cf7..2795d410 100644
--- a/src/managers/input/InputManager.hpp
+++ b/src/managers/input/InputManager.hpp
@@ -8,7 +8,7 @@
#include "InputMethodRelay.hpp"
#include "../../helpers/signal/Listener.hpp"
-class CConstraint;
+class CPointerConstraint;
class CWindow;
class CIdleInhibitor;
@@ -141,27 +141,28 @@ class CInputManager {
std::deque<SLayerSurface*> m_dExclusiveLSes;
// constraints
- std::vector<CConstraint*> m_vConstraints;
+ std::vector<std::weak_ptr<CPointerConstraint>> m_vConstraints;
- void newTabletTool(wlr_input_device*);
- void newTabletPad(wlr_input_device*);
- void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
- void newIdleInhibitor(std::any);
- void recheckIdleInhibitorStatus();
+ //
+ void newTabletTool(wlr_input_device*);
+ void newTabletPad(wlr_input_device*);
+ void focusTablet(STablet*, wlr_tablet_tool*, bool motion = false);
+ void newIdleInhibitor(std::any);
+ void recheckIdleInhibitorStatus();
- void onSwipeBegin(wlr_pointer_swipe_begin_event*);
- void onSwipeEnd(wlr_pointer_swipe_end_event*);
- void onSwipeUpdate(wlr_pointer_swipe_update_event*);
+ void onSwipeBegin(wlr_pointer_swipe_begin_event*);
+ void onSwipeEnd(wlr_pointer_swipe_end_event*);
+ void onSwipeUpdate(wlr_pointer_swipe_update_event*);
- SSwipeGesture m_sActiveSwipe;
+ SSwipeGesture m_sActiveSwipe;
- SKeyboard* m_pActiveKeyboard = nullptr;
+ SKeyboard* m_pActiveKeyboard = nullptr;
- CTimer m_tmrLastCursorMovement;
+ CTimer m_tmrLastCursorMovement;
- CInputMethodRelay m_sIMERelay;
+ CInputMethodRelay m_sIMERelay;
- void updateKeyboardsLeds(wlr_input_device* pKeyboard);
+ void updateKeyboardsLeds(wlr_input_device* pKeyboard);
// for shared mods
uint32_t accumulateModsFromAllKBs();
diff --git a/src/protocols/PointerConstraints.cpp b/src/protocols/PointerConstraints.cpp
new file mode 100644
index 00000000..ab228549
--- /dev/null
+++ b/src/protocols/PointerConstraints.cpp
@@ -0,0 +1,260 @@
+#include "PointerConstraints.hpp"
+#include "../desktop/WLSurface.hpp"
+#include "../Compositor.hpp"
+#include "../config/ConfigValue.hpp"
+
+#define LOGM PROTO::constraints->protoLog
+
+CPointerConstraint::CPointerConstraint(SP<CZwpLockedPointerV1> resource_, wlr_surface* surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) :
+ resourceL(resource_), locked(true) {
+ if (!resource_->resource())
+ return;
+
+ resource_->setOnDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); });
+ resource_->setDestroy([this](CZwpLockedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); });
+
+ pHLSurface = CWLSurface::surfaceFromWlr(surf);
+
+ if (!pHLSurface)
+ return;
+
+ if (region_)
+ region.set(wlr_region_from_resource(region_));
+
+ resource_->setSetRegion([this](CZwpLockedPointerV1* p, wl_resource* region) { onSetRegion(region); });
+ resource_->setSetCursorPositionHint([this](CZwpLockedPointerV1* p, wl_fixed_t x, wl_fixed_t y) {
+ static auto PXWLFORCESCALEZERO = CConfigValue<Hyprlang::INT>("xwayland:force_zero_scaling");
+
+ if (!pHLSurface)
+ return;
+
+ hintSet = true;
+
+ float scale = 1.f;
+ const auto PWINDOW = pHLSurface->getWindow();
+ if (PWINDOW) {
+ const auto ISXWL = PWINDOW->m_bIsX11;
+ scale = ISXWL && *PXWLFORCESCALEZERO ? PWINDOW->m_fX11SurfaceScaledBy : 1.f;
+ }
+
+ positionHint = {wl_fixed_to_double(x) / scale, wl_fixed_to_double(y) / scale};
+ g_pInputManager->simulateMouseMovement();
+ });
+
+ sharedConstructions();
+}
+
+CPointerConstraint::CPointerConstraint(SP<CZwpConfinedPointerV1> resource_, wlr_surface* surf, wl_resource* region_, zwpPointerConstraintsV1Lifetime lifetime) :
+ resourceC(resource_), locked(false) {
+ if (!resource_->resource())
+ return;
+
+ resource_->setOnDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); });
+ resource_->setDestroy([this](CZwpConfinedPointerV1* p) { PROTO::constraints->destroyPointerConstraint(this); });
+
+ pHLSurface = CWLSurface::surfaceFromWlr(surf);
+
+ if (!pHLSurface)
+ return;
+
+ if (region_)
+ region.set(wlr_region_from_resource(region_));
+
+ resource_->setSetRegion([this](CZwpConfinedPointerV1* p, wl_resource* region) { onSetRegion(region); });
+
+ sharedConstructions();
+}
+
+CPointerConstraint::~CPointerConstraint() {
+ std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) {
+ const auto SHP = c.lock();
+ return !SHP || SHP.get() == this;
+ });
+
+ if (pHLSurface)
+ pHLSurface->m_pConstraint.reset();
+}
+
+void CPointerConstraint::sharedConstructions() {
+ if (pHLSurface) {
+ listeners.destroySurface = pHLSurface->events.destroy.registerListener([this](std::any d) {
+ pHLSurface = nullptr;
+ if (active)
+ deactivate();
+
+ std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) {
+ const auto SHP = c.lock();
+ return !SHP || SHP.get() == this;
+ });
+ });
+ }
+
+ cursorPosOnActivate = g_pInputManager->getMouseCoordsInternal();
+
+ if (g_pCompositor->m_pLastFocus == pHLSurface->wlr())
+ activate();
+}
+
+bool CPointerConstraint::good() {
+ return locked ? resourceL->resource() : resourceC->resource();
+}
+
+void CPointerConstraint::deactivate() {
+ if (!active)
+ return;
+
+ if (locked)
+ resourceL->sendUnlocked();
+ else
+ resourceC->sendUnconfined();
+
+ active = false;
+
+ if (locked)
+ g_pCompositor->warpCursorTo(logicPositionHint(), true);
+
+ if (lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) {
+ dead = true;
+ // remove from inputmgr
+ std::erase_if(g_pInputManager->m_vConstraints, [this](const auto& c) {
+ const auto SHP = c.lock();
+ return !SHP || SHP.get() == this;
+ });
+ }
+}
+
+void CPointerConstraint::activate() {
+ if (dead || active)
+ return;
+
+ // TODO: hack, probably not a super duper great idea
+ if (g_pCompositor->m_sSeat.seat->pointer_state.focused_surface != pHLSurface->wlr()) {
+ const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal();
+ const auto LOCAL = SURFBOX.has_value() ? logicPositionHint() - SURFBOX->pos() : Vector2D{};
+ wlr_seat_pointer_enter(g_pCompositor->m_sSeat.seat, pHLSurface->wlr(), LOCAL.x, LOCAL.y);
+ }
+
+ g_pCompositor->warpCursorTo(logicPositionHint(), true);
+
+ if (locked)
+ resourceL->sendLocked();
+ else
+ resourceC->sendConfined();
+
+ active = true;
+
+ g_pInputManager->simulateMouseMovement();
+}
+
+bool CPointerConstraint::isActive() {
+ return active;
+}
+
+void CPointerConstraint::onSetRegion(wl_resource* wlRegion) {
+ if (!wlRegion) {
+ region.clear();
+ return;
+ }
+
+ const auto REGION = wlr_region_from_resource(wlRegion);
+
+ region.set(REGION);
+ positionHint = region.closestPoint(positionHint);
+ g_pInputManager->simulateMouseMovement(); // to warp the cursor if anything's amiss
+}
+
+CWLSurface* CPointerConstraint::owner() {
+ return pHLSurface;
+}
+
+CRegion CPointerConstraint::logicConstraintRegion() {
+ CRegion rg = region;
+ const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal();
+ const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
+ rg.translate(CONSTRAINTPOS);
+ return rg;
+}
+
+bool CPointerConstraint::isLocked() {
+ return locked;
+}
+
+Vector2D CPointerConstraint::logicPositionHint() {
+ if (!pHLSurface)
+ return {};
+
+ const auto SURFBOX = pHLSurface->getSurfaceBoxGlobal();
+ const auto CONSTRAINTPOS = SURFBOX.has_value() ? SURFBOX->pos() : Vector2D{};
+
+ return hintSet ? CONSTRAINTPOS + positionHint : (locked ? CONSTRAINTPOS + SURFBOX->size() / 2.f : cursorPosOnActivate);
+}
+
+CPointerConstraintsProtocol::CPointerConstraintsProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CPointerConstraintsProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(std::make_unique<CZwpPointerConstraintsV1>(client, ver, id)).get();
+ RESOURCE->setOnDestroy([this](CZwpPointerConstraintsV1* p) { this->onManagerResourceDestroy(p->resource()); });
+
+ RESOURCE->setDestroy([this](CZwpPointerConstraintsV1* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
+ RESOURCE->setConfinePointer([this](CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region,
+ zwpPointerConstraintsV1Lifetime lifetime) { this->onConfinePointer(pMgr, id, surface, pointer, region, lifetime); });
+ RESOURCE->setLockPointer([this](CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region,
+ zwpPointerConstraintsV1Lifetime lifetime) { this->onLockPointer(pMgr, id, surface, pointer, region, lifetime); });
+}
+
+void CPointerConstraintsProtocol::onManagerResourceDestroy(wl_resource* res) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
+}
+
+void CPointerConstraintsProtocol::destroyPointerConstraint(CPointerConstraint* hyprland🥚) {
+ std::erase_if(m_vConstraints, [&](const auto& other) { return other.get() == hyprland🥚; });
+}
+
+void CPointerConstraintsProtocol::onNewConstraint(SP<CPointerConstraint> constraint, CZwpPointerConstraintsV1* pMgr) {
+ if (!constraint->good()) {
+ LOGM(ERR, "Couldn't create constraint??");
+ wl_resource_post_no_memory(pMgr->resource());
+ m_vConstraints.pop_back();
+ return;
+ }
+
+ if (!constraint->owner()) {
+ LOGM(ERR, "New constraint has no CWLSurface owner??");
+ return;
+ }
+
+ const auto OWNER = constraint->owner();
+
+ const auto DUPES = std::count_if(m_vConstraints.begin(), m_vConstraints.end(), [OWNER](const auto& c) { return c->owner() == OWNER; });
+
+ if (DUPES > 1) {
+ LOGM(ERR, "Constraint for surface duped");
+ wl_resource_post_error(pMgr->resource(), ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, "Surface already confined");
+ m_vConstraints.pop_back();
+ return;
+ }
+
+ OWNER->appendConstraint(constraint);
+
+ g_pInputManager->m_vConstraints.push_back(constraint);
+}
+
+void CPointerConstraintsProtocol::onLockPointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region,
+ zwpPointerConstraintsV1Lifetime lifetime) {
+ const auto CLIENT = wl_resource_get_client(pMgr->resource());
+ const auto RESOURCE = m_vConstraints.emplace_back(std::make_shared<CPointerConstraint>(
+ std::make_shared<CZwpLockedPointerV1>(CLIENT, wl_resource_get_version(pMgr->resource()), id), wlr_surface_from_resource(surface), region, lifetime));
+
+ onNewConstraint(RESOURCE, pMgr);
+}
+
+void CPointerConstraintsProtocol::onConfinePointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region,
+ zwpPointerConstraintsV1Lifetime lifetime) {
+ const auto CLIENT = wl_resource_get_client(pMgr->resource());
+ const auto RESOURCE = m_vConstraints.emplace_back(std::make_shared<CPointerConstraint>(
+ std::make_shared<CZwpConfinedPointerV1>(CLIENT, wl_resource_get_version(pMgr->resource()), id), wlr_surface_from_resource(surface), region, lifetime));
+
+ onNewConstraint(RESOURCE, pMgr);
+}
diff --git a/src/protocols/PointerConstraints.hpp b/src/protocols/PointerConstraints.hpp
new file mode 100644
index 00000000..93e57c46
--- /dev/null
+++ b/src/protocols/PointerConstraints.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include <cstdint>
+#include "WaylandProtocol.hpp"
+#include "pointer-constraints-unstable-v1.hpp"
+#include "../helpers/Vector2D.hpp"
+#include "../helpers/Region.hpp"
+#include "../helpers/signal/Listener.hpp"
+
+class CWLSurface;
+
+class CPointerConstraint {
+ public:
+ CPointerConstraint(SP<CZwpLockedPointerV1> resource_, wlr_surface* surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime);
+ CPointerConstraint(SP<CZwpConfinedPointerV1> resource_, wlr_surface* surf, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime);
+ ~CPointerConstraint();
+
+ bool good();
+
+ void deactivate();
+ void activate();
+ bool isActive();
+
+ CWLSurface* owner();
+
+ CRegion logicConstraintRegion();
+ bool isLocked();
+ Vector2D logicPositionHint();
+
+ private:
+ SP<CZwpLockedPointerV1> resourceL;
+ SP<CZwpConfinedPointerV1> resourceC;
+ wl_client* pClient = nullptr;
+
+ CWLSurface* pHLSurface = nullptr;
+
+ CRegion region;
+ bool hintSet = false;
+ Vector2D positionHint = {-1, -1};
+ Vector2D cursorPosOnActivate = {-1, -1};
+ bool active = false;
+ bool locked = false;
+ bool dead = false;
+ zwpPointerConstraintsV1Lifetime lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT;
+
+ void sharedConstructions();
+ void onSetRegion(wl_resource* region);
+
+ struct {
+ CHyprSignalListener destroySurface;
+ } listeners;
+};
+
+class CPointerConstraintsProtocol : public IWaylandProtocol {
+ public:
+ CPointerConstraintsProtocol(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);
+
+ private:
+ void onManagerResourceDestroy(wl_resource* res);
+ void destroyPointerConstraint(CPointerConstraint* constraint);
+ void onLockPointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime);
+ void onConfinePointer(CZwpPointerConstraintsV1* pMgr, uint32_t id, wl_resource* surface, wl_resource* pointer, wl_resource* region, zwpPointerConstraintsV1Lifetime lifetime);
+ void onNewConstraint(SP<CPointerConstraint> constraint, CZwpPointerConstraintsV1* pMgr);
+
+ //
+ std::vector<UP<CZwpPointerConstraintsV1>> m_vManagers;
+ std::vector<SP<CPointerConstraint>> m_vConstraints;
+
+ friend class CPointerConstraint;
+};
+
+namespace PROTO {
+ inline UP<CPointerConstraintsProtocol> constraints;
+}; \ No newline at end of file