1
0
Fork 0
forked from wry/wry

tree: implement pointer constraints

This commit is contained in:
Julian Orth 2022-07-21 20:16:22 +02:00
parent d4c4497043
commit 38d1267ec9
19 changed files with 707 additions and 4 deletions

View file

@ -38,13 +38,13 @@ The following features have been implemented and should work:
- Monitor hotplug - Monitor hotplug
- Fractional scaling - Fractional scaling
- Hardware cursors - Hardware cursors
- Pointer constraints
### Missing Features ### Missing Features
The following features are known to be missing or broken and will be implemented The following features are known to be missing or broken and will be implemented
later: later:
- Games that require pointer grabs
- Touch and tablet support - Touch and tablet support
- Damage tracking (any kind of damage causes a complete re-render currently) - Damage tracking (any kind of damage causes a complete re-render currently)
- Selecting the primary device in multi-GPU systems - Selecting the primary device in multi-GPU systems

View file

@ -297,6 +297,10 @@ impl Client {
axis axis
} }
pub fn disable_pointer_constraint(&self, seat: Seat) {
self.send(&ClientMessage::DisablePointerConstraint { seat });
}
pub fn set_fullscreen(&self, seat: Seat, fullscreen: bool) { pub fn set_fullscreen(&self, seat: Seat, fullscreen: bool) {
self.send(&ClientMessage::SetFullscreen { seat, fullscreen }); self.send(&ClientMessage::SetFullscreen { seat, fullscreen });
} }

View file

@ -304,6 +304,9 @@ pub enum ClientMessage<'a> {
seat: Seat, seat: Seat,
use_hardware_cursor: bool, use_hardware_cursor: bool,
}, },
DisablePointerConstraint {
seat: Seat,
},
} }
#[derive(Encode, Decode, Debug)] #[derive(Encode, Decode, Debug)]

View file

@ -278,6 +278,11 @@ impl Seat {
pub fn set_fullscreen(self, fullscreen: bool) { pub fn set_fullscreen(self, fullscreen: bool) {
get!().set_fullscreen(self, fullscreen) get!().set_fullscreen(self, fullscreen)
} }
/// Disables the currently active pointer constraint on this seat.
pub fn disable_pointer_constraint(self) {
get!().disable_pointer_constraint(self)
}
} }
/// Returns all seats. /// Returns all seats.

View file

@ -605,6 +605,12 @@ impl ConfigProxyHandler {
Ok(()) Ok(())
} }
fn handle_disable_pointer_constraint(&self, seat: Seat) -> Result<(), CphError> {
let seat = self.get_seat(seat)?;
seat.disable_pointer_constraint();
Ok(())
}
fn handle_set_use_hardware_cursor( fn handle_set_use_hardware_cursor(
&self, &self,
seat: Seat, seat: Seat,
@ -1195,6 +1201,9 @@ impl ConfigProxyHandler {
} => self } => self
.handle_set_use_hardware_cursor(seat, use_hardware_cursor) .handle_set_use_hardware_cursor(seat, use_hardware_cursor)
.wrn("set_use_hardware_cursor")?, .wrn("set_use_hardware_cursor")?,
ClientMessage::DisablePointerConstraint { seat } => self
.handle_disable_pointer_constraint(seat)
.wrn("disable_pointer_constraint")?,
} }
Ok(()) Ok(())
} }

View file

@ -9,6 +9,8 @@ use std::{
pub struct Fixed(pub i32); pub struct Fixed(pub i32);
impl Fixed { impl Fixed {
pub const EPSILON: Self = Fixed(1);
pub fn is_integer(self) -> bool { pub fn is_integer(self) -> bool {
self.0 & 255 == 0 self.0 & 255 == 0
} }
@ -86,6 +88,22 @@ impl Add for Fixed {
} }
} }
impl Sub<i32> for Fixed {
type Output = Self;
fn sub(self, rhs: i32) -> Self::Output {
Self(self.0 - (rhs << 8))
}
}
impl Add<i32> for Fixed {
type Output = Self;
fn add(self, rhs: i32) -> Self::Output {
Self(self.0 + (rhs << 8))
}
}
impl AddAssign for Fixed { impl AddAssign for Fixed {
fn add_assign(&mut self, rhs: Self) { fn add_assign(&mut self, rhs: Self) {
self.0 += rhs.0; self.0 += rhs.0;

View file

@ -14,6 +14,7 @@ use {
wl_output::WlOutputGlobal, wl_output::WlOutputGlobal,
wl_registry::WlRegistry, wl_registry::WlRegistry,
wl_seat::{ wl_seat::{
zwp_pointer_constraints_v1::ZwpPointerConstraintsV1Global,
zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1Global, WlSeatGlobal, zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1Global, WlSeatGlobal,
}, },
wl_shm::WlShmGlobal, wl_shm::WlShmGlobal,
@ -147,6 +148,7 @@ impl Globals {
add_singleton!(ExtSessionLockManagerV1Global); add_singleton!(ExtSessionLockManagerV1Global);
add_singleton!(WpViewporterGlobal); add_singleton!(WpViewporterGlobal);
add_singleton!(WpFractionalScaleManagerV1Global); add_singleton!(WpFractionalScaleManagerV1Global);
add_singleton!(ZwpPointerConstraintsV1Global);
} }
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) { pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {

View file

@ -4,6 +4,7 @@ mod pointer_owner;
pub mod wl_keyboard; pub mod wl_keyboard;
pub mod wl_pointer; pub mod wl_pointer;
pub mod wl_touch; pub mod wl_touch;
pub mod zwp_pointer_constraints_v1;
pub mod zwp_relative_pointer_manager_v1; pub mod zwp_relative_pointer_manager_v1;
pub mod zwp_relative_pointer_v1; pub mod zwp_relative_pointer_v1;
@ -32,6 +33,7 @@ use {
wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE}, wl_keyboard::{WlKeyboard, WlKeyboardError, REPEAT_INFO_SINCE},
wl_pointer::WlPointer, wl_pointer::WlPointer,
wl_touch::WlTouch, wl_touch::WlTouch,
zwp_pointer_constraints_v1::{SeatConstraint, SeatConstraintStatus},
zwp_relative_pointer_v1::ZwpRelativePointerV1, zwp_relative_pointer_v1::ZwpRelativePointerV1,
}, },
wl_surface::WlSurface, wl_surface::WlSurface,
@ -152,6 +154,7 @@ pub struct WlSeatGlobal {
changes: NumCell<u32>, changes: NumCell<u32>,
cursor_size: Cell<u32>, cursor_size: Cell<u32>,
hardware_cursor: Cell<bool>, hardware_cursor: Cell<bool>,
constraint: CloneCell<Option<Rc<SeatConstraint>>>,
} }
const CHANGE_CURSOR_MOVED: u32 = 1 << 0; const CHANGE_CURSOR_MOVED: u32 = 1 << 0;
@ -205,6 +208,7 @@ impl WlSeatGlobal {
changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE), changes: NumCell::new(CHANGE_CURSOR_MOVED | CHANGE_TREE),
cursor_size: Cell::new(DEFAULT_CURSOR_SIZE), cursor_size: Cell::new(DEFAULT_CURSOR_SIZE),
hardware_cursor: Cell::new(state.globals.seats.len() == 0), hardware_cursor: Cell::new(state.globals.seats.len() == 0),
constraint: Default::default(),
}); });
state.add_cursor_size(DEFAULT_CURSOR_SIZE); state.add_cursor_size(DEFAULT_CURSOR_SIZE);
let seat = slf.clone(); let seat = slf.clone();
@ -388,6 +392,47 @@ impl WlSeatGlobal {
.set(Some(self.state.seat_queue.add_last(self.clone()))); .set(Some(self.state.seat_queue.add_last(self.clone())));
} }
pub fn disable_pointer_constraint(&self) {
if let Some(constraint) = self.constraint.get() {
constraint.deactivate();
if constraint.status.get() == SeatConstraintStatus::Inactive {
constraint
.status
.set(SeatConstraintStatus::ActivatableOnFocus);
}
}
}
fn maybe_constrain_pointer_node(&self) {
if let Some(pn) = self.pointer_node() {
if let Some(surface) = pn.node_into_surface() {
let (mut x, mut y) = self.pos.get();
let (sx, sy) = surface.buffer_abs_pos.get().position();
x -= Fixed::from_int(sx);
y -= Fixed::from_int(sy);
self.maybe_constrain(&surface, x, y);
}
}
}
fn maybe_constrain(&self, surface: &WlSurface, x: Fixed, y: Fixed) {
if self.constraint.get().is_some() {
return;
}
let candidate = match surface.constraints.get(&self.id) {
Some(c) if c.status.get() == SeatConstraintStatus::Inactive => c,
_ => return,
};
if !candidate.contains(x.round_down(), y.round_down()) {
return;
}
candidate.status.set(SeatConstraintStatus::Active);
if let Some(owner) = candidate.owner.get() {
owner.send_enabled();
}
self.constraint.set(Some(candidate));
}
pub fn set_fullscreen(&self, fullscreen: bool) { pub fn set_fullscreen(&self, fullscreen: bool) {
if let Some(tl) = self.keyboard_node.get().node_toplevel() { if let Some(tl) = self.keyboard_node.get().node_toplevel() {
tl.tl_set_fullscreen(fullscreen); tl.tl_set_fullscreen(fullscreen);
@ -800,6 +845,7 @@ impl WlSeatGlobal {
self.queue_link.set(None); self.queue_link.set(None);
self.tree_changed_handler.set(None); self.tree_changed_handler.set(None);
self.output.set(self.state.dummy_output.get().unwrap()); self.output.set(self.state.dummy_output.get().unwrap());
self.constraint.take();
} }
pub fn id(&self) -> SeatId { pub fn id(&self) -> SeatId {

View file

@ -19,6 +19,7 @@ use {
AXIS_VALUE120_SINCE_VERSION, POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, AXIS_VALUE120_SINCE_VERSION, POINTER_FRAME_SINCE_VERSION, WHEEL_TILT,
WHEEL_TILT_SINCE_VERSION, WHEEL_TILT_SINCE_VERSION,
}, },
zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus},
zwp_relative_pointer_v1::ZwpRelativePointerV1, zwp_relative_pointer_v1::ZwpRelativePointerV1,
Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED,
}, },
@ -216,6 +217,11 @@ impl WlSeatGlobal {
let pos = output.node.global.pos.get(); let pos = output.node.global.pos.get();
x += Fixed::from_int(pos.x1()); x += Fixed::from_int(pos.x1());
y += Fixed::from_int(pos.y1()); y += Fixed::from_int(pos.y1());
if let Some(c) = self.constraint.get() {
if c.ty == ConstraintType::Lock || !c.contains(x.round_down(), y.round_down()) {
c.deactivate();
}
}
self.state.for_each_seat_tester(|t| { self.state.for_each_seat_tester(|t| {
t.send_pointer_abs(self.id, time_usec, x, y); t.send_pointer_abs(self.id, time_usec, x, y);
}); });
@ -238,9 +244,26 @@ impl WlSeatGlobal {
dx_unaccelerated, dx_unaccelerated,
dy_unaccelerated, dy_unaccelerated,
); );
let constraint = self.constraint.get();
let locked = match &constraint {
Some(c) if c.ty == ConstraintType::Lock => true,
_ => false,
};
let (mut x, mut y) = self.pos.get(); let (mut x, mut y) = self.pos.get();
x += dx; if !locked {
y += dy; x += dx;
y += dy;
if let Some(c) = &constraint {
let surface_pos = c.surface.buffer_abs_pos.get();
let (x_rel, y_rel) = (x - surface_pos.x1(), y - surface_pos.y1());
let contained = surface_pos.contains(x.round_down(), y.round_down())
&& c.contains(x_rel.round_down(), y_rel.round_down());
if !contained {
let (x_rel, y_rel) = c.warp(x_rel, y_rel);
(x, y) = (x_rel + surface_pos.x1(), y_rel + surface_pos.y1());
}
}
}
self.state.for_each_seat_tester(|t| { self.state.for_each_seat_tester(|t| {
t.send_pointer_rel( t.send_pointer_rel(
self.id, self.id,
@ -604,6 +627,7 @@ impl WlSeatGlobal {
let time = (self.pos_time_usec.get() / 1000) as u32; let time = (self.pos_time_usec.get() / 1000) as u32;
self.surface_pointer_event(0, n, |p| p.send_motion(time, x, y)); self.surface_pointer_event(0, n, |p| p.send_motion(time, x, y));
self.surface_pointer_frame(n); self.surface_pointer_frame(n);
self.maybe_constrain(n, x, y);
} }
} }
@ -641,6 +665,12 @@ impl WlSeatGlobal {
n.client.last_enter_serial.set(serial); n.client.last_enter_serial.set(serial);
self.surface_pointer_event(0, n, |p| p.send_enter(serial, n.id, x, y)); self.surface_pointer_event(0, n, |p| p.send_enter(serial, n.id, x, y));
self.surface_pointer_frame(n); self.surface_pointer_frame(n);
for (_, constraint) in &n.constraints {
if constraint.status.get() == SeatConstraintStatus::ActivatableOnFocus {
constraint.status.set(SeatConstraintStatus::Inactive);
}
}
self.maybe_constrain(n, x, y);
} }
} }
@ -648,6 +678,9 @@ impl WlSeatGlobal {
impl WlSeatGlobal { impl WlSeatGlobal {
pub fn leave_surface(&self, n: &WlSurface) { pub fn leave_surface(&self, n: &WlSurface) {
let serial = n.client.next_serial(); let serial = n.client.next_serial();
for (_, constraint) in &n.constraints {
constraint.deactivate();
}
self.surface_pointer_event(0, n, |p| p.send_leave(serial, n.id)); self.surface_pointer_event(0, n, |p| p.send_leave(serial, n.id));
self.surface_pointer_frame(n); self.surface_pointer_frame(n);
} }

View file

@ -0,0 +1,308 @@
use {
crate::{
client::{Client, ClientError},
fixed::Fixed,
globals::{Global, GlobalName},
ifs::{
wl_seat::{
zwp_pointer_constraints_v1::zwp_confined_pointer_v1::ZwpConfinedPointerV1,
WlSeatGlobal,
},
wl_surface::WlSurface,
},
leaks::Tracker,
object::Object,
rect::Region,
utils::{
buffd::{MsgParser, MsgParserError},
clonecell::CloneCell,
},
wire::{
zwp_pointer_constraints_v1::*, WlPointerId, WlRegionId, WlSurfaceId,
ZwpPointerConstraintsV1Id,
},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
zwp_locked_pointer_v1::ZwpLockedPointerV1,
};
pub mod zwp_confined_pointer_v1;
pub mod zwp_locked_pointer_v1;
pub struct ZwpPointerConstraintsV1Global {
pub name: GlobalName,
}
pub struct ZwpPointerConstraintsV1 {
pub id: ZwpPointerConstraintsV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ConstraintType {
Lock,
Confine,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SeatConstraintStatus {
Active,
ActivatableOnFocus,
Inactive,
TerminallyDisabled,
}
pub struct SeatConstraint {
pub owner: CloneCell<Option<Rc<dyn ConstraintOwner>>>,
pub client: Rc<Client>,
pub seat: Rc<WlSeatGlobal>,
pub surface: Rc<WlSurface>,
pub region: CloneCell<Option<Rc<Region>>>,
pub one_shot: bool,
pub status: Cell<SeatConstraintStatus>,
pub ty: ConstraintType,
}
impl SeatConstraint {
pub fn deactivate(&self) {
if self.status.get() == SeatConstraintStatus::Active {
self.seat.constraint.take();
if let Some(owner) = self.owner.get() {
owner.send_disabled();
}
if self.one_shot {
self.status.set(SeatConstraintStatus::TerminallyDisabled);
} else {
self.status.set(SeatConstraintStatus::Inactive);
}
}
}
pub fn contains(&self, x: i32, y: i32) -> bool {
let region = self.region.get();
if let Some(region) = region {
return region.contains(x, y);
}
true
}
pub fn warp(&self, mut x: Fixed, mut y: Fixed) -> (Fixed, Fixed) {
let (x_int, y_int) = (x.round_down(), y.round_down());
let mut best_rect;
if let Some(region) = self.region.get() {
if region.is_empty() {
return (x, y);
}
best_rect = region[0];
let mut best_dist = region[0].dist_squared(x_int, y_int);
for rect in &region[1..] {
let dist = rect.dist_squared(x_int, y_int);
if dist < best_dist {
best_dist = dist;
best_rect = *rect;
}
}
} else {
best_rect = self.surface.buffer_abs_pos.get().at_point(0, 0);
}
if x_int < best_rect.x1() {
x = Fixed::from_int(best_rect.x1());
} else if x_int >= best_rect.x2() {
x = Fixed::from_int(best_rect.x2()) - Fixed::EPSILON;
}
if y_int < best_rect.y1() {
y = Fixed::from_int(best_rect.y1());
} else if y_int >= best_rect.y2() {
y = Fixed::from_int(best_rect.y2()) - Fixed::EPSILON;
}
(x, y)
}
fn detach(&self) {
self.deactivate();
self.owner.take();
self.surface.constraints.remove(&self.seat.id);
}
fn set_region(&self, region: WlRegionId) -> Result<(), ZwpPointerConstraintsV1Error> {
let region = if region.is_some() {
Some(self.client.lookup(region)?.region())
} else {
None
};
self.region.set(region);
Ok(())
}
}
pub trait ConstraintOwner {
fn send_enabled(&self);
fn send_disabled(&self);
}
const LT_ONESHOT: u32 = 1;
const LT_PERSISTENT: u32 = 2;
impl ZwpPointerConstraintsV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: ZwpPointerConstraintsV1Id,
client: &Rc<Client>,
_version: u32,
) -> Result<(), ZwpPointerConstraintsV1Error> {
let cs = Rc::new(ZwpPointerConstraintsV1 {
id,
client: client.clone(),
tracker: Default::default(),
});
track!(client, cs);
client.add_client_obj(&cs)?;
Ok(())
}
}
impl ZwpPointerConstraintsV1 {
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpPointerConstraintsV1Error> {
let _req: Destroy = self.client.parse(self, msg)?;
self.client.remove_obj(self)?;
Ok(())
}
fn create_constraint(
&self,
ty: ConstraintType,
pointer: WlPointerId,
surface: WlSurfaceId,
region: WlRegionId,
lifetime: u32,
) -> Result<Rc<SeatConstraint>, ZwpPointerConstraintsV1Error> {
let pointer = self.client.lookup(pointer)?;
let seat = &pointer.seat.global;
let surface = self.client.lookup(surface)?;
if surface.constraints.contains(&seat.id) {
return Err(ZwpPointerConstraintsV1Error::AlreadyConstrained);
}
let region = if region.is_some() {
Some(self.client.lookup(region)?.region())
} else {
None
};
let one_shot = match lifetime {
LT_ONESHOT => true,
LT_PERSISTENT => false,
_ => return Err(ZwpPointerConstraintsV1Error::UnknownLifetime(lifetime)),
};
Ok(Rc::new(SeatConstraint {
owner: Default::default(),
client: self.client.clone(),
seat: seat.clone(),
surface,
region: CloneCell::new(region),
one_shot,
status: Cell::new(SeatConstraintStatus::Inactive),
ty,
}))
}
fn lock_pointer(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpPointerConstraintsV1Error> {
let req: LockPointer = self.client.parse(self, msg)?;
let constraint = self.create_constraint(
ConstraintType::Lock,
req.pointer,
req.surface,
req.region,
req.lifetime,
)?;
let lp = Rc::new(ZwpLockedPointerV1 {
id: req.id,
tracker: Default::default(),
constraint,
});
self.client.add_client_obj(&lp)?;
lp.constraint.owner.set(Some(lp.clone()));
lp.constraint
.surface
.constraints
.insert(lp.constraint.seat.id, lp.constraint.clone());
lp.constraint.seat.maybe_constrain_pointer_node();
Ok(())
}
fn confine_pointer(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpPointerConstraintsV1Error> {
let req: ConfinePointer = self.client.parse(self, msg)?;
let constraint = self.create_constraint(
ConstraintType::Confine,
req.pointer,
req.surface,
req.region,
req.lifetime,
)?;
let lp = Rc::new(ZwpConfinedPointerV1 {
id: req.id,
tracker: Default::default(),
constraint,
});
self.client.add_client_obj(&lp)?;
lp.constraint.owner.set(Some(lp.clone()));
lp.constraint
.surface
.constraints
.insert(lp.constraint.seat.id, lp.constraint.clone());
lp.constraint.seat.maybe_constrain_pointer_node();
Ok(())
}
}
global_base!(
ZwpPointerConstraintsV1Global,
ZwpPointerConstraintsV1,
ZwpPointerConstraintsV1Error
);
impl Global for ZwpPointerConstraintsV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
}
simple_add_global!(ZwpPointerConstraintsV1Global);
object_base! {
ZwpPointerConstraintsV1;
DESTROY => destroy,
LOCK_POINTER => lock_pointer,
CONFINE_POINTER => confine_pointer,
}
impl Object for ZwpPointerConstraintsV1 {
fn num_requests(&self) -> u32 {
CONFINE_POINTER + 1
}
}
simple_add_obj!(ZwpPointerConstraintsV1);
#[derive(Debug, Error)]
pub enum ZwpPointerConstraintsV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error("The surface already has a constraint attached for the seat")]
AlreadyConstrained,
#[error("The constraint lifetime {0} is unknown")]
UnknownLifetime(u32),
}
efrom!(ZwpPointerConstraintsV1Error, ClientError);
efrom!(ZwpPointerConstraintsV1Error, MsgParserError);

View file

@ -0,0 +1,78 @@
use {
crate::{
client::ClientError,
ifs::wl_seat::zwp_pointer_constraints_v1::{
ConstraintOwner, SeatConstraint, ZwpPointerConstraintsV1Error,
},
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
wire::{zwp_confined_pointer_v1::*, ZwpConfinedPointerV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwpConfinedPointerV1 {
pub id: ZwpConfinedPointerV1Id,
pub tracker: Tracker<Self>,
pub constraint: Rc<SeatConstraint>,
}
impl ZwpConfinedPointerV1 {
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpConfinedPointerV1Error> {
let _req: Destroy = self.constraint.client.parse(self, msg)?;
self.constraint.detach();
self.constraint.client.remove_obj(self)?;
Ok(())
}
fn set_region(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpConfinedPointerV1Error> {
let req: SetRegion = self.constraint.client.parse(self, msg)?;
self.constraint.set_region(req.region)?;
Ok(())
}
}
impl ConstraintOwner for ZwpConfinedPointerV1 {
fn send_enabled(&self) {
self.constraint.client.event(Confined { self_id: self.id });
}
fn send_disabled(&self) {
self.constraint
.client
.event(Unconfined { self_id: self.id });
}
}
object_base! {
ZwpConfinedPointerV1;
DESTROY => destroy,
SET_REGION => set_region,
}
impl Object for ZwpConfinedPointerV1 {
fn num_requests(&self) -> u32 {
SET_REGION + 1
}
fn break_loops(&self) {
self.constraint.detach();
}
}
simple_add_obj!(ZwpConfinedPointerV1);
#[derive(Debug, Error)]
pub enum ZwpConfinedPointerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ZwpPointerConstraintsV1Error(#[from] ZwpPointerConstraintsV1Error),
}
efrom!(ZwpConfinedPointerV1Error, ClientError);
efrom!(ZwpConfinedPointerV1Error, MsgParserError);

View file

@ -0,0 +1,85 @@
use {
crate::{
client::ClientError,
ifs::wl_seat::zwp_pointer_constraints_v1::{
ConstraintOwner, SeatConstraint, ZwpPointerConstraintsV1Error,
},
leaks::Tracker,
object::Object,
utils::buffd::{MsgParser, MsgParserError},
wire::{zwp_locked_pointer_v1::*, ZwpLockedPointerV1Id},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwpLockedPointerV1 {
pub id: ZwpLockedPointerV1Id,
pub tracker: Tracker<Self>,
pub constraint: Rc<SeatConstraint>,
}
impl ZwpLockedPointerV1 {
fn destroy(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpLockedPointerV1Error> {
let _req: Destroy = self.constraint.client.parse(self, msg)?;
self.constraint.detach();
self.constraint.client.remove_obj(self)?;
Ok(())
}
fn set_cursor_position_hint(
&self,
msg: MsgParser<'_, '_>,
) -> Result<(), ZwpLockedPointerV1Error> {
let _req: SetCursorPositionHint = self.constraint.client.parse(self, msg)?;
Ok(())
}
fn set_region(&self, msg: MsgParser<'_, '_>) -> Result<(), ZwpLockedPointerV1Error> {
let req: SetRegion = self.constraint.client.parse(self, msg)?;
self.constraint.set_region(req.region)?;
Ok(())
}
}
impl ConstraintOwner for ZwpLockedPointerV1 {
fn send_enabled(&self) {
self.constraint.client.event(Locked { self_id: self.id });
}
fn send_disabled(&self) {
self.constraint.client.event(Unlocked { self_id: self.id });
}
}
object_base! {
ZwpLockedPointerV1;
DESTROY => destroy,
SET_CURSOR_POSITION_HINT => set_cursor_position_hint,
SET_REGION => set_region,
}
impl Object for ZwpLockedPointerV1 {
fn num_requests(&self) -> u32 {
SET_REGION + 1
}
fn break_loops(&self) {
self.constraint.detach();
}
}
simple_add_obj!(ZwpLockedPointerV1);
#[derive(Debug, Error)]
pub enum ZwpLockedPointerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("Parsing failed")]
MsgParserError(#[source] Box<MsgParserError>),
#[error(transparent)]
ZwpPointerConstraintsV1Error(#[from] ZwpPointerConstraintsV1Error),
}
efrom!(ZwpLockedPointerV1Error, ClientError);
efrom!(ZwpLockedPointerV1Error, MsgParserError);

View file

@ -20,7 +20,10 @@ use {
TF_180, TF_270, TF_90, TF_FLIPPED, TF_FLIPPED_180, TF_FLIPPED_270, TF_FLIPPED_90, TF_180, TF_270, TF_90, TF_FLIPPED, TF_FLIPPED_180, TF_FLIPPED_270, TF_FLIPPED_90,
TF_NORMAL, TF_NORMAL,
}, },
wl_seat::{wl_pointer::PendingScroll, Dnd, NodeSeatState, SeatId, WlSeatGlobal}, wl_seat::{
wl_pointer::PendingScroll, zwp_pointer_constraints_v1::SeatConstraint, Dnd,
NodeSeatState, SeatId, WlSeatGlobal,
},
wl_surface::{ wl_surface::{
cursor::CursorSurface, wl_subsurface::WlSubsurface, cursor::CursorSurface, wl_subsurface::WlSubsurface,
wp_fractional_scale_v1::WpFractionalScaleV1, wp_viewport::WpViewport, wp_fractional_scale_v1::WpFractionalScaleV1, wp_viewport::WpViewport,
@ -253,6 +256,7 @@ pub struct WlSurface {
viewporter: CloneCell<Option<Rc<WpViewport>>>, viewporter: CloneCell<Option<Rc<WpViewport>>>,
output: CloneCell<Rc<OutputNode>>, output: CloneCell<Rc<OutputNode>>,
fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>, fractional_scale: CloneCell<Option<Rc<WpFractionalScaleV1>>>,
pub constraints: SmallMap<SeatId, Rc<SeatConstraint>, 1>,
} }
impl Debug for WlSurface { impl Debug for WlSurface {
@ -386,6 +390,7 @@ impl WlSurface {
viewporter: Default::default(), viewporter: Default::default(),
output: CloneCell::new(client.state.dummy_output.get().unwrap()), output: CloneCell::new(client.state.dummy_output.get().unwrap()),
fractional_scale: Default::default(), fractional_scale: Default::default(),
constraints: Default::default(),
} }
} }
@ -595,6 +600,7 @@ impl WlSurface {
self.toplevel.set(None); self.toplevel.set(None);
self.client.remove_obj(self)?; self.client.remove_obj(self)?;
self.idle_inhibitors.clear(); self.idle_inhibitors.clear();
self.constraints.take();
Ok(()) Ok(())
} }
@ -957,6 +963,9 @@ impl WlSurface {
} }
pub fn destroy_node(&self) { pub fn destroy_node(&self) {
for (_, constraint) in &self.constraints {
constraint.deactivate();
}
for (_, inhibitor) in self.idle_inhibitors.lock().drain() { for (_, inhibitor) in self.idle_inhibitors.lock().drain() {
inhibitor.deactivate(); inhibitor.deactivate();
} }
@ -1020,6 +1029,7 @@ impl Object for WlSurface {
self.presentation_feedback.borrow_mut().clear(); self.presentation_feedback.borrow_mut().clear();
self.viewporter.take(); self.viewporter.take();
self.fractional_scale.take(); self.fractional_scale.take();
self.constraints.clear();
} }
} }

View file

@ -118,6 +118,22 @@ impl Rect {
self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y self.raw.x1 <= x && self.raw.y1 <= y && self.raw.x2 > x && self.raw.y2 > y
} }
pub fn dist_squared(&self, x: i32, y: i32) -> i32 {
let mut dx = 0;
if self.raw.x1 > x {
dx = self.raw.x1 - x;
} else if self.raw.x2 < x {
dx = x - self.raw.x1;
}
let mut dy = 0;
if self.raw.y1 > y {
dy = self.raw.y1 - y;
} else if self.raw.y2 < y {
dy = y - self.raw.y1;
}
dx * dx + dy * dy
}
#[allow(dead_code)] #[allow(dead_code)]
pub fn contains_rect(&self, rect: &Self) -> bool { pub fn contains_rect(&self, rect: &Self) -> bool {
self.raw.x1 <= rect.raw.x1 self.raw.x1 <= rect.raw.x1

View file

@ -78,6 +78,18 @@ impl Region {
pub fn extents(&self) -> Rect { pub fn extents(&self) -> Rect {
self.extents self.extents
} }
pub fn contains(&self, x: i32, y: i32) -> bool {
if !self.extents.contains(x, y) {
return false;
}
for r in self.deref() {
if r.contains(x, y) {
return true;
}
}
false
}
} }
impl Deref for Region { impl Deref for Region {

View file

@ -42,6 +42,10 @@ impl<K: Eq, V, const N: usize> SmallMap<K, V, N> {
} }
} }
pub fn contains(&self, k: &K) -> bool {
unsafe { self.m.get().deref().contains(k) }
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
unsafe { self.m.get().deref().len() } unsafe { self.m.get().deref().len() }
} }
@ -158,6 +162,15 @@ impl<K: Eq, V, const N: usize> SmallMapMut<K, V, N> {
self.m.len() self.m.len()
} }
pub fn contains(&self, k: &K) -> bool {
for (ek, _) in &self.m {
if ek == k {
return true;
}
}
false
}
pub fn insert(&mut self, k: K, v: V) -> Option<V> { pub fn insert(&mut self, k: K, v: V) -> Option<V> {
for (ek, ev) in &mut self.m { for (ek, ev) in &mut self.m {
if ek == &k { if ek == &k {

View file

@ -0,0 +1,19 @@
# requests
msg destroy = 0 {
}
msg set_region = 1 {
region: id(wl_region),
}
# events
msg confined = 0 {
}
msg unconfined = 1 {
}

View file

@ -0,0 +1,21 @@
# requests
msg destroy = 0 {
}
msg set_cursor_position_hint = 1 {
surface_x: fixed,
surface_y: fixed,
}
msg set_region = 2 {
region: id(wl_region),
}
# events
msg locked = 0 {
}
msg unlocked = 1 {
}

View file

@ -0,0 +1,21 @@
# requests
msg destroy = 0 {
}
msg lock_pointer = 1 {
id: id(zwp_locked_pointer_v1),
surface: id(wl_surface),
pointer: id(wl_pointer),
region: id(wl_region),
lifetime: u32,
}
msg confine_pointer = 2 {
id: id(zwp_confined_pointer_v1),
surface: id(wl_surface),
pointer: id(wl_pointer),
region: id(wl_region),
lifetime: u32,
}