fix: measure the swipe speed with the compositor's touch event times (Issue #28)

Pia's log shows the lift arriving 200-300 ms after the last move on every
swipe, so the release looked like a resting finger and nothing glided. The
vendored winit now keeps the Wayland timestamp of each touch event
(take_touch_time), and the fling measures with those times instead of when
the app got around to handling the events. The diagnostics log both gaps and
any event handling that blocks the app for more than 40 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
haraldandClaude Opus 5.5 committed 2026-10-26 14:08:13 +01:00
1 parent b11fb2d762
commit dd515787ec
5 files changed
+152 -9

No files matched your search

+4
View File
@@ -14,6 +14,10 @@ docs of the original crate were removed.
- `src/platform/wayland.rs`: `ignore_decoration_touches_until` lets ink-notes ignore touches on
the title bar while the pen is near the screen, so a resting palm does not move the window
(Issue #39).
- `src/platform/wayland.rs` and the touch handler: `take_touch_time` returns the compositor
timestamp of each touch event (Started, Moved, Ended), so ink-notes measures the swipe speed
for gliding with the real event times and not with the time it got around to handling them
(Issue #28).
- `Cargo.toml`: `[lints.rust] warnings = "allow"`, because a path dependency is not built with
capped lints like a crates.io dependency.
+30
View File
@@ -28,6 +28,36 @@ pub(crate) fn decoration_touches_ignored() -> bool {
.is_some_and(|until| std::time::Instant::now() < until)
}
/// ink-notes patch: compositor timestamps (ms) of the touch events not yet taken, in event order.
static TOUCH_TIMES: std::sync::Mutex<
std::collections::VecDeque<(u64, crate::event::TouchPhase, u32)>,
> = std::sync::Mutex::new(std::collections::VecDeque::new());
/// ink-notes patch: remembers the compositor time of a touch event that is about to be sent.
pub(crate) fn push_touch_time(id: u64, phase: crate::event::TouchPhase, time: u32) {
let mut times = TOUCH_TIMES.lock().unwrap_or_else(|e| e.into_inner());
// Nobody takes them (e.g. an app that never asks): keep only the latest
if times.len() >= 256 {
times.pop_front();
}
times.push_back((id, phase, time));
}
/// ink-notes patch: compositor time in milliseconds (arbitrary origin, wraps around) of the
/// touch event with this id and phase (`Started`, `Moved` or `Ended`; `Cancelled` has none),
/// for the event currently being handled. Call it once for every such `WindowEvent::Touch`.
/// `None` on X11 or if the time is unknown.
pub fn take_touch_time(id: u64, phase: crate::event::TouchPhase) -> Option<u32> {
let mut times = TOUCH_TIMES.lock().unwrap_or_else(|e| e.into_inner());
// Older entries that were never taken are skipped
while let Some((i, p, time)) = times.pop_front() {
if i == id && p == phase {
return Some(time);
}
}
None
}
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland.
pub trait EventLoopWindowTargetExtWayland {
/// True if the [`EventLoopWindowTarget`] uses Wayland.
@@ -83,6 +83,9 @@ impl TouchHandler for WinitState {
.touch_map
.insert(id, TouchPoint { surface, location, frame_of: None });
// ink-notes patch: keep the event time for `take_touch_time`
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Started, time);
self.events_sink.push_window_event(
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
@@ -138,6 +141,8 @@ impl TouchHandler for WinitState {
None => return,
};
// ink-notes patch: keep the event time for `take_touch_time`
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Ended, time);
self.events_sink.push_window_event(
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
@@ -157,7 +162,7 @@ impl TouchHandler for WinitState {
_: &Connection,
_: &QueueHandle<Self>,
touch: &WlTouch,
_: u32,
time: u32,
id: i32,
position: (f64, f64),
) {
@@ -192,6 +197,9 @@ impl TouchHandler for WinitState {
touch_point.location = LogicalPosition::<f64>::from(position);
// ink-notes patch: keep the event time for `take_touch_time`
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Moved, time);
self.events_sink.push_window_event(
WindowEvent::Touch(Touch {
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(