feat: enforce pointer constraints

This commit is contained in:
Samuel Onoja committed 2026-09-24 06:07:25 +01:00
1 parent 8fea6f85ee
commit 4f5f88b06f
2 files changed
+102 -17

No files matched your search

+23 -15
View File
@@ -12,7 +12,9 @@ use smithay::{
compositor::with_states, compositor::with_states,
fractional_scale::{FractionalScaleHandler, with_fractional_scale}, fractional_scale::{FractionalScaleHandler, with_fractional_scale},
output::OutputHandler, output::OutputHandler,
pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint}, pointer_constraints::{
PointerConstraint, PointerConstraintsHandler, with_pointer_constraint,
},
selection::{ selection::{
SelectionHandler, SelectionHandler,
data_device::{ data_device::{
@@ -57,25 +59,31 @@ impl FractionalScaleHandler for Ferese {
} }
impl PointerConstraintsHandler for Ferese { impl PointerConstraintsHandler for Ferese {
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) { fn new_constraint(&mut self, _surface: &WlSurface, pointer: &PointerHandle<Self>) {
let focused = pointer self.activate_focused_pointer_constraint(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 cursor_position_hint( fn cursor_position_hint(
&mut self, &mut self,
_surface: &WlSurface, surface: &WlSurface,
_pointer: &PointerHandle<Self>, pointer: &PointerHandle<Self>,
_location: smithay::utils::Point<f64, smithay::utils::Logical>, location: smithay::utils::Point<f64, smithay::utils::Logical>,
) { ) {
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);
}
} }
} }
+79 -2
View File
@@ -5,10 +5,11 @@ use smithay::{
}, },
input::{ input::{
keyboard::FilterResult, keyboard::FilterResult,
pointer::{AxisFrame, ButtonEvent, MotionEvent, RelativeMotionEvent}, pointer::{AxisFrame, ButtonEvent, MotionEvent, PointerHandle, RelativeMotionEvent},
}, },
reexports::wayland_server::protocol::wl_surface::WlSurface, 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; use crate::Ferese;
@@ -36,6 +37,8 @@ impl Ferese {
}; };
let position = event.position_transformed(geometry.size) + geometry.loc.to_f64(); let position = event.position_transformed(geometry.size) + geometry.loc.to_f64();
let pointer = self.seat.get_pointer().expect("seat has a pointer"); let pointer = self.seat.get_pointer().expect("seat has a pointer");
let position = self.constrain_pointer_position(&pointer, position);
pointer.motion( pointer.motion(
self, self,
self.surface_under(position), self.surface_under(position),
@@ -46,10 +49,12 @@ impl Ferese {
}, },
); );
pointer.frame(self); pointer.frame(self);
self.activate_focused_pointer_constraint(&pointer);
} }
InputEvent::PointerMotion { event, .. } => { InputEvent::PointerMotion { event, .. } => {
let pointer = self.seat.get_pointer().expect("seat has a pointer"); let pointer = self.seat.get_pointer().expect("seat has a pointer");
let focus = self.surface_under(pointer.current_location()); let focus = self.surface_under(pointer.current_location());
pointer.relative_motion( pointer.relative_motion(
self, self,
focus, focus,
@@ -64,9 +69,11 @@ impl Ferese {
InputEvent::PointerButton { event, .. } => { InputEvent::PointerButton { event, .. } => {
let pointer = self.seat.get_pointer().expect("seat has a pointer"); let pointer = self.seat.get_pointer().expect("seat has a pointer");
let serial = SERIAL_COUNTER.next_serial(); let serial = SERIAL_COUNTER.next_serial();
if event.state() == ButtonState::Pressed && !pointer.is_grabbed() { if event.state() == ButtonState::Pressed && !pointer.is_grabbed() {
self.focus_window_under_pointer(serial); self.focus_window_under_pointer(serial);
} }
pointer.button( pointer.button(
self, self,
&ButtonEvent { &ButtonEvent {
@@ -93,12 +100,14 @@ impl Ferese {
frame = frame.v120(Axis::Horizontal, value as i32); frame = frame.v120(Axis::Horizontal, value as i32);
} }
} }
if vertical != 0.0 { if vertical != 0.0 {
frame = frame.value(Axis::Vertical, vertical); frame = frame.value(Axis::Vertical, vertical);
if let Some(value) = event.amount_v120(Axis::Vertical) { if let Some(value) = event.amount_v120(Axis::Vertical) {
frame = frame.v120(Axis::Vertical, value as i32); frame = frame.v120(Axis::Vertical, value as i32);
} }
} }
if source == AxisSource::Finger { if source == AxisSource::Finger {
if event.amount(Axis::Horizontal) == Some(0.0) { if event.amount(Axis::Horizontal) == Some(0.0) {
frame = frame.stop(Axis::Horizontal); frame = frame.stop(Axis::Horizontal);
@@ -107,7 +116,9 @@ impl Ferese {
frame = frame.stop(Axis::Vertical); frame = frame.stop(Axis::Vertical);
} }
} }
let pointer = self.seat.get_pointer().expect("seat has a pointer"); let pointer = self.seat.get_pointer().expect("seat has a pointer");
pointer.axis(self, frame); pointer.axis(self, frame);
pointer.frame(self); pointer.frame(self);
} }
@@ -118,6 +129,7 @@ impl Ferese {
fn focus_window_under_pointer(&mut self, serial: Serial) { fn focus_window_under_pointer(&mut self, serial: Serial) {
let pointer = self.seat.get_pointer().expect("seat has a pointer"); let pointer = self.seat.get_pointer().expect("seat has a pointer");
let keyboard = self.seat.get_keyboard().expect("seat has a keyboard"); let keyboard = self.seat.get_keyboard().expect("seat has a keyboard");
if let Some((window, _)) = self.space.element_under(pointer.current_location()) { if let Some((window, _)) = self.space.element_under(pointer.current_location()) {
let window = window.clone(); let window = window.clone();
self.space.raise_element(&window, true); self.space.raise_element(&window, true);
@@ -130,10 +142,75 @@ impl Ferese {
} else { } else {
keyboard.set_focus(self, Option::<WlSurface>::None, serial); keyboard.set_focus(self, Option::<WlSurface>::None, serial);
} }
self.space.elements().for_each(|window| { self.space.elements().for_each(|window| {
if let Some(toplevel) = window.toplevel() { if let Some(toplevel) = window.toplevel() {
toplevel.send_pending_configure(); toplevel.send_pending_configure();
} }
}); });
} }
fn constrain_pointer_position(
&self,
pointer: &PointerHandle<Self>,
requested: Point<f64, Logical>,
) -> Point<f64, Logical> {
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<Self>) {
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();
}
});
}
} }