From 4f5f88b06f154ad6896b9e826b66c884858e7172 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Thu, 24 Sep 2026 06:07:25 +0100 Subject: [PATCH] feat: enforce pointer constraints --- compositor/src/handlers/mod.rs | 38 +++++++++------- compositor/src/input.rs | 81 +++++++++++++++++++++++++++++++++- 2 files changed, 102 insertions(+), 17 deletions(-) diff --git a/compositor/src/handlers/mod.rs b/compositor/src/handlers/mod.rs index 47ba796..27e4803 100644 --- a/compositor/src/handlers/mod.rs +++ b/compositor/src/handlers/mod.rs @@ -12,7 +12,9 @@ use smithay::{ compositor::with_states, fractional_scale::{FractionalScaleHandler, with_fractional_scale}, output::OutputHandler, - pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint}, + pointer_constraints::{ + PointerConstraint, PointerConstraintsHandler, with_pointer_constraint, + }, selection::{ SelectionHandler, data_device::{ @@ -57,25 +59,31 @@ impl FractionalScaleHandler for Ferese { } impl PointerConstraintsHandler for Ferese { - fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle) { - let focused = pointer - .current_focus() - .is_some_and(|focused| focused == *surface); - if focused { - with_pointer_constraint(surface, pointer, |constraint| { - if let Some(constraint) = constraint { - constraint.activate(); - } - }); - } + fn new_constraint(&mut self, _surface: &WlSurface, pointer: &PointerHandle) { + self.activate_focused_pointer_constraint(pointer); } fn cursor_position_hint( &mut self, - _surface: &WlSurface, - _pointer: &PointerHandle, - _location: smithay::utils::Point, + surface: &WlSurface, + pointer: &PointerHandle, + location: smithay::utils::Point, ) { + let locked = with_pointer_constraint(surface, pointer, |constraint| { + constraint.is_some_and(|constraint| { + constraint.is_active() && matches!(&*constraint, PointerConstraint::Locked(_)) + }) + }); + if !locked || pointer.current_focus().as_ref() != Some(surface) { + return; + } + + let Some((focused_surface, origin)) = self.surface_under(pointer.current_location()) else { + return; + }; + if focused_surface == *surface { + pointer.set_location(origin + location); + } } } diff --git a/compositor/src/input.rs b/compositor/src/input.rs index 856b1bb..a68fd8a 100644 --- a/compositor/src/input.rs +++ b/compositor/src/input.rs @@ -5,10 +5,11 @@ use smithay::{ }, input::{ keyboard::FilterResult, - pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, + pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerHandle, RelativeMotionEvent}, }, reexports::wayland_server::protocol::wl_surface::WlSurface, - utils::{SERIAL_COUNTER, Serial}, + utils::{Logical, Point, SERIAL_COUNTER, Serial}, + wayland::pointer_constraints::{PointerConstraint, with_pointer_constraint}, }; use crate::Ferese; @@ -36,6 +37,8 @@ impl Ferese { }; let position = event.position_transformed(geometry.size) + geometry.loc.to_f64(); let pointer = self.seat.get_pointer().expect("seat has a pointer"); + let position = self.constrain_pointer_position(&pointer, position); + pointer.motion( self, self.surface_under(position), @@ -46,10 +49,12 @@ impl Ferese { }, ); pointer.frame(self); + self.activate_focused_pointer_constraint(&pointer); } InputEvent::PointerMotion { event, .. } => { let pointer = self.seat.get_pointer().expect("seat has a pointer"); let focus = self.surface_under(pointer.current_location()); + pointer.relative_motion( self, focus, @@ -64,9 +69,11 @@ impl Ferese { InputEvent::PointerButton { event, .. } => { let pointer = self.seat.get_pointer().expect("seat has a pointer"); let serial = SERIAL_COUNTER.next_serial(); + if event.state() == ButtonState::Pressed && !pointer.is_grabbed() { self.focus_window_under_pointer(serial); } + pointer.button( self, &ButtonEvent { @@ -93,12 +100,14 @@ impl Ferese { frame = frame.v120(Axis::Horizontal, value as i32); } } + if vertical != 0.0 { frame = frame.value(Axis::Vertical, vertical); if let Some(value) = event.amount_v120(Axis::Vertical) { frame = frame.v120(Axis::Vertical, value as i32); } } + if source == AxisSource::Finger { if event.amount(Axis::Horizontal) == Some(0.0) { frame = frame.stop(Axis::Horizontal); @@ -107,7 +116,9 @@ impl Ferese { frame = frame.stop(Axis::Vertical); } } + let pointer = self.seat.get_pointer().expect("seat has a pointer"); + pointer.axis(self, frame); pointer.frame(self); } @@ -118,6 +129,7 @@ impl Ferese { fn focus_window_under_pointer(&mut self, serial: Serial) { let pointer = self.seat.get_pointer().expect("seat has a pointer"); let keyboard = self.seat.get_keyboard().expect("seat has a keyboard"); + if let Some((window, _)) = self.space.element_under(pointer.current_location()) { let window = window.clone(); self.space.raise_element(&window, true); @@ -130,10 +142,75 @@ impl Ferese { } else { keyboard.set_focus(self, Option::::None, serial); } + self.space.elements().for_each(|window| { if let Some(toplevel) = window.toplevel() { toplevel.send_pending_configure(); } }); } + + fn constrain_pointer_position( + &self, + pointer: &PointerHandle, + requested: Point, + ) -> Point { + let current = pointer.current_location(); + let Some(focused_surface) = pointer.current_focus() else { + return requested; + }; + let Some((surface, origin)) = self.surface_under(current) else { + return requested; + }; + if surface != focused_surface { + return requested; + } + + with_pointer_constraint(&surface, pointer, |constraint| { + let Some(constraint) = constraint.filter(|constraint| constraint.is_active()) else { + return requested; + }; + + match &*constraint { + PointerConstraint::Locked(_) => current, + PointerConstraint::Confined(_) => { + let remains_on_surface = self + .surface_under(requested) + .is_some_and(|(candidate, _)| candidate == surface); + let inside_region = constraint + .region() + .is_none_or(|region| region.contains((requested - origin).to_i32_round())); + + if remains_on_surface && inside_region { + requested + } else { + current + } + } + } + }) + } + + pub fn activate_focused_pointer_constraint(&self, pointer: &PointerHandle) { + let position = pointer.current_location(); + let Some((surface, origin)) = self.surface_under(position) else { + return; + }; + if pointer.current_focus().as_ref() != Some(&surface) { + return; + } + + with_pointer_constraint(&surface, pointer, |constraint| { + let Some(constraint) = constraint else { + return; + }; + let inside_region = constraint + .region() + .is_none_or(|region| region.contains((position - origin).to_i32_round())); + + if !constraint.is_active() && inside_region { + constraint.activate(); + } + }); + } }