mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2025-01-23 10:26:34 +11:00
Wayland: implement touch events (#342)
* wayland: move pointer to its own file * wayland: touch events support * update changelog
This commit is contained in:
parent
bbf13561b5
commit
c61f9b75f8
5 changed files with 313 additions and 174 deletions
|
@ -14,6 +14,7 @@
|
|||
- On Windows added `MonitorIdExt::hmonitor` method
|
||||
- Impl `Clone` for `EventsLoopProxy`
|
||||
- `EventsLoop::get_primary_monitor()` on X11 will fallback to any available monitor if no primary is found
|
||||
- Support for touch event on wayland
|
||||
|
||||
# Version 0.8.3 (2017-10-11)
|
||||
|
||||
|
|
|
@ -3,16 +3,16 @@ use std::collections::VecDeque;
|
|||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, EventsLoopClosed, ControlFlow};
|
||||
use {EventsLoopClosed, ControlFlow};
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::WindowId;
|
||||
use super::window::WindowStore;
|
||||
use super::keyboard::init_keyboard;
|
||||
|
||||
use wayland_client::{EnvHandler, EnvNotify, default_connect, EventQueue, EventQueueHandle, Proxy, StateToken};
|
||||
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
|
||||
wl_display, wl_registry, wl_output, wl_surface,
|
||||
wl_pointer, wl_keyboard};
|
||||
wl_pointer, wl_keyboard, wl_touch};
|
||||
|
||||
use super::wayland_window::{Frame, Shell, create_frame, FrameImplementation};
|
||||
use super::wayland_protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6;
|
||||
|
@ -132,6 +132,7 @@ impl EventsLoop {
|
|||
sink: sink.clone(),
|
||||
keyboard: None,
|
||||
pointer: None,
|
||||
touch: None,
|
||||
windows_token: store.clone()
|
||||
};
|
||||
|
||||
|
@ -439,6 +440,7 @@ struct SeatIData {
|
|||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
pointer: Option<wl_pointer::WlPointer>,
|
||||
keyboard: Option<wl_keyboard::WlKeyboard>,
|
||||
touch: Option<wl_touch::WlTouch>,
|
||||
windows_token: StateToken<WindowStore>
|
||||
}
|
||||
|
||||
|
@ -449,8 +451,11 @@ fn seat_implementation() -> wl_seat::Implementation<SeatIData> {
|
|||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Pointer) && idata.pointer.is_none() {
|
||||
let pointer = seat.get_pointer().expect("Seat is not dead");
|
||||
let p_idata = PointerIData::new(&idata.sink, idata.windows_token.clone());
|
||||
evqh.register(&pointer, pointer_implementation(), p_idata);
|
||||
let p_idata = super::pointer::PointerIData::new(
|
||||
&idata.sink,
|
||||
idata.windows_token.clone()
|
||||
);
|
||||
evqh.register(&pointer, super::pointer::pointer_implementation(), p_idata);
|
||||
idata.pointer = Some(pointer);
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
|
@ -471,179 +476,23 @@ fn seat_implementation() -> wl_seat::Implementation<SeatIData> {
|
|||
kbd.release();
|
||||
}
|
||||
}
|
||||
// TODO: Handle touch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PointerIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
mouse_focus: Option<WindowId>,
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
}
|
||||
|
||||
impl PointerIData {
|
||||
fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> PointerIData
|
||||
{
|
||||
PointerIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
mouse_focus: None,
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_implementation() -> wl_pointer::Implementation<PointerIData> {
|
||||
wl_pointer::Implementation {
|
||||
enter: |evqh, idata, _, _, surface, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
idata.mouse_focus = Some(wid);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::MouseEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
guard.send_event(
|
||||
Event::MouseMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
},
|
||||
wid,
|
||||
// create touch if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Touch) && idata.touch.is_none() {
|
||||
let touch = seat.get_touch().expect("Seat is not dead");
|
||||
let t_idata = super::touch::TouchIData::new(
|
||||
&idata.sink,
|
||||
idata.windows_token.clone()
|
||||
);
|
||||
evqh.register(&touch, super::touch::touch_implementation(), t_idata);
|
||||
idata.touch = Some(touch);
|
||||
}
|
||||
},
|
||||
leave: |evqh, idata, _, _, surface| {
|
||||
idata.mouse_focus = None;
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::MouseLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _, x, y| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y)
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
button: |_, idata, _, _, _, button, state| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
axis: |_, idata, pointer, _, axis, value| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.axis_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
// destroy touch if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Touch) {
|
||||
if let Some(touch) = idata.touch.take() {
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
frame: |_, idata, _| {
|
||||
let axis_buffer = idata.axis_buffer.take();
|
||||
let axis_discrete_buffer = idata.axis_discrete_buffer.take();
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
axis_source: |_, _, _, _| {},
|
||||
axis_stop: |_, idata, _, _, _| {
|
||||
idata.axis_state = TouchPhase::Ended;
|
||||
},
|
||||
axis_discrete: |_, idata, _, axis, discrete| {
|
||||
let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete
|
||||
}
|
||||
idata.axis_discrete_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ use wayland_client::protocol::wl_surface;
|
|||
use wayland_client::Proxy;
|
||||
|
||||
mod event_loop;
|
||||
mod pointer;
|
||||
mod touch;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
|
||||
|
|
181
src/platform/linux/wayland/pointer.rs
Normal file
181
src/platform/linux/wayland/pointer.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase};
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::{Proxy, StateToken};
|
||||
use wayland_client::protocol::wl_pointer;
|
||||
|
||||
pub struct PointerIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
mouse_focus: Option<WindowId>,
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
}
|
||||
|
||||
impl PointerIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> PointerIData
|
||||
{
|
||||
PointerIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
mouse_focus: None,
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Cancelled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_implementation() -> wl_pointer::Implementation<PointerIData> {
|
||||
wl_pointer::Implementation {
|
||||
enter: |evqh, idata, _, _, surface, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
idata.mouse_focus = Some(wid);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::MouseEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
guard.send_event(
|
||||
Event::MouseMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
leave: |evqh, idata, _, _, surface| {
|
||||
idata.mouse_focus = None;
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::MouseLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _, x, y| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (x, y)
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
button: |_, idata, _, _, _, button, state| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return
|
||||
};
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
},
|
||||
axis: |_, idata, pointer, _, axis, value| {
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = idata.axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
idata.axis_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
frame: |_, idata, _| {
|
||||
let axis_buffer = idata.axis_buffer.take();
|
||||
let axis_discrete_buffer = idata.axis_discrete_buffer.take();
|
||||
if let Some(wid) = idata.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
},
|
||||
wid
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
idata.sink.lock().unwrap().send_event(
|
||||
Event::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: idata.axis_state,
|
||||
},
|
||||
wid
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
axis_source: |_, _, _, _| {},
|
||||
axis_stop: |_, idata, _, _, _| {
|
||||
idata.axis_state = TouchPhase::Ended;
|
||||
},
|
||||
axis_discrete: |_, idata, _, axis, discrete| {
|
||||
let (mut x, mut y) = idata.axis_discrete_buffer.unwrap_or((0,0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete
|
||||
}
|
||||
idata.axis_discrete_buffer = Some((x,y));
|
||||
idata.axis_state = match idata.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
106
src/platform/linux/wayland/touch.rs
Normal file
106
src/platform/linux/wayland/touch.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {WindowEvent as Event, TouchPhase};
|
||||
|
||||
use super::{WindowId, DeviceId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use wayland_client::StateToken;
|
||||
use wayland_client::protocol::wl_touch;
|
||||
|
||||
pub struct TouchIData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
windows_token: StateToken<WindowStore>,
|
||||
pending_ids: Vec<TouchPoint>,
|
||||
}
|
||||
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
location: (f64, f64),
|
||||
id: i32
|
||||
}
|
||||
|
||||
impl TouchIData {
|
||||
pub fn new(sink: &Arc<Mutex<EventsLoopSink>>, token: StateToken<WindowStore>)
|
||||
-> TouchIData
|
||||
{
|
||||
TouchIData {
|
||||
sink: sink.clone(),
|
||||
windows_token: token,
|
||||
pending_ids: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn touch_implementation() -> wl_touch::Implementation<TouchIData> {
|
||||
wl_touch::Implementation {
|
||||
down: |evqh, idata, _, _serial, _time, surface, touch_id, x, y| {
|
||||
let wid = evqh.state().get(&idata.windows_token).find_wid(surface);
|
||||
if let Some(wid) = wid {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
idata.pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: touch_id
|
||||
});
|
||||
}
|
||||
},
|
||||
up: |_, idata, _, _serial, _time, touch_id| {
|
||||
let idx = idata.pending_ids.iter().position(|p| p.id == touch_id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = idata.pending_ids.remove(idx);
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
motion: |_, idata, _, _time, touch_id, x, y| {
|
||||
let pt = idata.pending_ids.iter_mut().find(|p| p.id == touch_id);
|
||||
if let Some(pt) = pt {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
pt.location = (x, y);
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
id: touch_id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
},
|
||||
frame: |_, _, _| {},
|
||||
cancel: |_, idata, _| {
|
||||
let mut guard = idata.sink.lock().unwrap();
|
||||
for pt in idata.pending_ids.drain(..) {
|
||||
guard.send_event(
|
||||
Event::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location,
|
||||
id: pt.id as u64
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue