wayland: implement new_constraint, remove_constraint and cursor_position_hint for PointerConstraintsHandler
new_constraint: check if the surface has pointer focus, keyboard focus, and if the pointer is within toplevel geometry or constraint region. If these conditions are met, then enable the constraint. remove_constraint: apply last cursor position hint that saved in seat. cursor_position_hint: save the cursor position hints provided by the client to the seat.
This commit is contained in:
parent
4fb7d49316
commit
208c2128cd
4 changed files with 156 additions and 42 deletions
|
|
@ -505,12 +505,12 @@ impl State {
|
||||||
let new_under = State::surface_under(pos, &output, shell)
|
let new_under = State::surface_under(pos, &output, shell)
|
||||||
.map(|(target, pos)| (target, pos.as_logical()));
|
.map(|(target, pos)| (target, pos.as_logical()));
|
||||||
|
|
||||||
//We should only check size, not the surface, so that we can move the mouse over the OSD in confined mode
|
// TODO: We might need a solution that allows constraints to bypass the surface without affecting the constraints themselves
|
||||||
// if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
|
if new_under.as_ref().and_then(|(under, _)| under.wl_surface())
|
||||||
// != surface.wl_surface()
|
!= surface.wl_surface()
|
||||||
// {
|
{
|
||||||
// return (false, None);
|
return (false, None);
|
||||||
// }
|
}
|
||||||
|
|
||||||
match surface {
|
match surface {
|
||||||
PointerFocusTarget::WlSurface { surface, .. } => {
|
PointerFocusTarget::WlSurface { surface, .. } => {
|
||||||
|
|
@ -2375,11 +2375,10 @@ impl State {
|
||||||
pointer: &PointerHandle<Self>,
|
pointer: &PointerHandle<Self>,
|
||||||
mut location: Point<f64, Logical>,
|
mut location: Point<f64, Logical>,
|
||||||
) {
|
) {
|
||||||
if let Some(client) = surface.client() {
|
let Some(client) = surface.client() else {
|
||||||
let scale = self.client_compositor_state(&client).client_scale();
|
return;
|
||||||
location.x /= scale;
|
};
|
||||||
location.y /= scale;
|
location = location.downscale(self.client_compositor_state(&client).client_scale());
|
||||||
}
|
|
||||||
|
|
||||||
let point_and_output = {
|
let point_and_output = {
|
||||||
let shell = self.common.shell.read();
|
let shell = self.common.shell.read();
|
||||||
|
|
|
||||||
|
|
@ -737,22 +737,23 @@ impl WorkspaceSet {
|
||||||
surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
) -> Option<(Rectangle<i32, Local>, Point<i32, Logical>)> {
|
) -> Option<(Rectangle<i32, Local>, Point<i32, Logical>)> {
|
||||||
let mut root = surface.clone();
|
let mut root = surface.clone();
|
||||||
loop {
|
|
||||||
while let Some(parent) = get_parent(&root) {
|
while let Some(parent) = get_parent(&root) {
|
||||||
|
root = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
while smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE) {
|
||||||
|
let parent = with_states(&root, |states| {
|
||||||
|
states
|
||||||
|
.data_map
|
||||||
|
.get::<XdgPopupSurfaceData>()
|
||||||
|
.and_then(|m| m.lock().unwrap().parent.as_ref().cloned())
|
||||||
|
});
|
||||||
|
if let Some(parent) = parent {
|
||||||
root = parent;
|
root = parent;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if smithay::wayland::compositor::get_role(&root) == Some(XDG_POPUP_ROLE)
|
|
||||||
&& let Some(parent) = with_states(&root, |states| {
|
|
||||||
states
|
|
||||||
.data_map
|
|
||||||
.get::<XdgPopupSurfaceData>()
|
|
||||||
.and_then(|m| m.lock().unwrap().parent.as_ref().cloned())
|
|
||||||
})
|
|
||||||
{
|
|
||||||
root = parent;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sticky_layer
|
self.sticky_layer
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,11 @@ use smithay::{
|
||||||
pointer::{CursorImageAttributes, CursorImageStatus},
|
pointer::{CursorImageAttributes, CursorImageStatus},
|
||||||
},
|
},
|
||||||
output::Output,
|
output::Output,
|
||||||
reexports::{input::Device as InputDevice, wayland_server::DisplayHandle},
|
reexports::{
|
||||||
utils::{Buffer, IsAlive, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
input::Device as InputDevice,
|
||||||
|
wayland_server::{DisplayHandle, protocol::wl_surface::WlSurface},
|
||||||
|
},
|
||||||
|
utils::{Buffer, IsAlive, Logical, Monotonic, Point, Rectangle, Serial, Time, Transform},
|
||||||
wayland::compositor::with_states,
|
wayland::compositor::with_states,
|
||||||
};
|
};
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
|
@ -178,6 +181,9 @@ struct ActiveOutput(pub Mutex<Output>);
|
||||||
/// The output which currently has keyboard focus
|
/// The output which currently has keyboard focus
|
||||||
struct FocusedOutput(pub Mutex<Option<Output>>);
|
struct FocusedOutput(pub Mutex<Option<Output>>);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PointerConstraintHint(pub Mutex<Option<(WlSurface, Point<f64, Logical>)>>);
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
pub struct LastModifierChange(pub Mutex<Option<Serial>>);
|
||||||
|
|
||||||
|
|
@ -201,6 +207,7 @@ pub fn create_seat(
|
||||||
userdata.insert_if_missing_threadsafe(CursorState::default);
|
userdata.insert_if_missing_threadsafe(CursorState::default);
|
||||||
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
userdata.insert_if_missing_threadsafe(|| ActiveOutput(Mutex::new(output.clone())));
|
||||||
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
|
userdata.insert_if_missing_threadsafe(|| FocusedOutput(Mutex::new(None)));
|
||||||
|
userdata.insert_if_missing_threadsafe(PointerConstraintHint::default);
|
||||||
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
userdata.insert_if_missing_threadsafe(|| Mutex::new(CursorImageStatus::default_named()));
|
||||||
|
|
||||||
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
// A lot of clients bind keyboard and pointer unconditionally once on launch..
|
||||||
|
|
@ -252,6 +259,8 @@ pub trait SeatExt {
|
||||||
fn supressed_buttons(&self) -> &SupressedButtons;
|
fn supressed_buttons(&self) -> &SupressedButtons;
|
||||||
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
fn modifiers_shortcut_queue(&self) -> &ModifiersShortcutQueue;
|
||||||
fn last_modifier_change(&self) -> Option<Serial>;
|
fn last_modifier_change(&self) -> Option<Serial>;
|
||||||
|
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)>;
|
||||||
|
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>);
|
||||||
|
|
||||||
fn cursor_geometry(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -337,6 +346,23 @@ impl SeatExt for Seat<State> {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pointer_constraint_hint(&self) -> Option<(WlSurface, Point<f64, Logical>)> {
|
||||||
|
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
|
||||||
|
let mut hint = lock.0.lock().unwrap();
|
||||||
|
// Check if alive
|
||||||
|
if let Some((ref surface, _)) = *hint
|
||||||
|
&& !surface.alive()
|
||||||
|
{
|
||||||
|
*hint = None;
|
||||||
|
}
|
||||||
|
hint.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_pointer_constraint_hint(&self, hint: Option<(WlSurface, Point<f64, Logical>)>) {
|
||||||
|
let lock = self.user_data().get::<PointerConstraintHint>().unwrap();
|
||||||
|
*lock.0.lock().unwrap() = hint;
|
||||||
|
}
|
||||||
|
|
||||||
fn cursor_geometry(
|
fn cursor_geometry(
|
||||||
&self,
|
&self,
|
||||||
loc: impl Into<Point<f64, Buffer>>,
|
loc: impl Into<Point<f64, Buffer>>,
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,123 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::{shell::CosmicSurface, state::State, utils::prelude::*};
|
||||||
use smithay::{
|
use smithay::{
|
||||||
input::pointer::PointerHandle,
|
input::pointer::PointerHandle,
|
||||||
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
reexports::wayland_server::protocol::wl_surface::WlSurface,
|
||||||
utils::{Logical, Point},
|
utils::{Logical, Point},
|
||||||
wayland::{
|
wayland::{pointer_constraints::PointerConstraintsHandler, seat::WaylandFocus},
|
||||||
pointer_constraints::{PointerConstraintsHandler, with_pointer_constraint},
|
|
||||||
seat::WaylandFocus,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use smithay::wayland::pointer_constraints::{PointerConstraintRef, with_pointer_constraint};
|
||||||
|
|
||||||
impl PointerConstraintsHandler for State {
|
impl PointerConstraintsHandler for State {
|
||||||
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
fn new_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||||
// XXX region
|
let seat = self
|
||||||
if pointer
|
.common
|
||||||
.current_focus()
|
.shell
|
||||||
.is_some_and(|x| x.wl_surface().as_deref() == Some(surface))
|
.read()
|
||||||
{
|
.seats
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
let (is_under, is_focused, surface_location) = if let Some(seat) = seat {
|
||||||
|
seat.set_pointer_constraint_hint(None);
|
||||||
|
let current_output = seat.active_output();
|
||||||
|
let position = seat.get_pointer().unwrap().current_location().as_global();
|
||||||
|
let shell = self.common.shell.read();
|
||||||
|
let under = State::surface_under(position, ¤t_output, &shell);
|
||||||
|
let mut surface_location = None;
|
||||||
|
|
||||||
|
let is_under = if let Some((target, target_loc)) = under
|
||||||
|
&& let Some(under_surface) = target.wl_surface()
|
||||||
|
{
|
||||||
|
if *under_surface == *surface {
|
||||||
|
surface_location = Some(target_loc);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
CosmicSurface::surface_tree_offset(surface, &under_surface).is_some_and(
|
||||||
|
|offset| {
|
||||||
|
surface_location = Some(target_loc - offset.to_f64().as_global());
|
||||||
|
true
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_focused = seat
|
||||||
|
.get_keyboard()
|
||||||
|
.and_then(|k| k.current_focus())
|
||||||
|
.is_some_and(|f| f.has_surface(&shell, surface));
|
||||||
|
|
||||||
|
(is_under, is_focused, surface_location)
|
||||||
|
} else {
|
||||||
|
(false, false, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_focused && is_under {
|
||||||
with_pointer_constraint(surface, pointer, |constraint| {
|
with_pointer_constraint(surface, pointer, |constraint| {
|
||||||
constraint.unwrap().activate();
|
if let Some(constraint) = constraint {
|
||||||
|
if let Some(region) = constraint.region() {
|
||||||
|
if let Some(surface_location) = surface_location
|
||||||
|
&& let position = pointer.current_location()
|
||||||
|
&& let point = (position - surface_location.as_logical()).to_i32_round()
|
||||||
|
&& region.contains(point)
|
||||||
|
{
|
||||||
|
constraint.activate();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
constraint.activate();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn remove_constraint(&mut self, surface: &WlSurface, pointer: &PointerHandle<Self>) {
|
||||||
|
if with_pointer_constraint(surface, pointer, |constraint| constraint.is_none()) {
|
||||||
|
let seat = self
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.seats
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
if let Some(seat) = seat
|
||||||
|
&& let Some((hint_surface, hint_location)) = seat.pointer_constraint_hint()
|
||||||
|
&& hint_surface == *surface
|
||||||
|
{
|
||||||
|
self.apply_cursor_hint(surface, pointer, hint_location);
|
||||||
|
seat.set_pointer_constraint_hint(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cursor_position_hint(
|
fn cursor_position_hint(
|
||||||
&mut self,
|
&mut self,
|
||||||
_surface: &WlSurface,
|
surface: &WlSurface,
|
||||||
_pointer: &PointerHandle<Self>,
|
pointer: &PointerHandle<Self>,
|
||||||
_location: Point<f64, Logical>,
|
location: Point<f64, Logical>,
|
||||||
) {
|
) {
|
||||||
// TODO
|
if with_pointer_constraint(surface, pointer, |constraint| {
|
||||||
|
constraint.is_some_and(|c| c.is_active())
|
||||||
|
}) {
|
||||||
|
let seat = self
|
||||||
|
.common
|
||||||
|
.shell
|
||||||
|
.read()
|
||||||
|
.seats
|
||||||
|
.iter()
|
||||||
|
.find(|s| s.get_pointer().as_ref() == Some(pointer))
|
||||||
|
.cloned();
|
||||||
|
|
||||||
|
if let Some(seat) = seat {
|
||||||
|
seat.set_pointer_constraint_hint(Some((surface.clone(), location)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue