From 54400f00213d8e9d6b1b3f70b81d9040cb85b96a Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 10 Jul 2024 13:37:30 +0200 Subject: [PATCH] Web: implement `DeviceIdExtWeb::is_primary()` --- src/changelog/unreleased.md | 2 + src/platform/web.rs | 17 ++++++ src/platform_impl/web/device.rs | 24 +++++++-- src/platform_impl/web/event_loop/runner.rs | 12 ++--- .../web/event_loop/window_target.rs | 52 +++++++++--------- src/platform_impl/web/web_sys/canvas.rs | 31 ++++++----- src/platform_impl/web/web_sys/pointer.rs | 53 +++++++++++-------- 7 files changed, 119 insertions(+), 72 deletions(-) diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index d74090d132..8b380d1c2f 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -43,6 +43,8 @@ changelog entry. ### Added - Add `ActiveEventLoop::create_proxy()`. +- On Web, add `DeviceIdExtWeb::is_primary()`, exposing a way to determine the + primary pointer device. ### Changed diff --git a/src/platform/web.rs b/src/platform/web.rs index 1dd612ce24..6eeaab89df 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -54,6 +54,7 @@ use web_sys::HtmlCanvasElement; use crate::application::ApplicationHandler; use crate::cursor::CustomCursorSource; +use crate::event::DeviceId; use crate::event_loop::{ActiveEventLoop, EventLoop}; #[cfg(web_platform)] use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture; @@ -437,3 +438,19 @@ impl Display for CustomCursorError { } } } + +/// Additional methods on [`DeviceId``] that are specific to Web. +pub trait DeviceIdExtWeb { + /// Indicates if the pointer represents the primary pointer of a pointer type. For non-pointer + /// devices this always returns [`true`]. + /// + /// This is relevant in multi-touch scenarios, where the the first finger to touch the screen + /// becomes the primary pointer. + fn is_primary(&self) -> bool; +} + +impl DeviceIdExtWeb for DeviceId { + fn is_primary(&self) -> bool { + self.0.is_primary() + } +} diff --git a/src/platform_impl/web/device.rs b/src/platform_impl/web/device.rs index 383bf3a1b8..cc7434e4c0 100644 --- a/src/platform_impl/web/device.rs +++ b/src/platform_impl/web/device.rs @@ -1,8 +1,26 @@ +use web_sys::PointerEvent; + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DeviceId(pub i32); +pub struct DeviceId { + pub id: Option, + pub primary: bool, +} impl DeviceId { - pub const unsafe fn dummy() -> Self { - Self(0) + pub fn new(event: &PointerEvent) -> Self { + Self { + // We assume that browsers don't use negative numbers unless they are representing the + // reserved `-1` for non-pointing devices. + id: event.pointer_id().try_into().ok(), + primary: event.is_primary(), + } + } + + pub const fn dummy() -> Self { + Self { id: None, primary: true } + } + + pub const fn is_primary(&self) -> bool { + self.primary } } diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index c7275dcc3e..c8938d301b 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -251,7 +251,7 @@ impl Shared { } // chorded button event - let device_id = RootDeviceId(DeviceId(event.pointer_id())); + let device_id = RootDeviceId(DeviceId::new(&event)); if let Some(button) = backend::event::mouse_button(&event) { debug_assert_eq!( @@ -307,7 +307,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 }, }); } @@ -328,7 +328,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)), event: DeviceEvent::Button { button: button.to_id(), state: ElementState::Pressed, @@ -351,7 +351,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)), event: DeviceEvent::Button { button: button.to_id(), state: ElementState::Released, @@ -369,7 +369,7 @@ impl Shared { } runner.send_event(Event::DeviceEvent { - device_id: RootDeviceId(unsafe { DeviceId::dummy() }), + device_id: RootDeviceId(DeviceId::dummy()), event: DeviceEvent::Key(RawKeyEvent { physical_key: backend::event::key_code(&event), state: ElementState::Pressed, @@ -387,7 +387,7 @@ impl Shared { } runner.send_event(Event::DeviceEvent { - device_id: RootDeviceId(unsafe { DeviceId::dummy() }), + device_id: RootDeviceId(DeviceId::dummy()), event: DeviceEvent::Key(RawKeyEvent { physical_key: backend::event::key_code(&event), 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 9df6748cf5..c82c7d5a64 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -150,7 +150,7 @@ impl ActiveEventLoop { } }); - let device_id = RootDeviceId(unsafe { DeviceId::dummy() }); + let device_id = RootDeviceId(DeviceId::dummy()); runner.send_events( iter::once(Event::WindowEvent { @@ -186,7 +186,7 @@ impl ActiveEventLoop { } }); - let device_id = RootDeviceId(unsafe { DeviceId::dummy() }); + let device_id = RootDeviceId(DeviceId::dummy()); runner.send_events( iter::once(Event::WindowEvent { @@ -225,11 +225,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() { @@ -252,11 +250,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() { @@ -286,7 +282,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, pointer_id, events| { + move |active_modifiers, device_id, events| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -297,7 +293,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(device_id); iter::once(Event::WindowEvent { window_id: RootWindowId(id), @@ -325,8 +321,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)), + id: device_id.id.unwrap_or(0).into(), + device_id: RootDeviceId(device_id), phase: TouchPhase::Moved, force: Some(force), location, @@ -341,7 +337,7 @@ impl ActiveEventLoop { let modifiers = self.modifiers.clone(); move |active_modifiers, - pointer_id, + device_id, position: crate::dpi::PhysicalPosition, buttons, button| { @@ -354,7 +350,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 @@ -398,7 +394,7 @@ impl ActiveEventLoop { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, pointer_id, position, button| { + move |active_modifiers, device_id, position, button| { let modifiers = (modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -407,7 +403,7 @@ impl ActiveEventLoop { } }); - let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id)); + let device_id: RootDeviceId = RootDeviceId(device_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 @@ -445,8 +441,8 @@ impl ActiveEventLoop { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + id: device_id.id.unwrap_or(0).into(), + device_id: RootDeviceId(device_id), phase: TouchPhase::Started, force: Some(force), location, @@ -478,7 +474,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, pointer_id, position, button| { + move |active_modifiers, device_id, position, button| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -488,7 +484,7 @@ impl ActiveEventLoop { } }); - let device_id: RootDeviceId = RootDeviceId(DeviceId(pointer_id)); + let device_id: RootDeviceId = RootDeviceId(device_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 @@ -528,8 +524,8 @@ impl ActiveEventLoop { Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + id: device_id.id.unwrap_or(0).into(), + device_id: RootDeviceId(device_id), phase: TouchPhase::Ended, force: Some(force), location, @@ -542,7 +538,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); @@ -556,7 +552,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, }, @@ -569,8 +565,8 @@ impl ActiveEventLoop { runner.send_event(Event::WindowEvent { window_id: RootWindowId(id), event: WindowEvent::Touch(Touch { - id: device_id as u64, - device_id: RootDeviceId(DeviceId(device_id)), + id: device_id.id.unwrap_or(0).into(), + device_id: RootDeviceId(device_id), phase: TouchPhase::Cancelled, force: Some(force), location, diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index f515bbf5ce..173ab5a380 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::device::DeviceId; use super::super::main_thread::MainThreadMarker; use super::super::WindowId; use super::animation_frame::AnimationFrameHandler; @@ -316,14 +317,14 @@ impl Canvas { pub fn on_cursor_leave(&mut self, handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.pointer_handler.on_cursor_leave(&self.common, handler) } pub fn on_cursor_enter(&mut self, handler: F) where - F: 'static + FnMut(ModifiersState, Option), + F: 'static + FnMut(ModifiersState, Option), { self.pointer_handler.on_cursor_enter(&self.common, handler) } @@ -335,8 +336,8 @@ impl Canvas { touch_handler: T, ) where MOD: 'static + FnMut(ModifiersState), - 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, PhysicalPosition, Force), { self.pointer_handler.on_mouse_release( &self.common, @@ -353,8 +354,8 @@ impl Canvas { touch_handler: T, ) where MOD: 'static + FnMut(ModifiersState), - 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, PhysicalPosition, Force), { self.pointer_handler.on_mouse_press( &self.common, @@ -373,10 +374,16 @@ impl Canvas { button_handler: B, ) where MOD: 'static + FnMut(ModifiersState), - 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, + &mut dyn Iterator, Force)>, + ), + B: 'static + + FnMut(ModifiersState, DeviceId, PhysicalPosition, ButtonsState, MouseButton), { self.pointer_handler.on_cursor_move( &self.common, @@ -390,14 +397,14 @@ impl Canvas { pub fn on_touch_cancel(&mut self, handler: F) where - F: 'static + FnMut(i32, PhysicalPosition, Force), + F: 'static + FnMut(DeviceId, PhysicalPosition, Force), { self.pointer_handler.on_touch_cancel(&self.common, handler) } pub fn on_mouse_wheel(&mut 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); @@ -408,7 +415,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 f5e9365d5c..28d940c44f 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::device::DeviceId; 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,15 @@ 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() == "mouse").then(|| event.pointer_id()); + let device_id = (event.pointer_type() == "mouse").then(|| DeviceId::new(&event)); - 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 +62,9 @@ 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() == "mouse").then(|| event.pointer_id()); + let device_id = (event.pointer_type() == "mouse").then(|| DeviceId::new(&event)); - handler(modifiers, pointer_id); + handler(modifiers, device_id); })); } @@ -75,8 +76,8 @@ impl PointerHandler { mut touch_handler: T, ) where MOD: 'static + FnMut(ModifiersState), - 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, PhysicalPosition, Force), { let window = canvas_common.window.clone(); self.on_pointer_release = @@ -86,13 +87,13 @@ impl PointerHandler { match event.pointer_type().as_str() { "touch" => touch_handler( modifiers, - event.pointer_id(), + DeviceId::new(&event), event::mouse_position(&event).to_physical(super::scale_factor(&window)), Force::Normalized(event.pressure() as f64), ), "mouse" => mouse_handler( modifiers, - event.pointer_id(), + DeviceId::new(&event), event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_button(&event).expect("no mouse button released"), ), @@ -110,8 +111,8 @@ impl PointerHandler { prevent_default: Rc>, ) where MOD: 'static + FnMut(ModifiersState), - 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, PhysicalPosition, Force), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); @@ -130,7 +131,7 @@ impl PointerHandler { "touch" => { touch_handler( modifiers, - event.pointer_id(), + DeviceId::new(&event), event::mouse_position(&event).to_physical(super::scale_factor(&window)), Force::Normalized(event.pressure() as f64), ); @@ -138,7 +139,7 @@ impl PointerHandler { "mouse" => { mouse_handler( modifiers, - event.pointer_id(), + DeviceId::new(&event), event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_button(&event).expect("no mouse button pressed"), ); @@ -164,10 +165,16 @@ impl PointerHandler { prevent_default: Rc>, ) where MOD: 'static + FnMut(ModifiersState), - 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, + &mut dyn Iterator, Force)>, + ), + B: 'static + + FnMut(ModifiersState, DeviceId, PhysicalPosition, ButtonsState, MouseButton), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); @@ -183,7 +190,7 @@ impl PointerHandler { return; } - let id = event.pointer_id(); + let device_id = DeviceId::new(&event); // chorded button event if let Some(button) = event::mouse_button(&event) { @@ -201,7 +208,7 @@ impl PointerHandler { button_handler( modifiers, - id, + device_id, event::mouse_position(&event).to_physical(super::scale_factor(&window)), event::mouse_buttons(&event), button, @@ -215,13 +222,13 @@ impl PointerHandler { match pointer_type.as_str() { "mouse" => mouse_handler( modifiers, - id, + device_id, &mut event::pointer_move_event(event) .map(|event| event::mouse_position(&event).to_physical(scale)), ), "touch" => touch_handler( modifiers, - id, + device_id, &mut event::pointer_move_event(event).map(|event| { ( event::mouse_position(&event).to_physical(scale), @@ -236,14 +243,14 @@ 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, 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" { handler( - event.pointer_id(), + DeviceId::new(&event), event::mouse_position(&event).to_physical(super::scale_factor(&window)), Force::Normalized(event.pressure() as f64), );