From 281374de268c5f4405ebbaccff9878d245199ebc Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 10:51:44 +0200 Subject: [PATCH 1/8] implement storing the timestamp and historical events in the exposed events --- winit-android/src/event_loop.rs | 26 ++- winit-appkit/src/view.rs | 19 ++- winit-core/src/event.rs | 25 ++- winit-core/src/event_loop/mod.rs | 22 +++ winit-orbital/src/event_loop.rs | 1 + winit-uikit/src/view.rs | 153 ++++++++++++------ winit-wayland/src/seat/pointer/mod.rs | 22 ++- .../src/seat/pointer/pointer_gesture.rs | 48 +++--- winit-wayland/src/seat/touch/mod.rs | 18 ++- winit-wayland/src/types/wp_tablet_input_v2.rs | 11 +- winit-web/src/event_loop/window_target.rs | 1 + winit-win32/src/event_loop.rs | 3 + winit-x11/src/event_processor.rs | 31 +++- 13 files changed, 294 insertions(+), 86 deletions(-) diff --git a/winit-android/src/event_loop.rs b/winit-android/src/event_loop.rs index ec8abe4d96..b416277ba8 100644 --- a/winit-android/src/event_loop.rs +++ b/winit-android/src/event_loop.rs @@ -18,7 +18,7 @@ use winit_core::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSize use winit_core::event_loop::pump_events::PumpStatus; use winit_core::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProvider, - EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, + EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider, HistoricalMoveEvent, OwnedDisplayHandle as CoreOwnedDisplayHandle, }; use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; @@ -323,6 +323,7 @@ impl EventLoop { InputEvent::MotionEvent(motion_event) => { let device_id = Some(DeviceId::from_raw(motion_event.device_id() as i64)); let action = motion_event.action(); + let event_time = Some(Duration::from_nanos(motion_event.event_time() as u64)); let pointers: Option< Box>>, @@ -358,6 +359,7 @@ impl EventLoop { } let event = event::WindowEvent::PointerEntered { device_id, + event_time, primary, position, kind: match tool_type { @@ -372,6 +374,7 @@ impl EventLoop { app.window_event(&self.window_target, GLOBAL_WINDOW, event); let event = event::WindowEvent::PointerButton { device_id, + event_time, primary, state: event::ElementState::Pressed, position, @@ -388,9 +391,27 @@ impl EventLoop { app.window_event(&self.window_target, GLOBAL_WINDOW, event); }, MotionAction::Move => { + let mut history = Vec::with_capacity(pointer.history().len()); + for h in pointer.history() { + let event_time = Duration::from_nanos(h.event_time() as u64); + let position = PhysicalPosition { x: h.x() as _, y: h.y() as _ }; + let force = Some(Force::Normalized(h.pressure() as f64)); + let source = match tool_type { + android_activity::input::ToolType::Finger => { + event::PointerSource::Touch { finger_id, force } + }, + // TODO mouse events + android_activity::input::ToolType::Mouse => continue, + _ => event::PointerSource::Unknown, + }; + + history + .push(HistoricalMoveEvent::new(event_time, position, source)); + } let primary = self.primary_pointer == Some(finger_id); let event = event::WindowEvent::PointerMoved { device_id, + event_time, primary, position, source: match tool_type { @@ -401,6 +422,7 @@ impl EventLoop { android_activity::input::ToolType::Mouse => continue, _ => event::PointerSource::Unknown, }, + history, }; app.window_event(&self.window_target, GLOBAL_WINDOW, event); }, @@ -416,6 +438,7 @@ impl EventLoop { if let MotionAction::Up | MotionAction::PointerUp = action { let event = event::WindowEvent::PointerButton { device_id, + event_time, primary, state: event::ElementState::Released, position, @@ -434,6 +457,7 @@ impl EventLoop { let event = event::WindowEvent::PointerLeft { device_id, + event_time, primary, position: Some(position), kind: match tool_type { diff --git a/winit-appkit/src/view.rs b/winit-appkit/src/view.rs index 64dc7080df..a775f103c3 100644 --- a/winit-appkit/src/view.rs +++ b/winit-appkit/src/view.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, VecDeque}; use std::rc::Rc; +use std::time::Duration; use dpi::{LogicalPosition, PhysicalSize}; use objc2::rc::Retained; @@ -667,6 +668,7 @@ define_class!( self.queue_event(WindowEvent::PointerEntered { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), primary: true, position, kind: PointerKind::Mouse, @@ -681,6 +683,7 @@ define_class!( self.queue_event(WindowEvent::PointerLeft { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), primary: true, position: Some(position), kind: PointerKind::Mouse, @@ -723,7 +726,12 @@ define_class!( self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| { app.device_event(event_loop, None, DeviceEvent::MouseWheel { delta }) }); - self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase }); + self.queue_event(WindowEvent::MouseWheel { + device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), + delta, + phase, + }); } #[unsafe(method(magnifyWithEvent:))] @@ -743,6 +751,7 @@ define_class!( self.queue_event(WindowEvent::PinchGesture { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), delta: event.magnification(), phase, }); @@ -754,7 +763,10 @@ define_class!( self.mouse_motion(event); - self.queue_event(WindowEvent::DoubleTapGesture { device_id: None }); + self.queue_event(WindowEvent::DoubleTapGesture { + device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), + }); } #[unsafe(method(rotateWithEvent:))] @@ -774,6 +786,7 @@ define_class!( self.queue_event(WindowEvent::RotationGesture { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), delta: event.rotation(), phase, }); @@ -785,6 +798,7 @@ define_class!( self.queue_event(WindowEvent::TouchpadPressure { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), pressure: event.pressure(), stage: event.stage() as i64, }); @@ -1230,6 +1244,7 @@ impl WinitView { primary: true, position: view_point.to_physical(self.scale_factor()), source: PointerSource::Mouse, + history: Default::default(), }); } diff --git a/winit-core/src/event.rs b/winit-core/src/event.rs index 8b25463254..be035ff006 100644 --- a/winit-core/src/event.rs +++ b/winit-core/src/event.rs @@ -3,6 +3,7 @@ use std::cell::LazyCell; use std::cmp::Ordering; use std::f64; use std::sync::{Arc, Mutex, Weak}; +use std::time::Duration; use dpi::{PhysicalPosition, PhysicalSize}; #[cfg(feature = "serde")] @@ -12,7 +13,7 @@ use smol_str::SmolStr; use crate::Instant; use crate::data_transfer::{DataTransferId, TypedData}; use crate::error::RequestError; -use crate::event_loop::{AsyncRequestSerial, DndAction}; +use crate::event_loop::{AsyncRequestSerial, DndAction, HistoricalMoveEvent}; use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}; #[cfg(doc)] use crate::window::Window; @@ -224,6 +225,7 @@ pub enum WindowEvent { /// Should be emitted regardless of window focus. PointerMoved { device_id: Option, + event_time: Option, /// (x,y) coordinates in pixels relative to the top-left corner of the window. Because the /// range of this data is limited by the display area and it may have been @@ -247,6 +249,8 @@ pub enum WindowEvent { primary: bool, source: PointerSource, + /// Historical move events between this event and the previous event + history: Vec, }, /// The pointer has entered the window. @@ -254,6 +258,7 @@ pub enum WindowEvent { /// Should be emitted regardless of window focus. PointerEntered { device_id: Option, + event_time: Option, /// The position of the pointer when it entered the window. /// @@ -281,6 +286,7 @@ pub enum WindowEvent { /// Should be emitted regardless of window focus. PointerLeft { device_id: Option, + event_time: Option, /// The position of the pointer when it left the window. The position reported can be /// outside the bounds of the window. @@ -305,12 +311,18 @@ pub enum WindowEvent { }, /// A mouse wheel movement or touchpad scroll occurred. - MouseWheel { device_id: Option, delta: MouseScrollDelta, phase: TouchPhase }, + MouseWheel { + device_id: Option, + delta: MouseScrollDelta, + phase: TouchPhase, + event_time: Option, + }, /// An mouse button press has been received. PointerButton { device_id: Option, state: ElementState, + event_time: Option, /// The position of the pointer when the button was pressed. /// @@ -364,7 +376,7 @@ pub enum WindowEvent { /// ## Platform-specific /// /// - Only available on **Wayland**. - HoldGesture { device_id: Option, phase: TouchPhase }, + HoldGesture { device_id: Option, phase: TouchPhase, event_time: Duration }, /// Two-finger pinch gesture, often used for magnification. /// @@ -374,6 +386,7 @@ pub enum WindowEvent { /// - On iOS, not recognized by default. It must be enabled when needed. PinchGesture { device_id: Option, + event_time: Option, /// Positive values indicate magnification (zooming in) and negative /// values indicate shrinking (zooming out). /// @@ -390,6 +403,7 @@ pub enum WindowEvent { /// - On iOS, not recognized by default. It must be enabled when needed. PanGesture { device_id: Option, + event_time: Option, /// Change in pixels of pan gesture from last update. delta: PhysicalPosition, phase: TouchPhase, @@ -413,7 +427,7 @@ pub enum WindowEvent { /// /// - Only available on **macOS 10.8** and later, and **iOS**. /// - On iOS, not recognized by default. It must be enabled when needed. - DoubleTapGesture { device_id: Option }, + DoubleTapGesture { device_id: Option, event_time: Option }, /// Two-finger rotation gesture. /// @@ -426,6 +440,7 @@ pub enum WindowEvent { /// - On iOS, not recognized by default. It must be enabled when needed. RotationGesture { device_id: Option, + event_time: Option, /// change in rotation in degrees delta: f32, phase: TouchPhase, @@ -439,6 +454,7 @@ pub enum WindowEvent { /// - **Android / iOS / Wayland / X11 / Windows / Orbital / Web:** Unsupported. TouchpadPressure { device_id: Option, + event_time: Option, /// Value between 0 and 1 representing how hard the touchpad is being /// pressed. pressure: f32, @@ -1694,6 +1710,7 @@ mod tests { primary: true, position: (0, 0).into(), source: PointerSource::Mouse, + history: Default::default(), }); with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(PointerEntered { diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 4d07b12eea..0cd23508cd 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -9,6 +9,7 @@ use std::sync::Arc; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::Duration; +use dpi::PhysicalPosition; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; use crate::Instant; @@ -16,6 +17,7 @@ use crate::application::ApplicationHandler; use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; use crate::error::{EventLoopError, NotSupportedError, RequestError}; +use crate::event::PointerSource; use crate::icon::Icon; use crate::monitor::MonitorHandle; use crate::window::{Theme, Window, WindowAttributes, WindowId}; @@ -412,6 +414,26 @@ pub enum DndAction { Private, } +/// Historical Move event +#[derive(Debug, Clone, PartialEq)] +#[non_exhaustive] +pub struct HistoricalMoveEvent { + /// Event time + event_time: Duration, + source: PointerSource, + position: PhysicalPosition, +} + +impl HistoricalMoveEvent { + pub fn new( + event_time: Duration, + position: PhysicalPosition, + source: PointerSource, + ) -> Self { + Self { event_time, position, source } + } +} + /// Control the [`ActiveEventLoop`], possibly from a different thread, without referencing it /// directly. #[derive(Clone, Debug)] diff --git a/winit-orbital/src/event_loop.rs b/winit-orbital/src/event_loop.rs index 952a87750c..6523f2731e 100644 --- a/winit-orbital/src/event_loop.rs +++ b/winit-orbital/src/event_loop.rs @@ -445,6 +445,7 @@ impl EventLoop { primary: true, position: event_state.mouse_pos.into(), source: event::PointerSource::Mouse, + history: Vec::new(), }); }, EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => { diff --git a/winit-uikit/src/view.rs b/winit-uikit/src/view.rs index dd1e3d76c6..1748586ce3 100644 --- a/winit-uikit/src/view.rs +++ b/winit-uikit/src/view.rs @@ -1,5 +1,6 @@ #![allow(clippy::unnecessary_cast)] use std::cell::{Cell, RefCell}; +use std::time::Duration; use dpi::PhysicalPosition; use objc2::rc::Retained; @@ -18,6 +19,7 @@ use winit_core::event::{ ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource, TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent, }; +use winit_core::event_loop::HistoricalMoveEvent; use winit_core::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; use super::app_state::{self, EventWrapper}; @@ -134,27 +136,27 @@ define_class!( } #[unsafe(method(touchesBegan:withEvent:))] - fn touches_began(&self, touches: &NSSet, _event: Option<&UIEvent>) { + fn touches_began(&self, touches: &NSSet, event: Option<&UIEvent>) { let _entered = debug_span!("touchesBegan:withEvent:").entered(); - self.handle_touches(touches) + self.handle_touches(touches, event) } #[unsafe(method(touchesMoved:withEvent:))] - fn touches_moved(&self, touches: &NSSet, _event: Option<&UIEvent>) { + fn touches_moved(&self, touches: &NSSet, event: Option<&UIEvent>) { let _entered = debug_span!("touchesMoved:withEvent:").entered(); - self.handle_touches(touches) + self.handle_touches(touches, event) } #[unsafe(method(touchesEnded:withEvent:))] - fn touches_ended(&self, touches: &NSSet, _event: Option<&UIEvent>) { + fn touches_ended(&self, touches: &NSSet, event: Option<&UIEvent>) { let _entered = debug_span!("touchesEnded:withEvent:").entered(); - self.handle_touches(touches) + self.handle_touches(touches, event) } #[unsafe(method(touchesCancelled:withEvent:))] - fn touches_cancelled(&self, touches: &NSSet, _event: Option<&UIEvent>) { + fn touches_cancelled(&self, touches: &NSSet, event: Option<&UIEvent>) { let _entered = debug_span!("touchesCancelled:withEvent:").entered(); - self.handle_touches(touches) + self.handle_touches(touches, event) } #[unsafe(method(pinchGesture:))] @@ -212,6 +214,7 @@ define_class!( fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) { let _entered = debug_span!("rotationGesture:").entered(); let window = self.window().unwrap(); + let event_time = recognizer.event_time(); let (phase, delta) = match recognizer.state() { UIGestureRecognizerState::Began => { @@ -244,6 +247,7 @@ define_class!( window_id: window.id(), event: WindowEvent::RotationGesture { device_id: None, + event_time, delta: -delta.to_degrees() as _, phase, }, @@ -297,6 +301,7 @@ define_class!( window_id: window.id(), event: WindowEvent::PanGesture { device_id: None, + event_time, delta: PhysicalPosition::new(dx as _, dy as _), phase, }, @@ -477,44 +482,54 @@ impl WinitView { } } - fn handle_touches(&self, touches: &NSSet) { + fn determine_force(&self, touch: &Retained) -> Option { + let touch_type = touch.r#type(); + if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) { + let use_force = match touch_type { + UITouchType::Pencil => true, + _ => { + let trait_collection = self.traitCollection(); + trait_collection.forceTouchCapability() == UIForceTouchCapability::Available + }, + }; + + if use_force { + let force = touch.force(); + let max_possible_force = touch.maximumPossibleForce(); + Some(Force::Calibrated { + force: force as _, + max_possible_force: max_possible_force as _, + }) + } else { + None + } + } else { + None + } + } + + fn determine_position(&self, touch: &Retained) -> PhysicalPosition { + let logical_location = touch.locationInView(None); + + let scale_factor = self.contentScaleFactor(); + PhysicalPosition::from_logical::<(f64, f64), f64>( + (logical_location.x as _, logical_location.y as _), + scale_factor as f64, + ) + } + + fn handle_touches(&self, touches: &NSSet, event: Option<&UIEvent>) { let window = self.window().unwrap(); let mut touch_events = Vec::new(); for touch in touches { - let logical_location = touch.locationInView(None); + let event_time = Some(Duration::from_secs_f64(touch.timestamp())); let touch_type = touch.r#type(); - let force = if available!(ios = 9.0, tvos = 9.0, visionos = 1.0) { - let use_force = match touch_type { - UITouchType::Pencil => true, - _ => { - let trait_collection = self.traitCollection(); - trait_collection.forceTouchCapability() == UIForceTouchCapability::Available - }, - }; - - if use_force { - let force = touch.force(); - let max_possible_force = touch.maximumPossibleForce(); - Some(Force::Calibrated { - force: force as _, - max_possible_force: max_possible_force as _, - }) - } else { - None - } - } else { - None - }; - let touch_id = Retained::as_ptr(&touch) as usize; + let force = self.determine_force(&touch); let phase = touch.phase(); - let position = { - let scale_factor = self.contentScaleFactor(); - PhysicalPosition::from_logical::<(f64, f64), f64>( - (logical_location.x as _, logical_location.y as _), - scale_factor as f64, - ) - }; + + let position = self.determine_position(&touch); let window_id = window.id(); + let touch_id = Retained::as_ptr(&touch) as usize; let finger_id = FingerId::from_raw(touch_id); let ivars = self.ivars(); @@ -545,6 +560,7 @@ impl WinitView { window_id, event: WindowEvent::PointerEntered { device_id: None, + event_time, primary, position, kind: if let UITouchType::Pencil = touch_type { @@ -558,6 +574,7 @@ impl WinitView { window_id, event: WindowEvent::PointerButton { device_id: None, + event_time, primary, state: ElementState::Pressed, position, @@ -576,26 +593,58 @@ impl WinitView { }); }, UITouchPhase::Moved => { - let (primary, source) = if let UITouchType::Pencil = touch_type { - let tool_data = self.tablet_tool_data_for_pencil(&touch); - (true, PointerSource::TabletTool { - kind: TabletToolKind::Pencil, - data: tool_data, - }) - } else { - (ivars.primary_finger.get().unwrap() == finger_id, PointerSource::Touch { - finger_id, - force, + fn primary_source( + _self: &WinitView, + touch: &Retained, + force: Option, + ) -> (bool, PointerSource) { + let touch_type = touch.r#type(); + let touch_id = Retained::as_ptr(touch) as usize; + let finger_id = FingerId::from_raw(touch_id); + if let UITouchType::Pencil = touch_type { + let tool_data = _self.tablet_tool_data_for_pencil(touch); + (true, PointerSource::TabletTool { + kind: TabletToolKind::Pencil, + data: tool_data, + }) + } else { + ( + _self.ivars().primary_finger.get().unwrap() == finger_id, + PointerSource::Touch { finger_id, force }, + ) + } + } + + let (primary, source) = primary_source(self, &touch, force); + + let historical_events = event + .and_then(|event| { + event.coalescedTouchesForTouch(&touch).map(|touch_events| { + let mut vec = Vec::with_capacity(touch_events.len()); + for touch in touch_events { + let event_time = Duration::from_secs_f64(touch.timestamp()); + let (_, source) = + primary_source(self, &touch, self.determine_force(&touch)); + vec.push(HistoricalMoveEvent::new( + event_time, + self.determine_position(&touch), + source, + )); + } + vec + }) }) - }; + .unwrap_or_default(); touch_events.push(EventWrapper::Window { window_id, event: WindowEvent::PointerMoved { device_id: None, + event_time, primary, position, source, + history: historical_events, }, }); }, @@ -617,6 +666,7 @@ impl WinitView { window_id, event: WindowEvent::PointerButton { device_id: None, + event_time, primary, state: ElementState::Released, position, @@ -639,6 +689,7 @@ impl WinitView { window_id, event: WindowEvent::PointerLeft { device_id: None, + event_time, primary, position: Some(position), kind: if let UITouchType::Pencil = touch_type { diff --git a/winit-wayland/src/seat/pointer/mod.rs b/winit-wayland/src/seat/pointer/mod.rs index e4e113e382..0b4540d919 100644 --- a/winit-wayland/src/seat/pointer/mod.rs +++ b/winit-wayland/src/seat/pointer/mod.rs @@ -125,6 +125,8 @@ impl PointerHandler for WinitState { WindowEvent::PointerEntered { primary: true, device_id: None, + // wl_pointer's enter event carries no timestamp. + event_time: None, position, kind: PointerKind::Mouse, }, @@ -146,25 +148,29 @@ impl PointerHandler for WinitState { WindowEvent::PointerLeft { primary: true, device_id: None, + // wl_pointer's leave event carries no timestamp. + event_time: None, position: Some(position), kind: PointerKind::Mouse, }, window_id, ); }, - PointerEventKind::Motion { .. } => { + PointerEventKind::Motion { time } => { self.events_sink.push_window_event( WindowEvent::PointerMoved { primary: true, device_id: None, + event_time: Some(Duration::from_millis(time as u64)), position, source: PointerSource::Mouse, + history: Vec::new(), }, window_id, ); }, - ref kind @ PointerEventKind::Press { button, serial, .. } - | ref kind @ PointerEventKind::Release { button, serial, .. } => { + ref kind @ PointerEventKind::Press { button, serial, time } + | ref kind @ PointerEventKind::Release { button, serial, time } => { // Update the last button serial. pointer.winit_data().data().inner.lock().unwrap().latest_button_serial = serial; @@ -184,6 +190,7 @@ impl PointerHandler for WinitState { WindowEvent::PointerButton { primary: true, device_id: None, + event_time: Some(Duration::from_millis(time as u64)), state, position, button, @@ -192,7 +199,7 @@ impl PointerHandler for WinitState { window_id, ); }, - PointerEventKind::Axis { horizontal, vertical, .. } => { + PointerEventKind::Axis { horizontal, vertical, time, .. } => { // Get the current phase. let mut pointer_data = pointer.winit_data().data().inner.lock().unwrap(); @@ -240,7 +247,12 @@ impl PointerHandler for WinitState { }; self.events_sink.push_window_event( - WindowEvent::MouseWheel { device_id: None, delta, phase }, + WindowEvent::MouseWheel { + device_id: None, + event_time: Some(Duration::from_millis(time as u64)), + delta, + phase, + }, window_id, ) }, diff --git a/winit-wayland/src/seat/pointer/pointer_gesture.rs b/winit-wayland/src/seat/pointer/pointer_gesture.rs index 1385a2c102..616e72f833 100644 --- a/winit-wayland/src/seat/pointer/pointer_gesture.rs +++ b/winit-wayland/src/seat/pointer/pointer_gesture.rs @@ -1,5 +1,6 @@ use std::ops::Deref; use std::sync::Mutex; +use std::time::Duration; use dpi::{LogicalPosition, PhysicalPosition}; use sctk::compositor::SurfaceData; @@ -83,8 +84,8 @@ impl Dispatch for Point _qhandle: &QueueHandle, ) { let mut pointer_gesture_data = data.inner.lock().unwrap(); - let (window_id, phase) = match event { - HoldEvent::Begin { surface, fingers, .. } => { + let (window_id, phase, time) = match event { + HoldEvent::Begin { surface, fingers, time, .. } => { if fingers < 2 { return; } @@ -92,9 +93,9 @@ impl Dispatch for Point let window_id = crate::make_wid(&surface); pointer_gesture_data.window_id = Some(window_id); - (window_id, TouchPhase::Started) + (window_id, TouchPhase::Started, time) }, - HoldEvent::End { cancelled, .. } => { + HoldEvent::End { cancelled, time, .. } => { let window_id = match pointer_gesture_data.window_id { Some(window_id) => window_id, _ => return, @@ -105,14 +106,19 @@ impl Dispatch for Point let phase = if cancelled == 0 { TouchPhase::Ended } else { TouchPhase::Cancelled }; - (window_id, phase) + (window_id, phase, time) }, _ => return, }; - state - .events_sink - .push_window_event(WindowEvent::HoldGesture { device_id: None, phase }, window_id); + state.events_sink.push_window_event( + WindowEvent::HoldGesture { + device_id: None, + event_time: Duration::from_millis(time as u64), + phase, + }, + window_id, + ); } } @@ -126,8 +132,8 @@ impl Dispatch for Poin _qhandle: &QueueHandle, ) { let mut pointer_gesture_data = data.inner.lock().unwrap(); - let (window_id, phase, pan_delta, pinch_delta, rotation_delta) = match event { - PinchEvent::Begin { surface, fingers, .. } => { + let (window_id, phase, pan_delta, pinch_delta, rotation_delta, time) = match event { + PinchEvent::Begin { surface, fingers, time, .. } => { // We only support two fingers for now. if fingers != 2 { return; @@ -146,9 +152,9 @@ impl Dispatch for Poin pointer_gesture_data.window_id = Some(window_id); pointer_gesture_data.previous_pinch = 1.; - (window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0.) + (window_id, TouchPhase::Started, PhysicalPosition::new(0., 0.), 0., 0., time) }, - PinchEvent::Update { dx, dy, scale: pinch, rotation, .. } => { + PinchEvent::Update { time, dx, dy, scale: pinch, rotation, .. } => { let window_id = match pointer_gesture_data.window_id { Some(window_id) => window_id, _ => return, @@ -167,9 +173,9 @@ impl Dispatch for Poin // Wayland provides rotation in degrees cw, opposite of winit's degrees ccw. let rotation_delta = -rotation as f32; - (window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta) + (window_id, TouchPhase::Moved, pan_delta, pinch_delta, rotation_delta, time) }, - PinchEvent::End { cancelled, .. } => { + PinchEvent::End { cancelled, time, .. } => { let window_id = match pointer_gesture_data.window_id { Some(window_id) => window_id, _ => return, @@ -179,23 +185,29 @@ impl Dispatch for Poin *pointer_gesture_data = Default::default(); let phase = if cancelled == 0 { TouchPhase::Ended } else { TouchPhase::Cancelled }; - (window_id, phase, PhysicalPosition::new(0., 0.), 0., 0.) + (window_id, phase, PhysicalPosition::new(0., 0.), 0., 0., time) }, _ => unreachable!("Unknown event {event:?}"), }; + let event_time = Some(Duration::from_millis(time as u64)); // The chance of only one of these events being necessary is extremely small, // so it is easier to just send all three state.events_sink.push_window_event( - WindowEvent::PanGesture { device_id: None, delta: pan_delta, phase }, + WindowEvent::PanGesture { device_id: None, event_time, delta: pan_delta, phase }, window_id, ); state.events_sink.push_window_event( - WindowEvent::PinchGesture { device_id: None, delta: pinch_delta, phase }, + WindowEvent::PinchGesture { device_id: None, event_time, delta: pinch_delta, phase }, window_id, ); state.events_sink.push_window_event( - WindowEvent::RotationGesture { device_id: None, delta: rotation_delta, phase }, + WindowEvent::RotationGesture { + device_id: None, + event_time, + delta: rotation_delta, + phase, + }, window_id, ); } diff --git a/winit-wayland/src/seat/touch/mod.rs b/winit-wayland/src/seat/touch/mod.rs index e7b3b04567..9c4787ff56 100644 --- a/winit-wayland/src/seat/touch/mod.rs +++ b/winit-wayland/src/seat/touch/mod.rs @@ -1,5 +1,7 @@ //! Touch handling. +use std::time::Duration; + use dpi::LogicalPosition; use sctk::reexports::client::protocol::wl_seat::WlSeat; use sctk::reexports::client::protocol::wl_surface::WlSurface; @@ -20,7 +22,7 @@ impl TouchHandler for WinitState { _: &QueueHandle, touch: &WlTouch, _: u32, - _: u32, + time: u32, surface: WlSurface, id: i32, position: (f64, f64), @@ -50,10 +52,12 @@ impl TouchHandler for WinitState { let position = location.to_physical(scale_factor); let finger_id = FingerId::from_raw(id as usize); + let event_time = Some(Duration::from_millis(time as u64)); self.events_sink.push_window_event( WindowEvent::PointerEntered { device_id: None, + event_time, primary, position, kind: PointerKind::Touch(finger_id), @@ -63,6 +67,7 @@ impl TouchHandler for WinitState { self.events_sink.push_window_event( WindowEvent::PointerButton { device_id: None, + event_time, primary, state: ElementState::Pressed, position, @@ -79,7 +84,7 @@ impl TouchHandler for WinitState { _: &QueueHandle, touch: &WlTouch, _: u32, - _: u32, + time: u32, id: i32, ) { let seat_state = match self.seats.get_mut(&touch.seat().id()) { @@ -113,10 +118,12 @@ impl TouchHandler for WinitState { let position = touch_point.location.to_physical(scale_factor); let finger_id = FingerId::from_raw(id as usize); + let event_time = Some(Duration::from_millis(time as u64)); self.events_sink.push_window_event( WindowEvent::PointerButton { device_id: None, + event_time, primary, state: ElementState::Released, position, @@ -128,6 +135,7 @@ impl TouchHandler for WinitState { self.events_sink.push_window_event( WindowEvent::PointerLeft { device_id: None, + event_time, primary, position: Some(position), kind: PointerKind::Touch(finger_id), @@ -141,7 +149,7 @@ impl TouchHandler for WinitState { _: &Connection, _: &QueueHandle, touch: &WlTouch, - _: u32, + time: u32, id: i32, position: (f64, f64), ) { @@ -172,12 +180,14 @@ impl TouchHandler for WinitState { self.events_sink.push_window_event( WindowEvent::PointerMoved { device_id: None, + event_time: Some(Duration::from_millis(time as u64)), primary, position: touch_point.location.to_physical(scale_factor), source: PointerSource::Touch { finger_id: FingerId::from_raw(id as usize), force: None, }, + history: Vec::new(), }, window_id, ); @@ -205,6 +215,8 @@ impl TouchHandler for WinitState { self.events_sink.push_window_event( WindowEvent::PointerLeft { device_id: None, + // wl_touch's cancel event carries no timestamp. + event_time: None, primary, position: Some(position), kind: PointerKind::Touch(FingerId::from_raw(id as usize)), diff --git a/winit-wayland/src/types/wp_tablet_input_v2.rs b/winit-wayland/src/types/wp_tablet_input_v2.rs index 1b02ddbd38..f2d1d906dd 100644 --- a/winit-wayland/src/types/wp_tablet_input_v2.rs +++ b/winit-wayland/src/types/wp_tablet_input_v2.rs @@ -1,6 +1,7 @@ //! Handling of wp_tablet_input_v2. use std::sync::Mutex; +use std::time::Duration; use dpi::LogicalPosition; use sctk::compositor::SurfaceData; @@ -171,7 +172,10 @@ impl Dispatch2 for TabletToolData { let event = TabletEvent::Button { button, state, serial: Some(serial) }; data.pending.push(event); }, - ToolEvent::Frame { .. } => { + ToolEvent::Frame { time } => { + // TODO: instead of creating single events we should use also the history vector on + // move + let event_time = Some(Duration::from_millis(time as u64)); let kind = data.ty; for event in std::mem::take(&mut data.pending) { if let TabletEvent::Enter { surface, serial } = &event { @@ -206,18 +210,21 @@ impl Dispatch2 for TabletToolData { let window_event = match event { TabletEvent::Enter { .. } => WindowEvent::PointerEntered { device_id: None, + event_time, position, primary: true, kind: PointerKind::TabletTool(kind), }, TabletEvent::Moved => WindowEvent::PointerMoved { device_id: None, + event_time, position, primary: true, source: PointerSource::TabletTool { kind, data: data.tool_state.clone(), }, + history: Vec::new(), }, TabletEvent::Button { button, state, serial } => { // Update serial if we have it. @@ -227,6 +234,7 @@ impl Dispatch2 for TabletToolData { WindowEvent::PointerButton { device_id: None, + event_time, state, position, primary: true, @@ -240,6 +248,7 @@ impl Dispatch2 for TabletToolData { }, TabletEvent::Left => WindowEvent::PointerLeft { device_id: None, + event_time, position: Some(position), primary: true, kind: PointerKind::TabletTool(kind), diff --git a/winit-web/src/event_loop/window_target.rs b/winit-web/src/event_loop/window_target.rs index 8db54caf10..fe59db884b 100644 --- a/winit-web/src/event_loop/window_target.rs +++ b/winit-web/src/event_loop/window_target.rs @@ -267,6 +267,7 @@ impl ActiveEventLoop { primary, position, source, + history: Vec::new(), }, })) }, diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index ca29be1bd3..b526fa2bc9 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -1804,6 +1804,7 @@ unsafe fn public_window_callback_inner( primary: true, position, source: PointerSource::Mouse, + history: Vec::new(), }); } @@ -2109,6 +2110,7 @@ unsafe fn public_window_callback_inner( primary, position, source: PointerSource::Touch { finger_id, force: None }, + history: Vec::new(), }); } else { continue; @@ -2293,6 +2295,7 @@ unsafe fn public_window_callback_inner( primary, position, source, + history: Vec::new(), }); } else { continue; diff --git a/winit-x11/src/event_processor.rs b/winit-x11/src/event_processor.rs index 494bb615d8..8d718f16ee 100644 --- a/winit-x11/src/event_processor.rs +++ b/winit-x11/src/event_processor.rs @@ -4,6 +4,7 @@ use std::mem::MaybeUninit; use std::os::raw::{c_char, c_int, c_long, c_ulong}; use std::slice; use std::sync::{Arc, Mutex}; +use std::time::Duration; use dpi::{PhysicalPosition, PhysicalSize}; use tracing::warn; @@ -1100,10 +1101,12 @@ impl EventProcessor { } let position = PhysicalPosition::new(event.event_x, event.event_y); + let event_time = Some(Duration::from_millis(event.time as u64)); let event = match event.detail as u32 { xlib::Button1 => WindowEvent::PointerButton { device_id, + event_time, primary: true, state, position, @@ -1112,6 +1115,7 @@ impl EventProcessor { }, xlib::Button2 => WindowEvent::PointerButton { device_id, + event_time, primary: true, state, position, @@ -1120,6 +1124,7 @@ impl EventProcessor { }, xlib::Button3 => WindowEvent::PointerButton { device_id, + event_time, primary: true, state, position, @@ -1134,6 +1139,7 @@ impl EventProcessor { 4..=7 => match state { ElementState::Pressed => WindowEvent::MouseWheel { device_id, + event_time, delta: match event.detail { 4 => MouseScrollDelta::LineDelta(0.0, 1.0), 5 => MouseScrollDelta::LineDelta(0.0, -1.0), @@ -1148,6 +1154,7 @@ impl EventProcessor { x @ 8..37 => WindowEvent::PointerButton { device_id, + event_time, primary: true, state, position, @@ -1158,6 +1165,7 @@ impl EventProcessor { }, x @ 37..=0xff => WindowEvent::PointerButton { device_id, + event_time, primary: true, state, position, @@ -1188,6 +1196,7 @@ impl EventProcessor { let window = event.event as xproto::Window; let window_id = mkwid(window); let new_cursor_pos = (event.event_x, event.event_y); + let event_time = Some(Duration::from_millis(event.time as u64)); let cursor_moved = self.with_window(window, |window| { let mut shared_state_lock = window.shared_state_lock(); @@ -1199,9 +1208,11 @@ impl EventProcessor { let event = WindowEvent::PointerMoved { device_id, + event_time, primary: true, position, source: PointerSource::Mouse, + history: Vec::new(), }; app.window_event(&self.target, window_id, event); } else if cursor_moved.is_none() { @@ -1240,7 +1251,12 @@ impl EventProcessor { ScrollOrientation::Vertical => MouseScrollDelta::LineDelta(0.0, -delta as f32), }; - let event = WindowEvent::MouseWheel { device_id, delta, phase: TouchPhase::Moved }; + let event = WindowEvent::MouseWheel { + device_id, + delta, + phase: TouchPhase::Moved, + event_time, + }; events.push(event); } @@ -1283,6 +1299,7 @@ impl EventProcessor { let event = WindowEvent::PointerEntered { device_id, + event_time: Some(Duration::from_millis(event.time as u64)), primary: true, position, kind: PointerKind::Mouse, @@ -1303,6 +1320,7 @@ impl EventProcessor { let window_id = mkwid(window); let event = WindowEvent::PointerLeft { device_id: Some(mkdid(event.deviceid as xinput::DeviceId)), + event_time: Some(Duration::from_millis(event.time as u64)), primary: true, position: Some(PhysicalPosition::new(event.event_x, event.event_y)), kind: PointerKind::Mouse, @@ -1359,9 +1377,11 @@ impl EventProcessor { let event = WindowEvent::PointerMoved { device_id, + event_time: Some(Duration::from_millis(xev.time as u64)), primary: true, position, source: PointerSource::Mouse, + history: Vec::new(), }; app.window_event(&self.target, window_id, event); } @@ -1422,6 +1442,7 @@ impl EventProcessor { let window_id = mkwid(window); let id = xev.detail as u32; let position = PhysicalPosition::new(xev.event_x, xev.event_y); + let event_time = Some(Duration::from_millis(xev.time as u64)); // Mouse cursor position changes when touch events are received. // Only the first concurrently active touch ID moves the mouse cursor. @@ -1430,9 +1451,11 @@ impl EventProcessor { if is_first_touch { let event = WindowEvent::PointerMoved { device_id: None, + event_time, primary: true, position: position.cast(), source: PointerSource::Mouse, + history: Vec::new(), }; app.window_event(&self.target, window_id, event); } @@ -1444,6 +1467,7 @@ impl EventProcessor { xinput2::XI_TouchBegin => { let event = WindowEvent::PointerEntered { device_id, + event_time, primary: is_first_touch, position, kind: PointerKind::Touch(finger_id), @@ -1451,6 +1475,7 @@ impl EventProcessor { app.window_event(&self.target, window_id, event); let event = WindowEvent::PointerButton { device_id, + event_time, primary: is_first_touch, state: ElementState::Pressed, position, @@ -1462,15 +1487,18 @@ impl EventProcessor { xinput2::XI_TouchUpdate => { let event = WindowEvent::PointerMoved { device_id, + event_time, primary: is_first_touch, position, source: PointerSource::Touch { finger_id, force: None }, + history: Vec::new(), }; app.window_event(&self.target, window_id, event); }, xinput2::XI_TouchEnd => { let event = WindowEvent::PointerButton { device_id, + event_time, primary: is_first_touch, state: ElementState::Released, position, @@ -1480,6 +1508,7 @@ impl EventProcessor { app.window_event(&self.target, window_id, event); let event = WindowEvent::PointerLeft { device_id, + event_time, primary: is_first_touch, position: Some(position), kind: PointerKind::Touch(finger_id), From 9fe263bd521bbd39f88dd2f0fdd45417d8285bbc Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 11:11:03 +0200 Subject: [PATCH 2/8] format --- winit-core/src/event_loop/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 81efbbc098..5a3fa2ae21 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -16,7 +16,10 @@ use crate::Instant; use crate::application::ApplicationHandler; use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; -use crate::error::{CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, RequestError, TransferError}; +use crate::error::{ + CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, RequestError, + TransferError, +}; use crate::event::PointerSource; use crate::icon::Icon; use crate::monitor::MonitorHandle; From fcba0944adc195934ed77084be1e15a3df5a45cd Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 09:41:44 +0000 Subject: [PATCH 3/8] fix clippy --- winit-core/src/event_loop/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index 5a3fa2ae21..ba9b1e5a7f 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -17,8 +17,7 @@ use crate::application::ApplicationHandler; use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; use crate::error::{ - CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, RequestError, - TransferError, + CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, TransferError }; use crate::event::PointerSource; use crate::icon::Icon; From 5ab156441b6f7bd968bc842e724f5f1bb2f1a21d Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 11:56:17 +0200 Subject: [PATCH 4/8] fix clippy --- winit-core/src/event_loop/mod.rs | 2 +- winit-x11/src/event_processor.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/winit-core/src/event_loop/mod.rs b/winit-core/src/event_loop/mod.rs index ba9b1e5a7f..6d38196e97 100644 --- a/winit-core/src/event_loop/mod.rs +++ b/winit-core/src/event_loop/mod.rs @@ -17,7 +17,7 @@ use crate::application::ApplicationHandler; use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::data_transfer::{DataTransfer, DataTransferId, DataTransferSend, TransferType}; use crate::error::{ - CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, TransferError + CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, TransferError, }; use crate::event::PointerSource; use crate::icon::Icon; diff --git a/winit-x11/src/event_processor.rs b/winit-x11/src/event_processor.rs index 8d718f16ee..66f92e1f6f 100644 --- a/winit-x11/src/event_processor.rs +++ b/winit-x11/src/event_processor.rs @@ -1101,7 +1101,7 @@ impl EventProcessor { } let position = PhysicalPosition::new(event.event_x, event.event_y); - let event_time = Some(Duration::from_millis(event.time as u64)); + let event_time = Some(Duration::from_millis(event.time)); let event = match event.detail as u32 { xlib::Button1 => WindowEvent::PointerButton { @@ -1196,7 +1196,7 @@ impl EventProcessor { let window = event.event as xproto::Window; let window_id = mkwid(window); let new_cursor_pos = (event.event_x, event.event_y); - let event_time = Some(Duration::from_millis(event.time as u64)); + let event_time = Some(Duration::from_millis(event.time)); let cursor_moved = self.with_window(window, |window| { let mut shared_state_lock = window.shared_state_lock(); @@ -1299,7 +1299,7 @@ impl EventProcessor { let event = WindowEvent::PointerEntered { device_id, - event_time: Some(Duration::from_millis(event.time as u64)), + event_time: Some(Duration::from_millis(event.time)), primary: true, position, kind: PointerKind::Mouse, @@ -1320,7 +1320,7 @@ impl EventProcessor { let window_id = mkwid(window); let event = WindowEvent::PointerLeft { device_id: Some(mkdid(event.deviceid as xinput::DeviceId)), - event_time: Some(Duration::from_millis(event.time as u64)), + event_time: Some(Duration::from_millis(event.time)), primary: true, position: Some(PhysicalPosition::new(event.event_x, event.event_y)), kind: PointerKind::Mouse, @@ -1377,7 +1377,7 @@ impl EventProcessor { let event = WindowEvent::PointerMoved { device_id, - event_time: Some(Duration::from_millis(xev.time as u64)), + event_time: Some(Duration::from_millis(xev.time)), primary: true, position, source: PointerSource::Mouse, @@ -1442,7 +1442,7 @@ impl EventProcessor { let window_id = mkwid(window); let id = xev.detail as u32; let position = PhysicalPosition::new(xev.event_x, xev.event_y); - let event_time = Some(Duration::from_millis(xev.time as u64)); + let event_time = Some(Duration::from_millis(xev.time)); // Mouse cursor position changes when touch events are received. // Only the first concurrently active touch ID moves the mouse cursor. From f951d8acf1196457eddfab46f8cfc93af64baf55 Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 12:45:25 +0200 Subject: [PATCH 5/8] fix format --- winit-core/src/event.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/winit-core/src/event.rs b/winit-core/src/event.rs index b5dc1f6dcb..a378db99e3 100644 --- a/winit-core/src/event.rs +++ b/winit-core/src/event.rs @@ -1707,6 +1707,7 @@ mod tests { with_window_event(Ime(Enabled)); with_window_event(PointerMoved { device_id: None, + event_time: None, primary: true, position: (0, 0).into(), source: PointerSource::Mouse, @@ -1715,6 +1716,7 @@ mod tests { with_window_event(ModifiersChanged(event::Modifiers::default())); with_window_event(PointerEntered { device_id: None, + event_time: None, primary: true, position: (0, 0).into(), kind: PointerKind::Mouse, @@ -1722,16 +1724,19 @@ mod tests { with_window_event(PointerLeft { primary: true, device_id: None, + event_time: None, position: Some((0, 0).into()), kind: PointerKind::Mouse, }); with_window_event(MouseWheel { device_id: None, + event_time: None, delta: event::MouseScrollDelta::LineDelta(0.0, 0.0), phase: event::TouchPhase::Started, }); with_window_event(PointerButton { device_id: None, + event_time: None, primary: true, state: event::ElementState::Pressed, position: (0, 0).into(), @@ -1740,6 +1745,7 @@ mod tests { }); with_window_event(PointerButton { device_id: None, + event_time: None, primary: true, state: event::ElementState::Released, position: (0, 0).into(), @@ -1751,21 +1757,24 @@ mod tests { }); with_window_event(PinchGesture { device_id: None, + event_time: None, delta: 0.0, phase: event::TouchPhase::Started, }); - with_window_event(DoubleTapGesture { device_id: None }); + with_window_event(DoubleTapGesture { device_id: None, event_time: None, }); with_window_event(RotationGesture { device_id: None, + event_time: None, delta: 0.0, phase: event::TouchPhase::Started, }); with_window_event(PanGesture { device_id: None, + event_time: None, delta: PhysicalPosition::::new(0.0, 0.0), phase: event::TouchPhase::Started, }); - with_window_event(TouchpadPressure { device_id: None, pressure: 0.0, stage: 0 }); + with_window_event(TouchpadPressure { event_time: None, device_id: None, pressure: 0.0, stage: 0 }); with_window_event(ThemeChanged(crate::window::Theme::Light)); with_window_event(Occluded(true)); }}; From 2b39ac0b60664537dd5960642c2b9613c015b275 Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Fri, 25 Sep 2026 15:25:11 +0200 Subject: [PATCH 6/8] Update view.rs --- winit-uikit/src/view.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/winit-uikit/src/view.rs b/winit-uikit/src/view.rs index 1748586ce3..b03f3b5119 100644 --- a/winit-uikit/src/view.rs +++ b/winit-uikit/src/view.rs @@ -163,6 +163,7 @@ define_class!( fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) { let _entered = debug_span!("pinchGesture:").entered(); let window = self.window().unwrap(); + let event_time = recognizer.event_time(); let (phase, delta) = match recognizer.state() { UIGestureRecognizerState::Began => { @@ -187,7 +188,12 @@ define_class!( let gesture_event = EventWrapper::Window { window_id: window.id(), - event: WindowEvent::PinchGesture { device_id: None, delta: delta as f64, phase }, + event: WindowEvent::PinchGesture { + device_id: None, + delta: delta as f64, + phase, + event_time, + }, }; let mtm = MainThreadMarker::new().unwrap(); @@ -261,6 +267,7 @@ define_class!( fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) { let _entered = debug_span!("panGesture:").entered(); let window = self.window().unwrap(); + let event_time = recognizer.event_time(); let translation = recognizer.translationInView(Some(self)); From 76e8538d308a1e561f195d19c4e84533f31bff39 Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Mon, 28 Sep 2026 09:38:16 +0200 Subject: [PATCH 7/8] fix clippy and missing event_times --- winit-appkit/src/view.rs | 2 + winit-orbital/src/event_loop.rs | 5 ++ winit-uikit/Cargo.toml | 2 + winit-uikit/src/view.rs | 15 ++++-- winit-web/src/event_loop/window_target.rs | 28 ++++++++---- winit-web/src/web_sys/canvas.rs | 50 +++++++++++++++++--- winit-web/src/web_sys/event.rs | 5 ++ winit-web/src/web_sys/pointer.rs | 56 ++++++++++++++++++++--- winit-win32/src/event_loop.rs | 41 +++++++++++++---- winit-x11/src/event_processor.rs | 46 ++++++++++++++++--- 10 files changed, 208 insertions(+), 42 deletions(-) diff --git a/winit-appkit/src/view.rs b/winit-appkit/src/view.rs index a775f103c3..052e58f17b 100644 --- a/winit-appkit/src/view.rs +++ b/winit-appkit/src/view.rs @@ -1213,6 +1213,7 @@ impl WinitView { self.queue_event(WindowEvent::PointerButton { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), primary: true, state: button_state, position, @@ -1241,6 +1242,7 @@ impl WinitView { self.queue_event(WindowEvent::PointerMoved { device_id: None, + event_time: Some(Duration::from_secs_f64(event.timestamp())), primary: true, position: view_point.to_physical(self.scale_factor()), source: PointerSource::Mouse, diff --git a/winit-orbital/src/event_loop.rs b/winit-orbital/src/event_loop.rs index 6d3c53dba6..b292dc7d49 100644 --- a/winit-orbital/src/event_loop.rs +++ b/winit-orbital/src/event_loop.rs @@ -442,6 +442,7 @@ impl EventLoop { event_state.mouse_pos = (x, y); app.window_event(window_target, window_id, event::WindowEvent::PointerMoved { device_id: None, + event_time: None, primary: true, position: event_state.mouse_pos.into(), source: event::PointerSource::Mouse, @@ -457,6 +458,7 @@ impl EventLoop { while let Some((button, state)) = event_state.mouse(left, middle, right) { app.window_event(window_target, window_id, event::WindowEvent::PointerButton { device_id: None, + event_time: None, primary: true, state, position: event_state.mouse_pos.into(), @@ -468,6 +470,7 @@ impl EventLoop { EventOption::Scroll(ScrollEvent { x, y }) => { app.window_event(window_target, window_id, event::WindowEvent::MouseWheel { device_id: None, + event_time: None, delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32), phase: event::TouchPhase::Moved, }); @@ -500,6 +503,7 @@ impl EventLoop { let event = if entered { event::WindowEvent::PointerEntered { device_id: None, + event_time: None, primary: true, position: dpi::PhysicalPosition::default(), kind: event::PointerKind::Mouse, @@ -507,6 +511,7 @@ impl EventLoop { } else { event::WindowEvent::PointerLeft { device_id: None, + event_time: None, primary: true, position: None, kind: event::PointerKind::Mouse, diff --git a/winit-uikit/Cargo.toml b/winit-uikit/Cargo.toml index 9fdc74bbc7..e83ccf6ce8 100644 --- a/winit-uikit/Cargo.toml +++ b/winit-uikit/Cargo.toml @@ -37,10 +37,12 @@ objc2-foundation = { workspace = true, features = [ "block2", "objc2-core-foundation", "NSArray", + "NSDate", "NSEnumerator", "NSGeometry", "NSObjCRuntime", "NSOperation", + "NSProcessInfo", "NSString", "NSThread", "NSSet", diff --git a/winit-uikit/src/view.rs b/winit-uikit/src/view.rs index b03f3b5119..f29670c53c 100644 --- a/winit-uikit/src/view.rs +++ b/winit-uikit/src/view.rs @@ -7,7 +7,7 @@ use objc2::rc::Retained; use objc2::runtime::{NSObjectProtocol, ProtocolObject}; use objc2::{DefinedClass, MainThreadMarker, available, define_class, msg_send, sel}; use objc2_core_foundation::{CGFloat, CGPoint, CGRect}; -use objc2_foundation::{NSObject, NSSet, NSString}; +use objc2_foundation::{NSObject, NSProcessInfo, NSSet, NSString}; use objc2_ui_kit::{ UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate, UIGestureRecognizerState, UIKeyInput, UIPanGestureRecognizer, UIPinchGestureRecognizer, @@ -163,7 +163,8 @@ define_class!( fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) { let _entered = debug_span!("pinchGesture:").entered(); let window = self.window().unwrap(); - let event_time = recognizer.event_time(); + let event_time = + Some(Duration::from_secs_f64(NSProcessInfo::processInfo().systemUptime())); let (phase, delta) = match recognizer.state() { UIGestureRecognizerState::Began => { @@ -206,9 +207,11 @@ define_class!( let window = self.window().unwrap(); if recognizer.state() == UIGestureRecognizerState::Ended { + let event_time = + Some(Duration::from_secs_f64(NSProcessInfo::processInfo().systemUptime())); let gesture_event = EventWrapper::Window { window_id: window.id(), - event: WindowEvent::DoubleTapGesture { device_id: None }, + event: WindowEvent::DoubleTapGesture { device_id: None, event_time }, }; let mtm = MainThreadMarker::new().unwrap(); @@ -220,7 +223,8 @@ define_class!( fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) { let _entered = debug_span!("rotationGesture:").entered(); let window = self.window().unwrap(); - let event_time = recognizer.event_time(); + let event_time = + Some(Duration::from_secs_f64(NSProcessInfo::processInfo().systemUptime())); let (phase, delta) = match recognizer.state() { UIGestureRecognizerState::Began => { @@ -267,7 +271,8 @@ define_class!( fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) { let _entered = debug_span!("panGesture:").entered(); let window = self.window().unwrap(); - let event_time = recognizer.event_time(); + let event_time = + Some(Duration::from_secs_f64(NSProcessInfo::processInfo().systemUptime())); let translation = recognizer.translationInView(Some(self)); diff --git a/winit-web/src/event_loop/window_target.rs b/winit-web/src/event_loop/window_target.rs index 923bac120e..75797b114d 100644 --- a/winit-web/src/event_loop/window_target.rs +++ b/winit-web/src/event_loop/window_target.rs @@ -197,7 +197,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, primary, position, kind| { + move |active_modifiers, device_id, primary, position, kind, event_time| { let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -210,6 +210,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::PointerLeft { device_id, + event_time, primary, position: Some(position), kind, @@ -223,7 +224,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, primary, position, kind| { + move |active_modifiers, device_id, primary, position, kind, event_time| { let focus = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -234,7 +235,13 @@ impl ActiveEventLoop { runner.send_events(focus.into_iter().chain(iter::once(Event::WindowEvent { window_id, - event: WindowEvent::PointerEntered { device_id, primary, position, kind }, + event: WindowEvent::PointerEntered { + device_id, + event_time, + primary, + position, + kind, + }, }))) } }); @@ -247,7 +254,7 @@ impl ActiveEventLoop { move |device_id, events| { runner.send_events(events.flat_map( - |(active_modifiers, primary, position, source)| { + |(active_modifiers, primary, position, source, event_time)| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers) .then(|| { @@ -264,6 +271,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::PointerMoved { device_id, + event_time, primary, position, source, @@ -279,7 +287,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, primary, position, state, button| { + move |active_modifiers, device_id, primary, position, state, button, event_time| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -293,6 +301,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::PointerButton { device_id, + event_time, primary, state, position, @@ -308,7 +317,7 @@ impl ActiveEventLoop { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, primary, position, button| { + move |active_modifiers, device_id, primary, position, button, event_time| { let modifiers = (modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); Event::WindowEvent { @@ -321,6 +330,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::PointerButton { device_id, + event_time, primary, state: ElementState::Pressed, position, @@ -336,7 +346,7 @@ impl ActiveEventLoop { let has_focus = has_focus.clone(); let modifiers = self.modifiers.clone(); - move |active_modifiers, device_id, primary, position, button| { + move |active_modifiers, device_id, primary, position, button, event_time| { let modifiers = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -350,6 +360,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::PointerButton { device_id, + event_time, primary, state: ElementState::Released, position, @@ -362,7 +373,7 @@ impl ActiveEventLoop { let runner = self.runner.clone(); let modifiers = self.modifiers.clone(); - canvas.on_mouse_wheel(move |delta, active_modifiers| { + canvas.on_mouse_wheel(move |delta, active_modifiers, event_time| { let modifiers_changed = (has_focus.get() && modifiers.get() != active_modifiers).then(|| { modifiers.set(active_modifiers); @@ -377,6 +388,7 @@ impl ActiveEventLoop { window_id, event: WindowEvent::MouseWheel { device_id: None, + event_time, delta, phase: TouchPhase::Moved, }, diff --git a/winit-web/src/web_sys/canvas.rs b/winit-web/src/web_sys/canvas.rs index ed7f2d720b..b6833b298b 100644 --- a/winit-web/src/web_sys/canvas.rs +++ b/winit-web/src/web_sys/canvas.rs @@ -2,6 +2,7 @@ use std::cell::{Cell, RefCell}; use std::ops::Deref; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use std::time::Duration; use dpi::{LogicalPosition, PhysicalPosition, PhysicalSize}; use smol_str::SmolStr; @@ -334,7 +335,14 @@ impl Canvas { pub fn on_pointer_leave(&self, handler: F) where F: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + PointerKind, + Option, + ), { self.handlers.borrow_mut().pointer_handler.on_pointer_leave(&self.common, handler) } @@ -342,7 +350,14 @@ impl Canvas { pub fn on_pointer_enter(&self, handler: F) where F: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + PointerKind, + Option, + ), { self.handlers.borrow_mut().pointer_handler.on_pointer_enter(&self.common, handler) } @@ -350,7 +365,14 @@ impl Canvas { pub fn on_pointer_release(&self, handler: C) where C: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + ButtonSource, + Option, + ), { self.handlers.borrow_mut().pointer_handler.on_pointer_release(&self.common, handler) } @@ -358,7 +380,14 @@ impl Canvas { pub fn on_pointer_press(&self, handler: C) where C: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + ButtonSource, + Option, + ), { self.handlers.borrow_mut().pointer_handler.on_pointer_press( &self.common, @@ -373,7 +402,13 @@ impl Canvas { + FnMut( Option, &mut dyn Iterator< - Item = (ModifiersState, bool, PhysicalPosition, PointerSource), + Item = ( + ModifiersState, + bool, + PhysicalPosition, + PointerSource, + Option, + ), >, ), B: 'static @@ -384,6 +419,7 @@ impl Canvas { PhysicalPosition, ElementState, ButtonSource, + Option, ), { self.handlers.borrow_mut().pointer_handler.on_pointer_move( @@ -396,7 +432,7 @@ impl Canvas { pub fn on_mouse_wheel(&self, mut handler: F) where - F: 'static + FnMut(MouseScrollDelta, ModifiersState), + F: 'static + FnMut(MouseScrollDelta, ModifiersState, Option), { let window = self.common.window.clone(); let prevent_default = Rc::clone(&self.prevent_default); @@ -408,7 +444,7 @@ impl Canvas { if let Some(delta) = event::mouse_scroll_delta(&window, &event) { let modifiers = event::mouse_modifiers(&event); - handler(delta, modifiers); + handler(delta, modifiers, event::event_time(&event)); } })); } diff --git a/winit-web/src/web_sys/event.rs b/winit-web/src/web_sys/event.rs index fd7e58118d..7b09a51b0a 100644 --- a/winit-web/src/web_sys/event.rs +++ b/winit-web/src/web_sys/event.rs @@ -1,5 +1,6 @@ use std::cell::OnceCell; use std::f64; +use std::time::Duration; use dpi::{LogicalPosition, PhysicalPosition, Position}; use smol_str::SmolStr; @@ -44,6 +45,10 @@ pub fn pointer_buttons(event: &MouseEvent) -> ButtonsState { ButtonsState::from_bits_retain(event.buttons()) } +pub fn event_time(event: &Event) -> Option { + Some(Duration::from_secs_f64(event.time_stamp() / 1000.0)) +} + pub fn raw_button(event: &MouseEvent) -> Option { // https://www.w3.org/TR/pointerevents3/#the-button-property #[allow(clippy::disallowed_methods)] diff --git a/winit-web/src/web_sys/pointer.rs b/winit-web/src/web_sys/pointer.rs index 7d90bb8adf..6ee2f9edb0 100644 --- a/winit-web/src/web_sys/pointer.rs +++ b/winit-web/src/web_sys/pointer.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::rc::Rc; +use std::time::Duration; use dpi::PhysicalPosition; use web_sys::PointerEvent; @@ -36,7 +37,14 @@ impl PointerHandler { pub fn on_pointer_leave(&mut self, canvas_common: &Common, mut handler: F) where F: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + PointerKind, + Option, + ), { let window = canvas_common.window.clone(); self.on_cursor_leave = @@ -47,14 +55,22 @@ impl PointerHandler { let position = event::pointer_position(&event).to_physical(super::scale_factor(&window)); let kind = event::pointer_kind(&event, pointer_id); - handler(modifiers, device_id, event.is_primary(), position, kind); + let event_time = event::event_time(&event); + handler(modifiers, device_id, event.is_primary(), position, kind, event_time); })); } pub fn on_pointer_enter(&mut self, canvas_common: &Common, mut handler: F) where F: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, PointerKind), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + PointerKind, + Option, + ), { let window = canvas_common.window.clone(); self.on_cursor_enter = @@ -65,14 +81,22 @@ impl PointerHandler { let position = event::pointer_position(&event).to_physical(super::scale_factor(&window)); let kind = event::pointer_kind(&event, pointer_id); - handler(modifiers, device_id, event.is_primary(), position, kind); + let event_time = event::event_time(&event); + handler(modifiers, device_id, event.is_primary(), position, kind, event_time); })); } pub fn on_pointer_release(&mut self, canvas_common: &Common, mut handler: C) where C: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + ButtonSource, + Option, + ), { let window = canvas_common.window.clone(); self.on_pointer_release = @@ -99,6 +123,7 @@ impl PointerHandler { event.is_primary(), event::pointer_position(&event).to_physical(super::scale_factor(&window)), source, + event::event_time(&event), ) })); } @@ -110,7 +135,14 @@ impl PointerHandler { prevent_default: Rc>, ) where C: 'static - + FnMut(ModifiersState, Option, bool, PhysicalPosition, ButtonSource), + + FnMut( + ModifiersState, + Option, + bool, + PhysicalPosition, + ButtonSource, + Option, + ), { let window = canvas_common.window.clone(); let canvas = canvas_common.raw().clone(); @@ -161,6 +193,7 @@ impl PointerHandler { event.is_primary(), event::pointer_position(&event).to_physical(super::scale_factor(&window)), source, + event::event_time(&event), ) })); } @@ -176,7 +209,13 @@ impl PointerHandler { + FnMut( Option, &mut dyn Iterator< - Item = (ModifiersState, bool, PhysicalPosition, PointerSource), + Item = ( + ModifiersState, + bool, + PhysicalPosition, + PointerSource, + Option, + ), >, ), B: 'static @@ -187,6 +226,7 @@ impl PointerHandler { PhysicalPosition, ElementState, ButtonSource, + Option, ), { let window = canvas_common.window.clone(); @@ -239,6 +279,7 @@ impl PointerHandler { event::pointer_position(&event).to_physical(super::scale_factor(&window)), state, button, + event::event_time(&event), ); return; @@ -255,6 +296,7 @@ impl PointerHandler { event.is_primary(), event::pointer_position(&event).to_physical(scale), event::pointer_source(&event, kind), + event::event_time(&event), ) }), ); diff --git a/winit-win32/src/event_loop.rs b/winit-win32/src/event_loop.rs index 2e13d4a943..2a2b29bb5e 100644 --- a/winit-win32/src/event_loop.rs +++ b/winit-win32/src/event_loop.rs @@ -42,15 +42,15 @@ use windows_sys::Win32::UI::Input::{ use windows_sys::Win32::UI::WindowsAndMessaging::{ CREATESTRUCTW, CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, EnumThreadWindows, GW_OWNER, GWL_STYLE, GWL_USERDATA, GetClientRect, GetCursorPos, GetMenu, - GetWindow, HTCAPTION, HTCLIENT, LoadCursorW, MINMAXINFO, MNC_CLOSE, MSG, MWMO_INPUTAVAILABLE, - MsgWaitForMultipleObjectsEx, NCCALCSIZE_PARAMS, PEN_FLAG_BARREL, PEN_FLAG_ERASER, - PEN_MASK_PRESSURE, PEN_MASK_ROTATION, PEN_MASK_TILT_X, PEN_MASK_TILT_Y, PM_REMOVE, PT_PEN, - PT_TOUCH, PeekMessageW, PostMessageW, QS_ALLINPUT, RI_MOUSE_HWHEEL, RI_MOUSE_WHEEL, - RegisterClassExW, RegisterWindowMessageA, SC_MINIMIZE, SC_RESTORE, SIZE_MAXIMIZED, - SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, - SWP_NOZORDER, SetCursor, SetWindowPos, SystemParametersInfoW, TranslateMessage, WHEEL_DELTA, - WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, WM_ENTERSIZEMOVE, - WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, + GetMessageTime, GetWindow, HTCAPTION, HTCLIENT, LoadCursorW, MINMAXINFO, MNC_CLOSE, MSG, + MWMO_INPUTAVAILABLE, MsgWaitForMultipleObjectsEx, NCCALCSIZE_PARAMS, PEN_FLAG_BARREL, + PEN_FLAG_ERASER, PEN_MASK_PRESSURE, PEN_MASK_ROTATION, PEN_MASK_TILT_X, PEN_MASK_TILT_Y, + PM_REMOVE, PT_PEN, PT_TOUCH, PeekMessageW, PostMessageW, QS_ALLINPUT, RI_MOUSE_HWHEEL, + RI_MOUSE_WHEEL, RegisterClassExW, RegisterWindowMessageA, SC_MINIMIZE, SC_RESTORE, + SIZE_MAXIMIZED, SPI_GETWHEELSCROLLCHARS, SPI_GETWHEELSCROLLLINES, SWP_NOACTIVATE, SWP_NOMOVE, + SWP_NOSIZE, SWP_NOZORDER, SetCursor, SetWindowPos, SystemParametersInfoW, TranslateMessage, + WHEEL_DELTA, WINDOWPOS, WM_CAPTURECHANGED, WM_CLOSE, WM_CREATE, WM_DESTROY, WM_DPICHANGED, + WM_ENTERSIZEMOVE, WM_EXITSIZEMOVE, WM_GETMINMAXINFO, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_SETCONTEXT, WM_IME_STARTCOMPOSITION, WM_INPUT, WM_INPUTLANGCHANGE, WM_KEYDOWN, WM_KEYUP, WM_KILLFOCUS, WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MENUCHAR, WM_MOUSEHWHEEL, WM_MOUSEMOVE, WM_MOUSEWHEEL, WM_NCACTIVATE, WM_NCCALCSIZE, WM_NCCREATE, @@ -1737,6 +1737,7 @@ unsafe fn public_window_callback_inner( let x = util::get_x_lparam(lparam as u32) as i32; let y = util::get_y_lparam(lparam as u32) as i32; let position = PhysicalPosition::new(x as f64, y as f64); + let event_time = Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)); let cursor_moved; { @@ -1753,6 +1754,7 @@ unsafe fn public_window_callback_inner( drop(w); userdata.send_window_event(window, PointerEntered { device_id: None, + event_time, primary: true, position, kind: PointerKind::Mouse, @@ -1776,6 +1778,7 @@ unsafe fn public_window_callback_inner( drop(w); userdata.send_window_event(window, PointerLeft { device_id: None, + event_time, primary: true, position: Some(position), kind: PointerKind::Mouse, @@ -1797,6 +1800,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerMoved { device_id: None, + event_time, primary: true, position, source: PointerSource::Mouse, @@ -1818,6 +1822,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerLeft { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), primary: true, position: None, kind: Mouse, @@ -1855,6 +1860,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, WindowEvent::MouseWheel { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), delta: LineDelta(0.0, value * scroll_lines_multiplier as f32), phase: TouchPhase::Moved, }); @@ -1888,6 +1894,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, WindowEvent::MouseWheel { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), delta: LineDelta(value * scroll_characters_multiplier as f32, 0.0), phase: TouchPhase::Moved, }); @@ -1924,6 +1931,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerButton { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), primary: true, state: Pressed, position, @@ -1954,6 +1962,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerButton { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), primary: true, state: Released, position, @@ -1988,6 +1997,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerButton { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), primary: true, state: Pressed, position, @@ -2018,6 +2028,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, PointerButton { device_id: None, + event_time: Some(Duration::from_millis(unsafe { GetMessageTime() } as u32 as u64)), primary: true, state: Released, position, @@ -2069,16 +2080,19 @@ unsafe fn public_window_callback_inner( let finger_id = FingerId::from_raw(input.dwID as usize); let primary = util::has_flag(input.dwFlags, TOUCHEVENTF_PRIMARY); + let event_time = Some(Duration::from_millis(input.dwTime as u64)); if util::has_flag(input.dwFlags, TOUCHEVENTF_DOWN) { userdata.send_window_event(window, WindowEvent::PointerEntered { device_id: None, + event_time, primary, position, kind: PointerKind::Touch(finger_id), }); userdata.send_window_event(window, WindowEvent::PointerButton { device_id: None, + event_time, primary, state: Pressed, position, @@ -2088,6 +2102,7 @@ unsafe fn public_window_callback_inner( } else if util::has_flag(input.dwFlags, TOUCHEVENTF_UP) { userdata.send_window_event(window, WindowEvent::PointerButton { device_id: None, + event_time, primary, state: Released, position, @@ -2096,6 +2111,7 @@ unsafe fn public_window_callback_inner( }); userdata.send_window_event(window, WindowEvent::PointerLeft { device_id: None, + event_time, primary, position: Some(position), kind: PointerKind::Touch(finger_id), @@ -2103,6 +2119,7 @@ unsafe fn public_window_callback_inner( } else if util::has_flag(input.dwFlags, TOUCHEVENTF_MOVE) { userdata.send_window_event(window, WindowEvent::PointerMoved { device_id: None, + event_time, primary, position, source: PointerSource::Touch { finger_id, force: None }, @@ -2211,6 +2228,7 @@ unsafe fn public_window_callback_inner( let finger_id = FingerId::from_raw(pointer_info.pointerId as usize); let primary = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_PRIMARY); + let event_time = Some(Duration::from_millis(pointer_info.dwTime as u64)); let is_down = util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_DOWN); if is_down || util::has_flag(pointer_info.pointerFlags, POINTER_FLAG_UP) { @@ -2244,6 +2262,7 @@ unsafe fn public_window_callback_inner( if is_down { userdata.send_window_event(window, WindowEvent::PointerEntered { device_id: None, + event_time, primary, position, kind, @@ -2251,6 +2270,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, WindowEvent::PointerButton { device_id: None, + event_time, primary, state: Pressed, position, @@ -2260,6 +2280,7 @@ unsafe fn public_window_callback_inner( } else { userdata.send_window_event(window, WindowEvent::PointerButton { device_id: None, + event_time, primary, state: Released, position, @@ -2268,6 +2289,7 @@ unsafe fn public_window_callback_inner( }); userdata.send_window_event(window, WindowEvent::PointerLeft { device_id: None, + event_time, primary, position: Some(position), kind, @@ -2288,6 +2310,7 @@ unsafe fn public_window_callback_inner( userdata.send_window_event(window, WindowEvent::PointerMoved { device_id: None, + event_time, primary, position, source, diff --git a/winit-x11/src/event_processor.rs b/winit-x11/src/event_processor.rs index 66f92e1f6f..b3b4270a4c 100644 --- a/winit-x11/src/event_processor.rs +++ b/winit-x11/src/event_processor.rs @@ -1101,7 +1101,12 @@ impl EventProcessor { } let position = PhysicalPosition::new(event.event_x, event.event_y); - let event_time = Some(Duration::from_millis(event.time)); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others" + )] + let event_time = Some(Duration::from_millis(event.time as u64)); let event = match event.detail as u32 { xlib::Button1 => WindowEvent::PointerButton { @@ -1196,7 +1201,12 @@ impl EventProcessor { let window = event.event as xproto::Window; let window_id = mkwid(window); let new_cursor_pos = (event.event_x, event.event_y); - let event_time = Some(Duration::from_millis(event.time)); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others." + )] + let event_time = Some(Duration::from_millis(event.time as u64)); let cursor_moved = self.with_window(window, |window| { let mut shared_state_lock = window.shared_state_lock(); @@ -1296,10 +1306,16 @@ impl EventProcessor { if self.window_exists(window) { let device_id = Some(device_id); let position = PhysicalPosition::new(event.event_x, event.event_y); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others." + )] + let event_time = Some(Duration::from_millis(event.time as u64)); let event = WindowEvent::PointerEntered { device_id, - event_time: Some(Duration::from_millis(event.time)), + event_time, primary: true, position, kind: PointerKind::Mouse, @@ -1318,9 +1334,15 @@ impl EventProcessor { // been destroyed, which the user presumably doesn't want to deal with. if self.window_exists(window) { let window_id = mkwid(window); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others." + )] + let event_time = Some(Duration::from_millis(event.time as u64)); let event = WindowEvent::PointerLeft { device_id: Some(mkdid(event.deviceid as xinput::DeviceId)), - event_time: Some(Duration::from_millis(event.time)), + event_time, primary: true, position: Some(PhysicalPosition::new(event.event_x, event.event_y)), kind: PointerKind::Mouse, @@ -1375,9 +1397,16 @@ impl EventProcessor { .get(&mkdid(xev.deviceid as xinput::DeviceId)) .map(|device| mkdid(device.attachment as xinput::DeviceId)); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others." + )] + let event_time = Some(Duration::from_millis(xev.time as u64)); + let event = WindowEvent::PointerMoved { device_id, - event_time: Some(Duration::from_millis(xev.time)), + event_time, primary: true, position, source: PointerSource::Mouse, @@ -1442,7 +1471,12 @@ impl EventProcessor { let window_id = mkwid(window); let id = xev.detail as u32; let position = PhysicalPosition::new(xev.event_x, xev.event_y); - let event_time = Some(Duration::from_millis(xev.time)); + #[expect( + clippy::unnecessary_cast, + reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ + 64-bit on others." + )] + let event_time = Some(Duration::from_millis(xev.time as u64)); // Mouse cursor position changes when touch events are received. // Only the first concurrently active touch ID moves the mouse cursor. From 7b6bf9caffc0b4abb38ff96d56436b3cbdb881df Mon Sep 17 00:00:00 2001 From: Martin Marmsoler Date: Mon, 28 Sep 2026 09:51:09 +0200 Subject: [PATCH 8/8] fix clippy --- winit-x11/src/event_processor.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/winit-x11/src/event_processor.rs b/winit-x11/src/event_processor.rs index b3b4270a4c..2c696c5a07 100644 --- a/winit-x11/src/event_processor.rs +++ b/winit-x11/src/event_processor.rs @@ -1101,7 +1101,7 @@ impl EventProcessor { } let position = PhysicalPosition::new(event.event_x, event.event_y); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others" @@ -1201,7 +1201,7 @@ impl EventProcessor { let window = event.event as xproto::Window; let window_id = mkwid(window); let new_cursor_pos = (event.event_x, event.event_y); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others." @@ -1306,7 +1306,7 @@ impl EventProcessor { if self.window_exists(window) { let device_id = Some(device_id); let position = PhysicalPosition::new(event.event_x, event.event_y); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others." @@ -1334,7 +1334,7 @@ impl EventProcessor { // been destroyed, which the user presumably doesn't want to deal with. if self.window_exists(window) { let window_id = mkwid(window); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others." @@ -1397,7 +1397,7 @@ impl EventProcessor { .get(&mkdid(xev.deviceid as xinput::DeviceId)) .map(|device| mkdid(device.attachment as xinput::DeviceId)); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others." @@ -1471,7 +1471,7 @@ impl EventProcessor { let window_id = mkwid(window); let id = xev.detail as u32; let position = PhysicalPosition::new(xev.event_x, xev.event_y); - #[expect( + #[allow( clippy::unnecessary_cast, reason = "`Time` is `c_ulong`, which is 32-bit on some platforms (e.g. x86) and \ 64-bit on others."