wayland: implement wl_touch
Co-authored-by: Julian Orth <ju.orth@gmail.com>
This commit is contained in:
parent
905e2dd7ba
commit
681c1ad033
35 changed files with 1071 additions and 52 deletions
|
|
@ -24,6 +24,7 @@ use {
|
|||
AXIS_STOP_SINCE_VERSION, AXIS_VALUE120_SINCE_VERSION, IDENTICAL, INVERTED,
|
||||
POINTER_FRAME_SINCE_VERSION, WHEEL_TILT, WHEEL_TILT_SINCE_VERSION,
|
||||
},
|
||||
wl_touch::WlTouch,
|
||||
zwp_pointer_constraints_v1::{ConstraintType, SeatConstraintStatus},
|
||||
zwp_relative_pointer_v1::ZwpRelativePointerV1,
|
||||
Dnd, SeatId, WlSeat, WlSeatGlobal, CHANGE_CURSOR_MOVED, CHANGE_TREE,
|
||||
|
|
@ -31,6 +32,7 @@ use {
|
|||
wl_surface::{xdg_surface::xdg_popup::XdgPopup, WlSurface},
|
||||
},
|
||||
object::Version,
|
||||
rect::Rect,
|
||||
state::DeviceHandlerData,
|
||||
tree::{Direction, Node, ToplevelNode},
|
||||
utils::{bitflags::BitflagsExt, hash_map_ext::HashMapExt, smallmap::SmallMap},
|
||||
|
|
@ -54,6 +56,7 @@ pub struct NodeSeatState {
|
|||
pointer_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
kb_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
gesture_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
touch_foci: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
pointer_grabs: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
dnd_targets: SmallMap<SeatId, Rc<WlSeatGlobal>, 1>,
|
||||
tablet_pad_foci: SmallMap<TabletPadId, Rc<TabletPad>, 1>,
|
||||
|
|
@ -111,6 +114,14 @@ impl NodeSeatState {
|
|||
self.tablet_tool_foci.remove(&tool.id);
|
||||
}
|
||||
|
||||
pub(super) fn touch_begin(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.touch_foci.insert(seat.id, seat.clone());
|
||||
}
|
||||
|
||||
pub(super) fn touch_end(&self, seat: &WlSeatGlobal) {
|
||||
self.touch_foci.remove(&seat.id);
|
||||
}
|
||||
|
||||
pub(super) fn add_dnd_target(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.dnd_targets.insert(seat.id, seat.clone());
|
||||
}
|
||||
|
|
@ -184,6 +195,9 @@ impl NodeSeatState {
|
|||
while let Some((_, pad)) = self.tablet_pad_foci.pop() {
|
||||
pad.pad_owner.focus_root(&pad);
|
||||
}
|
||||
while let Some((_, seat)) = self.touch_foci.pop() {
|
||||
seat.touch_owner.cancel(&seat);
|
||||
}
|
||||
self.release_kb_focus2(focus_last);
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +244,11 @@ impl WlSeatGlobal {
|
|||
| InputEvent::TabletPadButton { time_usec, .. }
|
||||
| InputEvent::TabletPadModeSwitch { time_usec, .. }
|
||||
| InputEvent::TabletPadRing { time_usec, .. }
|
||||
| InputEvent::TabletPadStrip { time_usec, .. } => {
|
||||
| InputEvent::TabletPadStrip { time_usec, .. }
|
||||
| InputEvent::TouchDown { time_usec, .. }
|
||||
| InputEvent::TouchUp { time_usec, .. }
|
||||
| InputEvent::TouchMotion { time_usec, .. }
|
||||
| InputEvent::TouchCancel { time_usec, .. } => {
|
||||
self.last_input_usec.set(time_usec);
|
||||
if self.idle_notifications.is_not_empty() {
|
||||
for notification in self.idle_notifications.lock().drain_values() {
|
||||
|
|
@ -243,7 +261,8 @@ impl WlSeatGlobal {
|
|||
| InputEvent::AxisStop { .. }
|
||||
| InputEvent::Axis120 { .. }
|
||||
| InputEvent::TabletToolAdded { .. }
|
||||
| InputEvent::TabletToolRemoved { .. } => {}
|
||||
| InputEvent::TabletToolRemoved { .. }
|
||||
| InputEvent::TouchFrame => {}
|
||||
}
|
||||
match event {
|
||||
InputEvent::ConnectorPosition { .. }
|
||||
|
|
@ -274,6 +293,11 @@ impl WlSeatGlobal {
|
|||
InputEvent::TabletPadModeSwitch { .. } => {}
|
||||
InputEvent::TabletPadRing { .. } => {}
|
||||
InputEvent::TabletPadStrip { .. } => {}
|
||||
InputEvent::TouchDown { .. } => {}
|
||||
InputEvent::TouchUp { .. } => {}
|
||||
InputEvent::TouchMotion { .. } => {}
|
||||
InputEvent::TouchCancel { .. } => {}
|
||||
InputEvent::TouchFrame => {}
|
||||
}
|
||||
match event {
|
||||
InputEvent::Key {
|
||||
|
|
@ -407,6 +431,21 @@ impl WlSeatGlobal {
|
|||
source,
|
||||
position,
|
||||
} => self.tablet_event_pad_strip(pad, strip, source, position, time_usec),
|
||||
InputEvent::TouchDown {
|
||||
time_usec,
|
||||
id,
|
||||
x_normed,
|
||||
y_normed,
|
||||
} => self.touch_down(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed),
|
||||
InputEvent::TouchUp { time_usec, id } => self.touch_up(time_usec, id),
|
||||
InputEvent::TouchMotion {
|
||||
time_usec,
|
||||
id,
|
||||
x_normed,
|
||||
y_normed,
|
||||
} => self.touch_motion(time_usec, id, dev.get_rect(&self.state), x_normed, y_normed),
|
||||
InputEvent::TouchCancel { time_usec, id } => self.touch_cancel(time_usec, id),
|
||||
InputEvent::TouchFrame => self.touch_frame(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -613,6 +652,58 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
fn touch_down(
|
||||
self: &Rc<Self>,
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
rect: Rect,
|
||||
x_normed: Fixed,
|
||||
y_normed: Fixed,
|
||||
) {
|
||||
self.cursor_group().deactivate();
|
||||
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
||||
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_touch_down(self.id, time_usec, id, x, y);
|
||||
});
|
||||
self.touch_owner.down(self, time_usec, id, x, y);
|
||||
}
|
||||
|
||||
fn touch_up(self: &Rc<Self>, time_usec: u64, id: i32) {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_touch_up(self.id, time_usec, id);
|
||||
});
|
||||
self.touch_owner.up(self, time_usec, id);
|
||||
}
|
||||
|
||||
fn touch_motion(
|
||||
self: &Rc<Self>,
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
rect: Rect,
|
||||
x_normed: Fixed,
|
||||
y_normed: Fixed,
|
||||
) {
|
||||
self.cursor_group().deactivate();
|
||||
let x = Fixed::from_f64(rect.x1() as f64 + rect.width() as f64 * x_normed.to_f64());
|
||||
let y = Fixed::from_f64(rect.y1() as f64 + rect.height() as f64 * y_normed.to_f64());
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_touch_motion(self.id, time_usec, id, x, y);
|
||||
});
|
||||
self.touch_owner.motion(self, time_usec, id, x, y);
|
||||
}
|
||||
|
||||
fn touch_cancel(self: &Rc<Self>, time_usec: u64, id: i32) {
|
||||
self.state.for_each_seat_tester(|t| {
|
||||
t.send_touch_cancel(self.id, time_usec, id);
|
||||
});
|
||||
self.touch_owner.cancel(self);
|
||||
}
|
||||
|
||||
fn touch_frame(self: &Rc<Self>) {
|
||||
self.touch_owner.frame(self);
|
||||
}
|
||||
|
||||
pub(super) fn key_event<F>(
|
||||
self: &Rc<Self>,
|
||||
time_usec: u64,
|
||||
|
|
@ -744,7 +835,7 @@ impl WlSeatGlobal {
|
|||
self.kb_owner.set_kb_node(self, node);
|
||||
}
|
||||
|
||||
fn for_each_seat<C>(&self, ver: Version, client: ClientId, mut f: C)
|
||||
pub(super) fn for_each_seat<C>(&self, ver: Version, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlSeat>),
|
||||
{
|
||||
|
|
@ -794,6 +885,18 @@ impl WlSeatGlobal {
|
|||
})
|
||||
}
|
||||
|
||||
fn for_each_touch<C>(&self, ver: Version, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlTouch>),
|
||||
{
|
||||
self.for_each_seat(ver, client, |seat| {
|
||||
let touches = seat.touches.lock();
|
||||
for touch in touches.values() {
|
||||
f(touch);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn for_each_data_device<C>(&self, ver: Version, client: ClientId, mut f: C)
|
||||
where
|
||||
C: FnMut(&Rc<WlDataDevice>),
|
||||
|
|
@ -869,6 +972,16 @@ impl WlSeatGlobal {
|
|||
// client.flush();
|
||||
}
|
||||
|
||||
pub fn surface_touch_event<F>(&self, ver: Version, surface: &WlSurface, mut f: F)
|
||||
where
|
||||
F: FnMut(&Rc<WlTouch>),
|
||||
{
|
||||
let client = &surface.client;
|
||||
self.for_each_touch(ver, client.id, |p| {
|
||||
f(p);
|
||||
});
|
||||
}
|
||||
|
||||
fn cursor_moved(self: &Rc<Self>, time_usec: u64) {
|
||||
self.pos_time_usec.set(time_usec);
|
||||
self.changes.or_assign(CHANGE_CURSOR_MOVED);
|
||||
|
|
@ -1133,6 +1246,53 @@ impl WlSeatGlobal {
|
|||
}
|
||||
}
|
||||
|
||||
// Touch callbacks
|
||||
impl WlSeatGlobal {
|
||||
pub fn touch_down_surface(
|
||||
self: &Rc<Self>,
|
||||
surface: &WlSurface,
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let serial = surface.client.next_serial();
|
||||
let time = (time_usec / 1000) as _;
|
||||
self.surface_touch_event(Version::ALL, surface, |t| {
|
||||
t.send_down(serial, time, surface.id, id, x, y)
|
||||
});
|
||||
if let Some(node) = surface.get_focus_node(self.id) {
|
||||
self.focus_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn touch_up_surface(&self, surface: &WlSurface, time_usec: u64, id: i32) {
|
||||
let serial = surface.client.next_serial();
|
||||
let time = (time_usec / 1000) as _;
|
||||
self.surface_touch_event(Version::ALL, surface, |t| t.send_up(serial, time, id))
|
||||
}
|
||||
|
||||
pub fn touch_motion_surface(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
time_usec: u64,
|
||||
id: i32,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
let time = (time_usec / 1000) as _;
|
||||
self.surface_touch_event(Version::ALL, surface, |t| t.send_motion(time, id, x, y));
|
||||
}
|
||||
|
||||
pub fn touch_frame_surface(&self, surface: &WlSurface) {
|
||||
self.surface_touch_event(Version::ALL, surface, |t| t.send_frame())
|
||||
}
|
||||
|
||||
pub fn touch_cancel_surface(&self, surface: &WlSurface) {
|
||||
self.surface_touch_event(Version::ALL, surface, |t| t.send_cancel())
|
||||
}
|
||||
}
|
||||
|
||||
// Dnd callbacks
|
||||
impl WlSeatGlobal {
|
||||
pub fn dnd_surface_leave(&self, surface: &WlSurface, dnd: &Dnd) {
|
||||
|
|
|
|||
167
src/ifs/wl_seat/touch_owner.rs
Normal file
167
src/ifs/wl_seat/touch_owner.rs
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
use {
|
||||
crate::{
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::WlSeatGlobal,
|
||||
tree::{FindTreeUsecase, FoundNode, Node},
|
||||
utils::{clonecell::CloneCell, smallmap::SmallMap},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct TouchOwnerHolder {
|
||||
default: Rc<DefaultTouchOwner>,
|
||||
owner: CloneCell<Rc<dyn TouchOwner>>,
|
||||
}
|
||||
|
||||
impl Default for TouchOwnerHolder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
default: Rc::new(DefaultTouchOwner),
|
||||
owner: CloneCell::new(Rc::new(DefaultTouchOwner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TouchOwnerHolder {
|
||||
pub fn down(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||
self.owner.get().down(seat, time_usec, id, x, y)
|
||||
}
|
||||
|
||||
pub fn up(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32) {
|
||||
self.owner.get().up(seat, time_usec, id)
|
||||
}
|
||||
|
||||
pub fn motion(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||
self.owner.get().motion(seat, time_usec, id, x, y)
|
||||
}
|
||||
|
||||
pub fn frame(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.owner.get().frame(seat)
|
||||
}
|
||||
|
||||
pub fn cancel(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.owner.get().cancel(seat)
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.set_default_owner();
|
||||
}
|
||||
|
||||
fn set_default_owner(&self) {
|
||||
self.owner.set(self.default.clone());
|
||||
}
|
||||
}
|
||||
|
||||
struct DefaultTouchOwner;
|
||||
|
||||
struct GrabTouchOwner {
|
||||
node: Rc<dyn Node>,
|
||||
down_ids: SmallMap<i32, (), 10>,
|
||||
}
|
||||
|
||||
trait TouchOwner {
|
||||
fn down(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed);
|
||||
fn up(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32);
|
||||
fn motion(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed);
|
||||
fn frame(&self, seat: &Rc<WlSeatGlobal>);
|
||||
fn cancel(&self, seat: &Rc<WlSeatGlobal>);
|
||||
}
|
||||
|
||||
impl TouchOwner for DefaultTouchOwner {
|
||||
fn down(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||
let mut found_tree = seat.found_tree.borrow_mut();
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
found_tree.push(FoundNode {
|
||||
node: seat.state.root.clone(),
|
||||
x: x_int,
|
||||
y: y_int,
|
||||
});
|
||||
seat.state
|
||||
.root
|
||||
.node_find_tree_at(x_int, y_int, &mut found_tree, FindTreeUsecase::None);
|
||||
let node = found_tree.pop();
|
||||
found_tree.clear();
|
||||
drop(found_tree);
|
||||
if let Some(node) = node {
|
||||
node.node.node_seat_state().touch_begin(seat);
|
||||
let owner = Rc::new(GrabTouchOwner {
|
||||
node: node.node,
|
||||
down_ids: Default::default(),
|
||||
});
|
||||
seat.touch_owner.owner.set(owner.clone());
|
||||
owner.down(seat, time_usec, id, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
fn up(&self, _seat: &Rc<WlSeatGlobal>, _time_usec: u64, _id: i32) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn motion(&self, _seat: &Rc<WlSeatGlobal>, _time_usec: u64, _id: i32, _x: Fixed, _y: Fixed) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn frame(&self, _seat: &Rc<WlSeatGlobal>) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
fn cancel(&self, _seat: &Rc<WlSeatGlobal>) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
|
||||
impl GrabTouchOwner {
|
||||
fn translate(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
|
||||
let x_int = x.round_down();
|
||||
let y_int = y.round_down();
|
||||
let (x_int, y_int) = self.node.node_absolute_position().translate(x_int, y_int);
|
||||
(x.apply_fract(x_int), y.apply_fract(y_int))
|
||||
}
|
||||
|
||||
fn revert_to_default(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.node.node_seat_state().touch_end(seat);
|
||||
seat.touch_owner.set_default_owner();
|
||||
}
|
||||
}
|
||||
|
||||
impl TouchOwner for GrabTouchOwner {
|
||||
fn down(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||
if self.down_ids.insert(id, ()).is_some() {
|
||||
return;
|
||||
}
|
||||
let (x, y) = self.translate(x, y);
|
||||
self.node
|
||||
.clone()
|
||||
.node_on_touch_down(seat, time_usec, id, x, y);
|
||||
}
|
||||
|
||||
fn up(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32) {
|
||||
if self.down_ids.remove(&id).is_none() {
|
||||
return;
|
||||
}
|
||||
self.node.clone().node_on_touch_up(seat, time_usec, id);
|
||||
}
|
||||
|
||||
fn motion(&self, seat: &Rc<WlSeatGlobal>, time_usec: u64, id: i32, x: Fixed, y: Fixed) {
|
||||
if !self.down_ids.contains(&id) {
|
||||
return;
|
||||
}
|
||||
let (x, y) = self.translate(x, y);
|
||||
self.node
|
||||
.clone()
|
||||
.node_on_touch_motion(seat, time_usec, id, x, y);
|
||||
}
|
||||
|
||||
fn frame(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.node.node_on_touch_frame(seat);
|
||||
if self.down_ids.is_empty() {
|
||||
self.revert_to_default(seat);
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
self.node.node_on_touch_cancel(seat);
|
||||
self.revert_to_default(seat);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,20 @@
|
|||
use {
|
||||
crate::{
|
||||
client::ClientError,
|
||||
fixed::Fixed,
|
||||
ifs::wl_seat::WlSeat,
|
||||
leaks::Tracker,
|
||||
object::Object,
|
||||
wire::{wl_touch::*, WlTouchId},
|
||||
object::{Object, Version},
|
||||
wire::{wl_touch::*, WlSurfaceId, WlTouchId},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
const DOWN: u32 = 0;
|
||||
pub const SHAPE_SINCE_VERSION: Version = Version(6);
|
||||
#[allow(dead_code)]
|
||||
const UP: u32 = 1;
|
||||
#[allow(dead_code)]
|
||||
const MOTION: u32 = 2;
|
||||
#[allow(dead_code)]
|
||||
const FRAME: u32 = 3;
|
||||
#[allow(dead_code)]
|
||||
const CANCEL: u32 = 4;
|
||||
#[allow(dead_code)]
|
||||
const SHAPE: u32 = 5;
|
||||
#[allow(dead_code)]
|
||||
const ORIENTATION: u32 = 6;
|
||||
pub const ORIENTATION_DIRECTION_SINCE_VERSION: Version = Version(6);
|
||||
|
||||
pub struct WlTouch {
|
||||
id: WlTouchId,
|
||||
|
|
@ -39,12 +30,79 @@ impl WlTouch {
|
|||
tracker: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_down(
|
||||
&self,
|
||||
serial: u32,
|
||||
time: u32,
|
||||
surface: WlSurfaceId,
|
||||
id: i32,
|
||||
x: Fixed,
|
||||
y: Fixed,
|
||||
) {
|
||||
self.seat.client.event(Down {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
time,
|
||||
surface,
|
||||
id,
|
||||
x,
|
||||
y,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_up(&self, serial: u32, time: u32, id: i32) {
|
||||
self.seat.client.event(Up {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
time,
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_motion(&self, time: u32, id: i32, x: Fixed, y: Fixed) {
|
||||
self.seat.client.event(Motion {
|
||||
self_id: self.id,
|
||||
time,
|
||||
id,
|
||||
x,
|
||||
y,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn send_frame(&self) {
|
||||
self.seat.client.event(Frame { self_id: self.id })
|
||||
}
|
||||
|
||||
pub fn send_cancel(&self) {
|
||||
self.seat.client.event(Cancel { self_id: self.id })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn send_shape(&self, id: i32, major: Fixed, minor: Fixed) {
|
||||
self.seat.client.event(Shape {
|
||||
self_id: self.id,
|
||||
id,
|
||||
major,
|
||||
minor,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn send_orientation(&self, id: i32, orientation: Fixed) {
|
||||
self.seat.client.event(Orientation {
|
||||
self_id: self.id,
|
||||
id,
|
||||
orientation,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl WlTouchRequestHandler for WlTouch {
|
||||
type Error = WlTouchError;
|
||||
|
||||
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.seat.touches.remove(&self.id);
|
||||
self.seat.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue