diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index 5c408300bd..5f3e0da2b0 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -56,6 +56,9 @@ changelog entry. well. - On Android, add `{Active,}EventLoopExtAndroid::android_app()` to access the app used to create the loop. - Add `ActiveEventLoop::system_theme()`, returning the current system theme. +- Add `Touch::finger_id` with a new type `FingerId`. +- On Web and Windows, add `FingerIdExt*::is_primary()`, exposing a way to determine + the primary finger in a multi-touch interaction. ### Changed @@ -110,6 +113,7 @@ changelog entry. v0.5 support. v0.6 remains in place and is enabled by default. - Remove `DeviceEvent::Added` and `DeviceEvent::Removed`. - Remove `DeviceEvent::Motion` and `WindowEvent::AxisMotion`. +- Remove `Touch::id` in favor of `Touch::finger_id`. ### Fixed diff --git a/src/event.rs b/src/event.rs index 5bbd089d01..eb23117563 100644 --- a/src/event.rs +++ b/src/event.rs @@ -447,6 +447,26 @@ impl DeviceId { } } +/// Identifier of a finger in a touch event. +/// +/// Whenever a touch event is received it contains a `FingerId` which uniquely identifies the finger +/// used for the current interaction. +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId(pub(crate) platform_impl::FingerId); + +impl FingerId { + /// Returns a dummy id, useful for unit testing. + /// + /// # Notes + /// + /// The only guarantee made about the return value of this function is that + /// it will always be equal to itself and to future values returned by this function. + /// No other guarantees are made. This may be equal to a real `FingerId`. + pub const fn dummy() -> Self { + FingerId(platform_impl::FingerId::dummy()) + } +} + /// Represents raw hardware events that are not associated with any particular window. /// /// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera @@ -846,7 +866,7 @@ pub struct Touch { /// [android documentation](https://developer.android.com/reference/android/view/MotionEvent#AXIS_PRESSURE). pub force: Option, /// Unique identifier of a finger. - pub id: u64, + pub finger_id: FingerId, } /// Describes the force of a touch event @@ -1012,6 +1032,7 @@ mod tests { #[allow(unused_mut)] let mut x = $closure; let did = event::DeviceId::dummy(); + let fid = event::FingerId::dummy(); #[allow(deprecated)] { @@ -1075,7 +1096,7 @@ mod tests { device_id: did, phase: event::TouchPhase::Started, location: (0.0, 0.0).into(), - id: 0, + finger_id: fid, force: Some(event::Force::Normalized(0.0)), })); with_window_event(ThemeChanged(crate::window::Theme::Light)); @@ -1133,6 +1154,7 @@ mod tests { let _ = event::StartCause::Init.clone(); let did = crate::event::DeviceId::dummy().clone(); + let fid = crate::event::FingerId::dummy().clone(); HashSet::new().insert(did); let mut set = [did, did, did]; set.sort_unstable(); @@ -1148,7 +1170,7 @@ mod tests { device_id: did, phase: event::TouchPhase::Started, location: (0.0, 0.0).into(), - id: 0, + finger_id: fid, force: Some(event::Force::Normalized(0.0)), } .clone(); diff --git a/src/platform/web.rs b/src/platform/web.rs index f86140fb5a..a3242b0baa 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -56,6 +56,7 @@ use web_sys::HtmlCanvasElement; use crate::application::ApplicationHandler; use crate::cursor::CustomCursorSource; use crate::error::NotSupportedError; +use crate::event::FingerId; use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::monitor::MonitorHandle; use crate::platform_impl::PlatformCustomCursorSource; @@ -763,3 +764,16 @@ impl Display for OrientationLockError { } impl Error for OrientationLockError {} + +/// Additional methods on [`FingerId`] that are specific to Web. +pub trait FingerIdExtWeb { + /// Indicates if the finger represents the first contact in a multi-touch interaction. + #[allow(clippy::wrong_self_convention)] + fn is_primary(self) -> bool; +} + +impl FingerIdExtWeb for FingerId { + fn is_primary(self) -> bool { + self.0.is_primary() + } +} diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 3d8084f90f..bf235d24d1 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -7,7 +7,7 @@ use std::ffi::c_void; use std::path::Path; use crate::dpi::PhysicalSize; -use crate::event::DeviceId; +use crate::event::{DeviceId, FingerId}; use crate::event_loop::EventLoopBuilder; use crate::monitor::MonitorHandle; use crate::window::{BadIcon, Icon, Window, WindowAttributes}; @@ -668,6 +668,20 @@ impl DeviceIdExtWindows for DeviceId { } } +/// Additional methods on `FingerId` that are specific to Windows. +pub trait FingerIdExtWindows { + /// Indicates if the finger represents the first contact in a multi-touch interaction. + #[allow(clippy::wrong_self_convention)] + fn is_primary(self) -> bool; +} + +impl FingerIdExtWindows for FingerId { + #[inline] + fn is_primary(self) -> bool { + self.0.is_primary() + } +} + /// Additional methods on `Icon` that are specific to Windows. pub trait IconExtWindows: Sized { /// Create an icon from a file path. diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index df0f91f50a..ecfccd6e87 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -359,7 +359,7 @@ impl EventLoop { device_id, phase, location, - id: pointer.pointer_id() as u64, + finger_id: event::FingerId(FingerId(pointer.pointer_id())), force: Some(Force::Normalized(pointer.pressure() as f64)), }); @@ -705,6 +705,15 @@ impl DeviceId { } } +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct FingerId(i32); + +impl FingerId { + pub const fn dummy() -> Self { + FingerId(0) + } +} + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct PlatformSpecificWindowAttributes; diff --git a/src/platform_impl/apple/appkit/mod.rs b/src/platform_impl/apple/appkit/mod.rs index 2867814932..607f17e271 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -43,6 +43,15 @@ impl DeviceId { // Constant device ID; to be removed when if backend is updated to report real device IDs. pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId; + +impl FingerId { + pub const fn dummy() -> Self { + FingerId + } +} + #[derive(Debug)] pub enum OsError { CGError(core_graphics::base::CGError), diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs index 2c3221e041..417587e7db 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -38,6 +38,15 @@ impl DeviceId { pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId); +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId(usize); + +impl FingerId { + pub const fn dummy() -> Self { + FingerId(0) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct KeyEventExtra {} diff --git a/src/platform_impl/apple/uikit/view.rs b/src/platform_impl/apple/uikit/view.rs index 0c7fd39c0a..d946445eda 100644 --- a/src/platform_impl/apple/uikit/view.rs +++ b/src/platform_impl/apple/uikit/view.rs @@ -14,9 +14,9 @@ use objc2_ui_kit::{ use super::app_state::{self, EventWrapper}; use super::window::WinitUIWindow; -use super::DEVICE_ID; +use super::{FingerId, DEVICE_ID}; use crate::dpi::PhysicalPosition; -use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent}; +use crate::event::{Event, FingerId as RootFingerId, Force, Touch, TouchPhase, WindowEvent}; use crate::window::{WindowAttributes, WindowId as RootWindowId}; pub struct WinitViewState { @@ -480,7 +480,7 @@ impl WinitView { } else { None }; - let touch_id = touch as *const UITouch as u64; + let touch_id = touch as *const UITouch as usize; let phase = touch.phase(); let phase = match phase { UITouchPhase::Began => TouchPhase::Started, @@ -502,7 +502,7 @@ impl WinitView { window_id: RootWindowId(window.id()), event: WindowEvent::Touch(Touch { device_id: DEVICE_ID, - id: touch_id, + finger_id: RootFingerId(FingerId(touch_id)), location: physical_location, force, phase, diff --git a/src/platform_impl/linux/mod.rs b/src/platform_impl/linux/mod.rs index 3d51c77f0d..f347633514 100644 --- a/src/platform_impl/linux/mod.rs +++ b/src/platform_impl/linux/mod.rs @@ -177,6 +177,23 @@ impl DeviceId { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum FingerId { + #[cfg(x11_platform)] + X(x11::FingerId), + #[cfg(wayland_platform)] + Wayland(wayland::FingerId), +} + +impl FingerId { + pub const fn dummy() -> Self { + #[cfg(wayland_platform)] + return FingerId::Wayland(wayland::FingerId::dummy()); + #[cfg(all(not(wayland_platform), x11_platform))] + return FingerId::X(x11::FingerId::dummy()); + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum MonitorHandle { #[cfg(x11_platform)] diff --git a/src/platform_impl/linux/wayland/mod.rs b/src/platform_impl/linux/wayland/mod.rs index 67e6e90d1e..18de24277d 100644 --- a/src/platform_impl/linux/wayland/mod.rs +++ b/src/platform_impl/linux/wayland/mod.rs @@ -71,6 +71,15 @@ impl DeviceId { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId(i32); + +impl FingerId { + pub const fn dummy() -> Self { + FingerId(0) + } +} + /// Get the WindowId out of the surface. #[inline] fn make_wid(surface: &WlSurface) -> WindowId { diff --git a/src/platform_impl/linux/wayland/seat/touch/mod.rs b/src/platform_impl/linux/wayland/seat/touch/mod.rs index f4d2c14de5..665279f019 100644 --- a/src/platform_impl/linux/wayland/seat/touch/mod.rs +++ b/src/platform_impl/linux/wayland/seat/touch/mod.rs @@ -10,7 +10,7 @@ use tracing::warn; use crate::dpi::LogicalPosition; use crate::event::{Touch, TouchPhase, WindowEvent}; use crate::platform_impl::wayland::state::WinitState; -use crate::platform_impl::wayland::{self, DeviceId}; +use crate::platform_impl::wayland::{self, DeviceId, FingerId}; impl TouchHandler for WinitState { fn down( @@ -50,7 +50,9 @@ impl TouchHandler for WinitState { phase: TouchPhase::Started, location: location.to_physical(scale_factor), force: None, - id: id as u64, + finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( + FingerId(id), + )), }), window_id, ); @@ -93,7 +95,9 @@ impl TouchHandler for WinitState { phase: TouchPhase::Ended, location: touch_point.location.to_physical(scale_factor), force: None, - id: id as u64, + finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( + FingerId(id), + )), }), window_id, ); @@ -138,7 +142,9 @@ impl TouchHandler for WinitState { phase: TouchPhase::Moved, location: touch_point.location.to_physical(scale_factor), force: None, - id: id as u64, + finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( + FingerId(id), + )), }), window_id, ); @@ -170,7 +176,9 @@ impl TouchHandler for WinitState { phase: TouchPhase::Cancelled, location, force: None, - id: id as u64, + finger_id: crate::event::FingerId(crate::platform_impl::FingerId::Wayland( + FingerId(id), + )), }), window_id, ); diff --git a/src/platform_impl/linux/x11/event_processor.rs b/src/platform_impl/linux/x11/event_processor.rs index a80e540ac3..09bb02912c 100644 --- a/src/platform_impl/linux/x11/event_processor.rs +++ b/src/platform_impl/linux/x11/event_processor.rs @@ -33,8 +33,8 @@ use crate::platform_impl::platform::x11::ActiveEventLoop; use crate::platform_impl::x11::atoms::*; use crate::platform_impl::x11::util::cookie::GenericEventCookie; use crate::platform_impl::x11::{ - mkdid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState, ImeReceiver, - ScrollOrientation, UnownedWindow, WindowId, + mkdid, mkfid, mkwid, util, CookieResultExt, Device, DeviceId, DeviceInfo, Dnd, DndState, + ImeReceiver, ScrollOrientation, UnownedWindow, WindowId, }; /// The maximum amount of X modifiers to replay. @@ -60,7 +60,7 @@ pub struct EventProcessor { // // Used to detect key repeats. pub held_key_press: Option, - pub first_touch: Option, + pub first_touch: Option, // Currently focused window belonging to this process pub active_window: Option, /// Latest modifiers we've sent for the user to trigger change in event. @@ -1313,7 +1313,7 @@ impl EventProcessor { let window = xev.event as xproto::Window; if self.window_exists(window) { let window_id = mkwid(window); - let id = xev.detail as u64; + let id = xev.detail as u32; let location = PhysicalPosition::new(xev.event_x, xev.event_y); // Mouse cursor position changes when touch events are received. @@ -1336,7 +1336,7 @@ impl EventProcessor { phase, location, force: None, // TODO - id, + finger_id: mkfid(id), }), }; callback(&self.target, event) @@ -1761,7 +1761,7 @@ impl EventProcessor { } } -fn is_first_touch(first: &mut Option, num: &mut u32, id: u64, phase: TouchPhase) -> bool { +fn is_first_touch(first: &mut Option, num: &mut u32, id: u32, phase: TouchPhase) -> bool { match phase { TouchPhase::Started => { if *num == 0 { diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index e669d155b5..247f8f1609 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -822,6 +822,16 @@ impl DeviceId { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId(u32); + +impl FingerId { + #[allow(unused)] + pub const fn dummy() -> Self { + FingerId(0) + } +} + pub(crate) struct Window(Arc); impl Deref for Window { @@ -1027,6 +1037,10 @@ fn mkdid(w: xinput::DeviceId) -> crate::event::DeviceId { crate::event::DeviceId(crate::platform_impl::DeviceId::X(DeviceId(w))) } +fn mkfid(w: u32) -> crate::event::FingerId { + crate::event::FingerId(crate::platform_impl::FingerId::X(FingerId(w))) +} + #[derive(Debug)] pub struct Device { _name: String, diff --git a/src/platform_impl/orbital/mod.rs b/src/platform_impl/orbital/mod.rs index 2cfdd370de..34a8555309 100644 --- a/src/platform_impl/orbital/mod.rs +++ b/src/platform_impl/orbital/mod.rs @@ -127,6 +127,15 @@ impl DeviceId { } } +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct FingerId; + +impl FingerId { + pub const fn dummy() -> Self { + FingerId + } +} + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct PlatformSpecificWindowAttributes; diff --git a/src/platform_impl/web/device.rs b/src/platform_impl/web/device.rs deleted file mode 100644 index 91d08586c8..0000000000 --- a/src/platform_impl/web/device.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(pub i32); - -impl DeviceId { - pub const fn dummy() -> Self { - Self(0) - } -} diff --git a/src/platform_impl/web/event.rs b/src/platform_impl/web/event.rs new file mode 100644 index 0000000000..937702b118 --- /dev/null +++ b/src/platform_impl/web/event.rs @@ -0,0 +1,32 @@ +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DeviceId(i32); + +impl DeviceId { + pub fn new(pointer_id: i32) -> Self { + Self(pointer_id) + } + + pub const fn dummy() -> Self { + Self(-1) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId { + pointer_id: i32, + primary: bool, +} + +impl FingerId { + pub fn new(pointer_id: i32, primary: bool) -> Self { + Self { pointer_id, primary } + } + + pub const fn dummy() -> Self { + Self { pointer_id: -1, primary: false } + } + + pub fn is_primary(self) -> bool { + self.primary + } +} diff --git a/src/platform_impl/web/event_loop/mod.rs b/src/platform_impl/web/event_loop/mod.rs index b9c47a4b2d..89721cac8d 100644 --- a/src/platform_impl/web/event_loop/mod.rs +++ b/src/platform_impl/web/event_loop/mod.rs @@ -1,4 +1,4 @@ -use super::{backend, device, window, HasMonitorPermissionFuture, MonitorPermissionFuture}; +use super::{backend, event, window, HasMonitorPermissionFuture, MonitorPermissionFuture}; use crate::application::ApplicationHandler; use crate::error::{EventLoopError, NotSupportedError}; use crate::event::Event; diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index b3760e584a..56d8a807d0 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -287,7 +287,7 @@ impl Shared { } // chorded button event - let device_id = RootDeviceId(DeviceId(event.pointer_id())); + let device_id = RootDeviceId(DeviceId::new(event.pointer_id())); if let Some(button) = backend::event::mouse_button(&event) { let state = if backend::event::mouse_buttons(&event).contains(button.into()) { @@ -328,7 +328,7 @@ impl Shared { if let Some(delta) = backend::event::mouse_scroll_delta(&window, &event) { runner.send_event(Event::DeviceEvent { - device_id: RootDeviceId(DeviceId(0)), + device_id: RootDeviceId(DeviceId::dummy()), event: DeviceEvent::MouseWheel { delta }, }); } @@ -345,7 +345,7 @@ impl Shared { let button = backend::event::mouse_button(&event).expect("no mouse button pressed"); runner.send_event(Event::DeviceEvent { - device_id: RootDeviceId(DeviceId(event.pointer_id())), + device_id: RootDeviceId(DeviceId::new(event.pointer_id())), event: DeviceEvent::Button { button: button.to_id(), state: ElementState::Pressed, @@ -364,7 +364,7 @@ impl Shared { let button = backend::event::mouse_button(&event).expect("no mouse button pressed"); runner.send_event(Event::DeviceEvent { - device_id: RootDeviceId(DeviceId(event.pointer_id())), + device_id: RootDeviceId(DeviceId::new(event.pointer_id())), event: DeviceEvent::Button { button: button.to_id(), state: ElementState::Released, diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 86e94ccea3..8643c696dd 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -8,13 +8,14 @@ use web_sys::Element; use super::super::monitor::MonitorPermissionFuture; use super::super::{lock, KeyEventExtra}; -use super::device::DeviceId; +use super::event::DeviceId; use super::runner::{EventWrapper, WeakShared}; use super::window::WindowId; use super::{backend, runner, EventLoopProxy}; use crate::error::{ExternalError, NotSupportedError}; use crate::event::{ - DeviceId as RootDeviceId, ElementState, Event, KeyEvent, Touch, TouchPhase, WindowEvent, + DeviceId as RootDeviceId, ElementState, Event, FingerId as RootFingerId, KeyEvent, Touch, + TouchPhase, WindowEvent, }; use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, @@ -220,11 +221,9 @@ impl ActiveEventLoop { } }); - let pointer = pointer_id.map(|pointer_id| Event::WindowEvent { + let pointer = pointer_id.map(|device_id| Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::CursorLeft { - device_id: RootDeviceId(DeviceId(pointer_id)), - }, + event: WindowEvent::CursorLeft { device_id: RootDeviceId(device_id) }, }); if focus.is_some() || pointer.is_some() { @@ -247,11 +246,9 @@ impl ActiveEventLoop { } }); - let pointer = pointer_id.map(|pointer_id| Event::WindowEvent { + let pointer = pointer_id.map(|device_id| Event::WindowEvent { window_id: RootWindowId(id), - event: WindowEvent::CursorEntered { - device_id: RootDeviceId(DeviceId(pointer_id)), - }, + event: WindowEvent::CursorEntered { device_id: RootDeviceId(device_id) }, }); if focus.is_some() || pointer.is_some() { @@ -277,7 +274,7 @@ impl ActiveEventLoop { }); runner.send_events(modifiers.into_iter().chain(events.flat_map(|position| { - let device_id = RootDeviceId(DeviceId(pointer_id)); + let device_id = RootDeviceId(pointer_id); iter::once(Event::WindowEvent { window_id: RootWindowId(id), @@ -291,7 +288,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, events| { + move |active_modifiers, device_id, finger_id, events| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -305,8 +302,8 @@ impl ActiveEventLoop { |(location, force)| Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + finger_id: RootFingerId(finger_id), + device_id: RootDeviceId(device_id), phase: TouchPhase::Moved, force: Some(force), location, @@ -321,7 +318,7 @@ impl ActiveEventLoop { let modifiers = self.modifiers.clone(); move |active_modifiers, - pointer_id, + device_id, position: crate::dpi::PhysicalPosition, buttons, button| { @@ -334,7 +331,7 @@ impl ActiveEventLoop { } }); - let device_id = RootDeviceId(DeviceId(pointer_id)); + let device_id = RootDeviceId(device_id); let state = if buttons.contains(button.into()) { ElementState::Pressed @@ -373,7 +370,7 @@ impl ActiveEventLoop { } }); - let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id)); + let device_id: RootDeviceId = RootDeviceId(pointer_id); // A mouse down event may come in without any prior CursorMoved events, // therefore we should send a CursorMoved event to make sure that the @@ -398,7 +395,7 @@ impl ActiveEventLoop { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, location, force| { + move |active_modifiers, device_id, finger_id, location, force| { let modifiers = (modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -411,8 +408,8 @@ impl ActiveEventLoop { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + finger_id: RootFingerId(finger_id), + device_id: RootDeviceId(device_id), phase: TouchPhase::Started, force: Some(force), location, @@ -439,7 +436,7 @@ impl ActiveEventLoop { } }); - let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id)); + let device_id: RootDeviceId = RootDeviceId(pointer_id); // A mouse up event may come in without any prior CursorMoved events, // therefore we should send a CursorMoved event to make sure that the @@ -465,7 +462,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, location, force| { + move |active_modifiers, device_id, finger_id, location, force| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -479,8 +476,8 @@ impl ActiveEventLoop { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + finger_id: RootFingerId(finger_id), + device_id: RootDeviceId(device_id), phase: TouchPhase::Ended, force: Some(force), location, @@ -493,7 +490,7 @@ impl ActiveEventLoop { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - canvas.on_mouse_wheel(move |pointer_id, delta, active_modifiers| { + canvas.on_mouse_wheel(move |delta, active_modifiers| { let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -507,7 +504,7 @@ impl ActiveEventLoop { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::MouseWheel { - device_id: RootDeviceId(DeviceId(pointer_id)), + device_id: RootDeviceId(DeviceId::dummy()), delta, phase: TouchPhase::Moved, }, @@ -516,12 +513,12 @@ impl ActiveEventLoop { }); let runner = self.runner.clone(); - canvas.on_touch_cancel(move |device_id, location, force| { + canvas.on_touch_cancel(move |device_id, finger_id, location, force| { runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + finger_id: RootFingerId(finger_id), + device_id: RootDeviceId(device_id), phase: TouchPhase::Cancelled, force: Some(force), location, diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index 325ab478aa..d6a8dd77d1 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -22,8 +22,8 @@ mod r#async; mod cursor; -mod device; mod error; +mod event; mod event_loop; mod keyboard; mod lock; @@ -37,8 +37,8 @@ pub(crate) use cursor::{ CustomCursorSource as PlatformCustomCursorSource, }; -pub use self::device::DeviceId; pub use self::error::OsError; +pub use self::event::{DeviceId, FingerId}; pub(crate) use self::event_loop::{ ActiveEventLoop, EventLoop, EventLoopProxy, OwnedDisplayHandle, PlatformSpecificEventLoopAttributes, diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index 10fd654723..e7d95d84dc 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -12,6 +12,7 @@ use web_sys::{ }; use super::super::cursor::CursorHandler; +use super::super::event::{DeviceId, FingerId}; use super::super::main_thread::MainThreadMarker; use super::super::WindowId; use super::animation_frame::AnimationFrameHandler; @@ -329,22 +330,22 @@ impl Canvas { pub fn on_cursor_leave(&self, handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.handlers.borrow_mut().pointer_handler.on_cursor_leave(&self.common, handler) } pub fn on_cursor_enter(&self, handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.handlers.borrow_mut().pointer_handler.on_cursor_enter(&self.common, handler) } pub fn on_mouse_release(&self, mouse_handler: M, touch_handler: T) where - M: 'static + FnMut(ModifiersState, i32, PhysicalPosition, MouseButton), - T: 'static + FnMut(ModifiersState, i32, PhysicalPosition, Force), + M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition, MouseButton), + T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition, Force), { self.handlers.borrow_mut().pointer_handler.on_mouse_release( &self.common, @@ -355,8 +356,8 @@ impl Canvas { pub fn on_mouse_press(&self, mouse_handler: M, touch_handler: T) where - M: 'static + FnMut(ModifiersState, i32, PhysicalPosition, MouseButton), - T: 'static + FnMut(ModifiersState, i32, PhysicalPosition, Force), + M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition, MouseButton), + T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition, Force), { self.handlers.borrow_mut().pointer_handler.on_mouse_press( &self.common, @@ -368,10 +369,17 @@ impl Canvas { pub fn on_cursor_move(&self, mouse_handler: M, touch_handler: T, button_handler: B) where - M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator>), + M: 'static + + FnMut(ModifiersState, DeviceId, &mut dyn Iterator>), T: 'static - + FnMut(ModifiersState, i32, &mut dyn Iterator, Force)>), - B: 'static + FnMut(ModifiersState, i32, PhysicalPosition, ButtonsState, MouseButton), + + FnMut( + ModifiersState, + DeviceId, + FingerId, + &mut dyn Iterator, Force)>, + ), + B: 'static + + FnMut(ModifiersState, DeviceId, PhysicalPosition, ButtonsState, MouseButton), { self.handlers.borrow_mut().pointer_handler.on_cursor_move( &self.common, @@ -384,14 +392,14 @@ impl Canvas { pub fn on_touch_cancel(&self, handler: F) where - F: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(DeviceId, FingerId, PhysicalPosition, Force), { self.handlers.borrow_mut().pointer_handler.on_touch_cancel(&self.common, handler) } pub fn on_mouse_wheel(&self, mut handler: F) where - F: 'static + FnMut(i32, MouseScrollDelta, ModifiersState), + F: 'static + FnMut(MouseScrollDelta, ModifiersState), { let window = self.common.window.clone(); let prevent_default = Rc::clone(&self.prevent_default); @@ -403,7 +411,7 @@ impl Canvas { if let Some(delta) = event::mouse_scroll_delta(&window, &event) { let modifiers = event::mouse_modifiers(&event); - handler(0, delta, modifiers); + handler(delta, modifiers); } })); } diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index 14b6722399..6679cbd2b0 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use event::ButtonsState; use web_sys::PointerEvent; +use super::super::event::{DeviceId, FingerId}; use super::canvas::Common; use super::event; use super::event_handle::EventListenerHandle; @@ -35,7 +36,7 @@ impl PointerHandler { pub fn on_cursor_leave(&mut self, canvas_common: &Common, mut handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.on_cursor_leave = Some(canvas_common.add_event("pointerout", move |event: PointerEvent| { @@ -44,15 +45,16 @@ impl PointerHandler { // touch events are handled separately // handling them here would produce duplicate mouse events, inconsistent with // other platforms. - let pointer_id = (event.pointer_type() != "touch").then(|| event.pointer_id()); + let device_id = + (event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id())); - handler(modifiers, pointer_id); + handler(modifiers, device_id); })); } pub fn on_cursor_enter(&mut self, canvas_common: &Common, mut handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.on_cursor_enter = Some(canvas_common.add_event("pointerover", move |event: PointerEvent| { @@ -61,9 +63,10 @@ impl PointerHandler { // touch events are handled separately // handling them here would produce duplicate mouse events, inconsistent with // other platforms. - let pointer_id = (event.pointer_type() != "touch").then(|| event.pointer_id()); + let device_id = + (event.pointer_type() != "touch").then(|| DeviceId::new(event.pointer_id())); - handler(modifiers, pointer_id); + handler(modifiers, device_id); })); } @@ -73,8 +76,8 @@ impl PointerHandler { mut mouse_handler: M, mut touch_handler: T, ) where - M: 'static + FnMut(ModifiersState, i32, PhysicalPosition, MouseButton), - T: 'static + FnMut(ModifiersState, i32, PhysicalPosition, Force), + M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition, MouseButton), + T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition, Force), { let window = canvas_common.window.clone(); self.on_pointer_release = @@ -82,15 +85,19 @@ impl PointerHandler { let modifiers = event::mouse_modifiers(&event); match event.pointer_type().as_str() { - "touch" => touch_handler( - modifiers, - event.pointer_id(), - event::mouse_position(&event).to_physical(super::scale_factor(&window)), - Force::Normalized(event.pressure() as f64), - ), + "touch" => { + let pointer_id = event.pointer_id(); + touch_handler( + modifiers, + DeviceId::new(pointer_id), + FingerId::new(pointer_id, event.is_primary()), + event::mouse_position(&event).to_physical(super::scale_factor(&window)), + Force::Normalized(event.pressure() as f64), + ) + }, _ => mouse_handler( modifiers, - event.pointer_id(), + DeviceId::new(event.pointer_id()), event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_button(&event).expect("no mouse button released"), ), @@ -105,8 +112,8 @@ impl PointerHandler { mut touch_handler: T, prevent_default: Rc>, ) where - M: 'static + FnMut(ModifiersState, i32, PhysicalPosition, MouseButton), - T: 'static + FnMut(ModifiersState, i32, PhysicalPosition, Force), + M: 'static + FnMut(ModifiersState, DeviceId, PhysicalPosition, MouseButton), + T: 'static + FnMut(ModifiersState, DeviceId, FingerId, PhysicalPosition, Force), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); @@ -124,17 +131,21 @@ impl PointerHandler { match pointer_type.as_str() { "touch" => { + let pointer_id = event.pointer_id(); touch_handler( modifiers, - event.pointer_id(), + DeviceId::new(pointer_id), + FingerId::new(pointer_id, event.is_primary()), event::mouse_position(&event).to_physical(super::scale_factor(&window)), Force::Normalized(event.pressure() as f64), ); }, _ => { + let pointer_id = event.pointer_id(); + mouse_handler( modifiers, - event.pointer_id(), + DeviceId::new(pointer_id), event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_button(&event).expect("no mouse button pressed"), ); @@ -145,7 +156,7 @@ impl PointerHandler { // grabbed, and there is probably not a // situation where this could fail, that we // care if it fails. - let _e = canvas.set_pointer_capture(event.pointer_id()); + let _e = canvas.set_pointer_capture(pointer_id); } }, } @@ -160,18 +171,25 @@ impl PointerHandler { mut button_handler: B, prevent_default: Rc>, ) where - M: 'static + FnMut(ModifiersState, i32, &mut dyn Iterator>), + M: 'static + + FnMut(ModifiersState, DeviceId, &mut dyn Iterator>), T: 'static - + FnMut(ModifiersState, i32, &mut dyn Iterator, Force)>), - B: 'static + FnMut(ModifiersState, i32, PhysicalPosition, ButtonsState, MouseButton), + + FnMut( + ModifiersState, + DeviceId, + FingerId, + &mut dyn Iterator, Force)>, + ), + B: 'static + + FnMut(ModifiersState, DeviceId, PhysicalPosition, ButtonsState, MouseButton), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); self.on_cursor_move = Some(canvas_common.add_event("pointermove", move |event: PointerEvent| { let modifiers = event::mouse_modifiers(&event); - - let id = event.pointer_id(); + let pointer_id = event.pointer_id(); + let device_id = DeviceId::new(pointer_id); // chorded button event if let Some(button) = event::mouse_button(&event) { @@ -184,7 +202,7 @@ impl PointerHandler { button_handler( modifiers, - id, + device_id, event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_buttons(&event), button, @@ -198,7 +216,8 @@ impl PointerHandler { match event.pointer_type().as_str() { "touch" => touch_handler( modifiers, - id, + device_id, + FingerId::new(pointer_id, event.is_primary()), &mut event::pointer_move_event(event).map(|event| { ( event::mouse_position(&event).to_physical(scale), @@ -208,7 +227,7 @@ impl PointerHandler { ), _ => mouse_handler( modifiers, - id, + device_id, &mut event::pointer_move_event(event) .map(|event| event::mouse_position(&event).to_physical(scale)), ), @@ -218,14 +237,16 @@ impl PointerHandler { pub fn on_touch_cancel(&mut self, canvas_common: &Common, mut handler: F) where - F: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(DeviceId, FingerId, PhysicalPosition, Force), { let window = canvas_common.window.clone(); self.on_touch_cancel = Some(canvas_common.add_event("pointercancel", move |event: PointerEvent| { if event.pointer_type() == "touch" { + let pointer_id = event.pointer_id(); handler( - event.pointer_id(), + DeviceId::new(pointer_id), + FingerId::new(pointer_id, event.is_primary()), event::mouse_position(&event).to_physical(super::scale_factor(&window)), Force::Normalized(event.pressure() as f64), ); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index eb2fef2d40..eeb748ce5c 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -25,11 +25,11 @@ use windows_sys::Win32::UI::Input::KeyboardAndMouse::{ ReleaseCapture, SetCapture, TrackMouseEvent, TME_LEAVE, TRACKMOUSEEVENT, }; use windows_sys::Win32::UI::Input::Pointer::{ - POINTER_FLAG_DOWN, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, + POINTER_FLAG_DOWN, POINTER_FLAG_PRIMARY, POINTER_FLAG_UP, POINTER_FLAG_UPDATE, }; use windows_sys::Win32::UI::Input::Touch::{ - CloseTouchInputHandle, GetTouchInputInfo, TOUCHEVENTF_DOWN, TOUCHEVENTF_MOVE, TOUCHEVENTF_UP, - TOUCHINPUT, + CloseTouchInputHandle, GetTouchInputInfo, TOUCHEVENTF_DOWN, TOUCHEVENTF_MOVE, + TOUCHEVENTF_PRIMARY, TOUCHEVENTF_UP, TOUCHINPUT, }; use windows_sys::Win32::UI::Input::{RAWINPUT, RIM_TYPEKEYBOARD, RIM_TYPEMOUSE}; use windows_sys::Win32::UI::WindowsAndMessaging::{ @@ -58,7 +58,8 @@ use crate::application::ApplicationHandler; use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::error::{EventLoopError, ExternalError, OsError}; use crate::event::{ - Event, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, WindowEvent, + Event, FingerId as RootFingerId, Force, Ime, InnerSizeWriter, RawKeyEvent, Touch, TouchPhase, + WindowEvent, }; use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, @@ -80,7 +81,7 @@ use crate::platform_impl::platform::window_state::{ CursorFlags, ImeState, WindowFlags, WindowState, }; use crate::platform_impl::platform::{ - raw_input, util, wrap_device_id, Fullscreen, WindowId, DEVICE_ID, + raw_input, util, wrap_device_id, FingerId, Fullscreen, WindowId, DEVICE_ID, }; use crate::platform_impl::Window; use crate::utils::Lazy; @@ -1830,7 +1831,10 @@ unsafe fn public_window_callback_inner( }, location, force: None, // WM_TOUCH doesn't support pressure information - id: input.dwID as u64, + finger_id: RootFingerId(FingerId { + id: input.dwID, + primary: util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY), + }), device_id: DEVICE_ID, }), }); @@ -1976,7 +1980,13 @@ unsafe fn public_window_callback_inner( }, location, force, - id: pointer_info.pointerId as u64, + finger_id: RootFingerId(FingerId { + id: pointer_info.pointerId, + primary: util::has_flag( + pointer_info.pointerFlags, + POINTER_FLAG_PRIMARY, + ), + }), device_id: DEVICE_ID, }), }); diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index abd9ed1605..9c98c3a01e 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -78,6 +78,24 @@ impl DeviceId { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct FingerId { + id: u32, + primary: bool, +} + +impl FingerId { + pub const fn dummy() -> Self { + FingerId { id: 0, primary: false } + } +} + +impl FingerId { + pub fn is_primary(self) -> bool { + self.primary + } +} + // Constant device ID, to be removed when this backend is updated to report real device IDs. const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId(0));