mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 14:49:07 -04:00
Add timestamps to events
This commit is contained in:
@@ -60,6 +60,7 @@ objc2-core-foundation = { version = "0.3.2", default-features = false }
|
||||
objc2-core-graphics = { version = "0.3.2", default-features = false }
|
||||
objc2-core-video = { version = "0.3.2", default-features = false }
|
||||
objc2-foundation = { version = "0.3.2", default-features = false }
|
||||
objc2-quartz-core = { version = "0.3.2", default-features = false }
|
||||
objc2-ui-kit = { version = "0.3.2", default-features = false }
|
||||
|
||||
# Windows dependencies.
|
||||
|
||||
@@ -107,6 +107,7 @@ objc2-foundation = { workspace = true, features = [
|
||||
"NSThread",
|
||||
"NSValue",
|
||||
] }
|
||||
objc2-quartz-core = { workspace = true, features = ["std", "CABase"] }
|
||||
winit-common = { workspace = true, features = ["core-foundation", "event-handler"] }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -98,6 +98,7 @@ pub(crate) fn override_send_event(global_app: &NSApplication) {
|
||||
}
|
||||
|
||||
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
let time = app_state.event_time(event);
|
||||
let event_type = event.r#type();
|
||||
#[allow(non_upper_case_globals)]
|
||||
match event_type {
|
||||
@@ -110,7 +111,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, None, DeviceEvent::PointerMotion {
|
||||
app.device_event(event_loop, None, time, DeviceEvent::PointerMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
});
|
||||
@@ -119,7 +120,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
let button = event.buttonNumber() as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, None, DeviceEvent::Button {
|
||||
app.device_event(event_loop, None, time, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
@@ -128,7 +129,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
let button = event.buttonNumber() as u32;
|
||||
app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, None, DeviceEvent::Button {
|
||||
app.device_event(event_loop, None, time, DeviceEvent::Button {
|
||||
button,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
|
||||
@@ -2,12 +2,14 @@ use std::cell::{Cell, OnceCell, RefCell};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use dispatch2::MainThreadBound;
|
||||
use objc2::MainThreadMarker;
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
|
||||
use objc2_foundation::NSNotification;
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSRunningApplication};
|
||||
use objc2_foundation::{NSNotification, NSTimeInterval};
|
||||
use objc2_quartz_core::CACurrentMediaTime;
|
||||
use tracing::warn;
|
||||
use winit_common::core_foundation::{EventLoopProxy, MainRunLoop};
|
||||
use winit_common::event_handler::EventHandler;
|
||||
use winit_core::application::ApplicationHandler;
|
||||
@@ -43,6 +45,8 @@ pub(super) struct AppState {
|
||||
start_time: Cell<Option<Instant>>,
|
||||
wait_timeout: Cell<Option<Instant>>,
|
||||
pending_redraw: RefCell<Vec<WindowId>>,
|
||||
startup_instant: Instant,
|
||||
startup_timestamp: NSTimeInterval,
|
||||
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
|
||||
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||
}
|
||||
@@ -63,6 +67,17 @@ impl AppState {
|
||||
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
|
||||
}));
|
||||
|
||||
// Prime dylib caches etc.
|
||||
let _ = CACurrentMediaTime();
|
||||
|
||||
// Find the current Rust timestamp.
|
||||
let startup_instant = Instant::now();
|
||||
// Find the timestamp
|
||||
//
|
||||
// `NSProcessInfo::processInfo().systemUptime()` needs the required reason manifest,
|
||||
// `CACurrentMediaTime` (currently) doesn't, so we use that instead.
|
||||
let startup_timestamp = CACurrentMediaTime();
|
||||
|
||||
let this = Rc::new(Self {
|
||||
mtm,
|
||||
activation_policy,
|
||||
@@ -83,6 +98,8 @@ impl AppState {
|
||||
start_time: Cell::new(None),
|
||||
wait_timeout: Cell::new(None),
|
||||
pending_redraw: RefCell::new(vec![]),
|
||||
startup_instant,
|
||||
startup_timestamp,
|
||||
});
|
||||
|
||||
GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this))
|
||||
@@ -96,6 +113,16 @@ impl AppState {
|
||||
.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn event_time(&self, event: &NSEvent) -> Instant {
|
||||
if event.timestamp() == 0.0 {
|
||||
warn!(?event, "got zero timestamp");
|
||||
return Instant::now();
|
||||
}
|
||||
let duration_since_startup = event.timestamp() - self.startup_timestamp;
|
||||
let duration_since_startup = Duration::from_secs_f64(duration_since_startup as f64);
|
||||
self.startup_instant + duration_since_startup
|
||||
}
|
||||
|
||||
// NOTE: This notification will, globally, only be emitted once,
|
||||
// no matter how many `EventLoop`s the user creates.
|
||||
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
|
||||
@@ -247,7 +274,12 @@ impl AppState {
|
||||
// -> Don't go back into the event handler when our callstack originates from there
|
||||
if !self.event_handler.in_use() {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
|
||||
app.window_event(
|
||||
event_loop,
|
||||
window_id,
|
||||
Instant::now(),
|
||||
WindowEvent::RedrawRequested,
|
||||
);
|
||||
});
|
||||
|
||||
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
|
||||
@@ -347,7 +379,12 @@ impl AppState {
|
||||
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.with_handler(|app, event_loop| {
|
||||
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
|
||||
app.window_event(
|
||||
event_loop,
|
||||
window_id,
|
||||
Instant::now(),
|
||||
WindowEvent::RedrawRequested,
|
||||
);
|
||||
});
|
||||
}
|
||||
self.with_handler(|app, event_loop| {
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::rc::Rc;
|
||||
use std::time::Instant;
|
||||
|
||||
use dpi::{LogicalPosition, LogicalSize};
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::{AnyObject, Sel};
|
||||
use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send};
|
||||
use objc2::{AnyThread, DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
|
||||
use objc2_app_kit::{
|
||||
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
|
||||
NSTrackingAreaOptions, NSView, NSWindow,
|
||||
@@ -16,6 +17,7 @@ use objc2_foundation::{
|
||||
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
|
||||
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
use tracing::warn;
|
||||
use winit_core::event::{
|
||||
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
|
||||
PointerKind, PointerSource, TouchPhase, WindowEvent,
|
||||
@@ -680,8 +682,9 @@ define_class!(
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
let time = self.ivars().app_state.event_time(event);
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.device_event(event_loop, None, DeviceEvent::MouseWheel { delta })
|
||||
app.device_event(event_loop, None, time, DeviceEvent::MouseWheel { delta })
|
||||
});
|
||||
self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase });
|
||||
}
|
||||
@@ -844,9 +847,16 @@ impl WinitView {
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
let app = NSApplication::sharedApplication(self.mtm());
|
||||
let window_id = window_id(&self.window());
|
||||
let time = if let Some(nsevent) = app.currentEvent() {
|
||||
self.ivars().app_state.event_time(&nsevent)
|
||||
} else {
|
||||
warn!("queued event with wrong timestamp, no active NSEvent found");
|
||||
Instant::now()
|
||||
};
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
app.window_event(event_loop, window_id, time, event);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
|
||||
use dpi::{
|
||||
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
|
||||
@@ -903,9 +904,16 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
let app = NSApplication::sharedApplication(self.mtm());
|
||||
let window_id = window_id(self.window());
|
||||
let time = if let Some(nsevent) = app.currentEvent() {
|
||||
self.ivars().app_state.event_time(&nsevent)
|
||||
} else {
|
||||
warn!("queued event with wrong timestamp, no active NSEvent found");
|
||||
Instant::now()
|
||||
};
|
||||
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
|
||||
app.window_event(event_loop, window_id, event);
|
||||
app.window_event(event_loop, window_id, time, event);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
//! End user application handling.
|
||||
#[cfg(not(target_family = "wasm"))]
|
||||
use std::time::Instant;
|
||||
|
||||
#[cfg(target_family = "wasm")]
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
|
||||
use crate::event_loop::ActiveEventLoop;
|
||||
@@ -192,10 +197,14 @@ pub trait ApplicationHandler {
|
||||
}
|
||||
|
||||
/// Emitted when the OS sends an event to a winit window.
|
||||
///
|
||||
/// Contains the ID of the window, the event, and the time the event was received. Note that
|
||||
/// since events are queued, the time will differ from [`Instant::now()`].
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
timestamp: Instant,
|
||||
event: WindowEvent,
|
||||
);
|
||||
|
||||
@@ -206,9 +215,10 @@ pub trait ApplicationHandler {
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: Option<DeviceId>,
|
||||
timestamp: Instant,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
let _ = (event_loop, device_id, event);
|
||||
let _ = (event_loop, device_id, timestamp, event);
|
||||
}
|
||||
|
||||
/// Emitted when the event loop is about to block and wait for new events.
|
||||
@@ -372,9 +382,10 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
timestamp: Instant,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
(**self).window_event(event_loop, window_id, event);
|
||||
(**self).window_event(event_loop, window_id, timestamp, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -382,9 +393,10 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: Option<DeviceId>,
|
||||
timestamp: Instant,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
(**self).device_event(event_loop, device_id, event);
|
||||
(**self).device_event(event_loop, device_id, timestamp, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -440,9 +452,10 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
timestamp: Instant,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
(**self).window_event(event_loop, window_id, event);
|
||||
(**self).window_event(event_loop, window_id, timestamp, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -450,9 +463,10 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
device_id: Option<DeviceId>,
|
||||
timestamp: Instant,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
(**self).device_event(event_loop, device_id, event);
|
||||
(**self).device_event(event_loop, device_id, timestamp, event);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Simple winit window example.
|
||||
|
||||
use std::error::Error;
|
||||
use std::time::Instant;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::event::WindowEvent;
|
||||
@@ -36,8 +37,14 @@ impl ApplicationHandler for App {
|
||||
}
|
||||
}
|
||||
|
||||
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
|
||||
println!("{event:?}");
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &dyn ActiveEventLoop,
|
||||
_: WindowId,
|
||||
timestamp: Instant,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Close was requested; stopping");
|
||||
@@ -62,11 +69,21 @@ impl ApplicationHandler for App {
|
||||
fill::fill_window(window.as_ref());
|
||||
|
||||
// For contiguous redraw loop you can request a redraw from here.
|
||||
// window.request_redraw();
|
||||
window.request_redraw();
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_event_loop: &dyn ActiveEventLoop,
|
||||
_device_id: Option<winit::event::DeviceId>,
|
||||
timestamp: Instant,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
Reference in New Issue
Block a user