From d7c92bf59a7e0925b3b72ee9b5df468be5007b23 Mon Sep 17 00:00:00 2001 From: Ian Douglas Scott Date: Wed, 13 Sep 2023 20:52:10 -0700 Subject: [PATCH] WIP pointer constraints Depends on https://github.com/Smithay/smithay/pull/872. For some reason, pointer confinment isn't working correctly here at the moment, even though the code matches Anvil... May need solution to https://github.com/Smithay/smithay/issues/1126 for behavior to be correct. --- Cargo.lock | 2 +- Cargo.toml | 3 +- src/input/mod.rs | 112 +++++++++++++++++--- src/state.rs | 2 + src/wayland/handlers/mod.rs | 1 + src/wayland/handlers/pointer_constraints.rs | 29 +++++ 6 files changed, 132 insertions(+), 17 deletions(-) create mode 100644 src/wayland/handlers/pointer_constraints.rs diff --git a/Cargo.lock b/Cargo.lock index 1094454a..a41f4b39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3761,7 +3761,7 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/smithay//smithay?rev=5affbde525#5affbde5256ada864d98804933959e1dcb2129e0" +source = "git+https://github.com/ids1024/smithay?branch=pointer-constrant#2d23eadf1e3e6c852982aeb91c8f3e14daa49ad9" dependencies = [ "appendlist", "ash", diff --git a/Cargo.toml b/Cargo.toml index 2375d254..a8c311a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,4 +87,5 @@ debug = true lto = "fat" [patch."https://github.com/Smithay/smithay.git"] -smithay = { git = "https://github.com/smithay//smithay", rev = "5affbde525" } +# smithay = { git = "https://github.com/smithay//smithay", rev = "5affbde525" } +smithay = { git = "https://github.com/ids1024/smithay", branch = "pointer-constrant" } diff --git a/src/input/mod.rs b/src/input/mod.rs index 83148a3a..770c2284 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -17,10 +17,13 @@ use calloop::{timer::Timer, RegistrationToken}; use cosmic_protocols::screencopy::v1::server::zcosmic_screencopy_session_v1::InputType; #[allow(deprecated)] use smithay::{ - backend::input::{ - Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, - GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, InputEvent, - KeyState, PointerAxisEvent, + backend::{ + input::{ + Axis, AxisSource, Device, DeviceCapability, GestureBeginEvent, GestureEndEvent, + GesturePinchUpdateEvent as _, GestureSwipeUpdateEvent as _, InputBackend, InputEvent, + KeyState, PointerAxisEvent, + }, + renderer::utils::with_renderer_surface_state, }, desktop::{layer_map_for_output, space::SpaceElement, WindowSurfaceType}, input::{ @@ -40,7 +43,9 @@ use smithay::{ }, utils::{Logical, Point, Rectangle, Serial, SERIAL_COUNTER}, wayland::{ - keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, seat::WaylandFocus, + keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitorSeat, + pointer_constraints::{with_pointer_constraint, PointerConstraint}, + seat::WaylandFocus, shell::wlr_layer::Layer as WlrLayer, }, xwayland::X11Surface, @@ -545,7 +550,7 @@ impl State { &output, output_geometry, &self.common.shell.override_redirect_windows, - overview.0, + overview.0.clone(), workspace, ); @@ -561,7 +566,44 @@ impl State { session.cursor_info(&seat, InputType::Pointer, geometry, offset); } } + let ptr = seat.get_pointer().unwrap(); + + let mut pointer_locked = false; + let mut confine_rect = None; + if let Some((under, surface_loc)) = under + .as_ref() + .and_then(|(target, l)| Some((target.wl_surface()?, l))) + { + let surface_size = with_renderer_surface_state(&under, |surface_state| { + surface_state.surface_size() + }); + with_pointer_constraint(&under, &ptr, |constraint| match constraint { + Some(constraint) if constraint.is_active() => { + match &*constraint { + PointerConstraint::Locked(_locked) => { + // TODO handle region + pointer_locked = true; + } + PointerConstraint::Confined(_locked) => { + // For now, just confine to surface, ignoring region + // TODO handle region + if let Some(surface_size) = surface_size { + confine_rect = Some( + Rectangle::from_loc_and_size( + *surface_loc, + surface_size, + ) + .to_f64(), + ); + } + } + } + } + _ => {} + }); + } + // Relative motion is sent first to ensure they're part of a `frame` // TODO: Find more correct solution ptr.relative_motion( @@ -573,15 +615,55 @@ impl State { utime: event.time(), }, ); - ptr.motion( - self, - under, - &MotionEvent { - location: position, - serial, - time: event.time_msec(), - }, - ); + if !pointer_locked { + // If confined, clamp to surface rectangle + if let Some(rect) = confine_rect { + position.x = + position.x.clamp(rect.loc.x, rect.loc.x + rect.size.w - 1.); + position.y = + position.y.clamp(rect.loc.y, rect.loc.y + rect.size.h - 1.); + } + + ptr.motion( + self, + under, + &MotionEvent { + location: position, + serial, + time: event.time_msec(), + }, + ); + + // If pointer is now in a constraint region, activate it + let workspace = self.common.shell.workspaces.active_mut(&output); + if let Some((under, surface_location)) = State::surface_under( + position, + relative_pos, + &output, + output_geometry, + &self.common.shell.override_redirect_windows, + overview.0, + workspace, + ) + .and_then(|(target, loc)| Some((target.wl_surface()?, loc))) + { + with_pointer_constraint(&under, &ptr, |constraint| match constraint { + Some(constraint) if !constraint.is_active() => { + let region = match &*constraint { + PointerConstraint::Locked(locked) => locked.region(), + PointerConstraint::Confined(confined) => confined.region(), + }; + let point = + ptr.current_location().to_i32_round() - surface_location; + if region.map_or(true, |region| region.contains(point)) { + constraint.activate(); + } + } + _ => {} + }); + } + } + // XXX frame #[cfg(feature = "debug")] if self.common.seats().position(|x| x == &seat).unwrap() == 0 { let location = if let Some(output) = self.common.shell.outputs.first() { diff --git a/src/state.rs b/src/state.rs index 02a3c714..a92b6148 100644 --- a/src/state.rs +++ b/src/state.rs @@ -65,6 +65,7 @@ use smithay::{ fractional_scale::{with_fractional_scale, FractionalScaleManagerState}, keyboard_shortcuts_inhibit::KeyboardShortcutsInhibitState, output::OutputManagerState, + pointer_constraints::PointerConstraintsState, pointer_gestures::PointerGesturesState, presentation::PresentationState, primary_selection::PrimarySelectionState, @@ -313,6 +314,7 @@ impl State { let kde_decoration_state = KdeDecorationState::new::(&dh, Mode::Client); let xdg_decoration_state = XdgDecorationState::new::(&dh); XWaylandKeyboardGrabState::new::(&dh); + PointerConstraintsState::new::(&dh); PointerGesturesState::new::(&dh); SecurityContextState::new::(&dh, client_has_security_context); diff --git a/src/wayland/handlers/mod.rs b/src/wayland/handlers/mod.rs index d8eac626..8107ec71 100644 --- a/src/wayland/handlers/mod.rs +++ b/src/wayland/handlers/mod.rs @@ -10,6 +10,7 @@ pub mod keyboard_shortcuts_inhibit; pub mod layer_shell; pub mod output; pub mod output_configuration; +pub mod pointer_constraints; pub mod pointer_gestures; pub mod presentation; pub mod primary_selection; diff --git a/src/wayland/handlers/pointer_constraints.rs b/src/wayland/handlers/pointer_constraints.rs new file mode 100644 index 00000000..33d48d50 --- /dev/null +++ b/src/wayland/handlers/pointer_constraints.rs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-only + +use crate::state::State; +use smithay::{ + delegate_pointer_constraints, + input::pointer::PointerHandle, + reexports::wayland_server::protocol::wl_surface::WlSurface, + wayland::{ + pointer_constraints::{with_pointer_constraint, PointerConstraintsHandler}, + seat::WaylandFocus, + }, +}; + +impl PointerConstraintsHandler for State { + fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle) { + // XXX region + if pointer + .current_focus() + .and_then(|x| x.wl_surface()) + .as_ref() + == Some(surface) + { + with_pointer_constraint(surface, pointer, |constraint| { + constraint.unwrap().activate(); + }); + } + } +} +delegate_pointer_constraints!(State);