aboutsummaryrefslogtreecommitdiff
path: root/src/swipe_tracker.rs
diff options
context:
space:
mode:
authorIvan Molodetskikh <yalterz@gmail.com>2024-03-02 14:33:22 +0400
committerIvan Molodetskikh <yalterz@gmail.com>2024-03-02 14:33:22 +0400
commite0ec6e5b11bc222c252157beb064104f4c60dfbe (patch)
tree04fd4807424fa75b1dd36e87f9e3bc5b94d91c05 /src/swipe_tracker.rs
parent93243d77728c3a0f7d314ed916b5f1a273861990 (diff)
downloadniri-e0ec6e5b11bc222c252157beb064104f4c60dfbe.tar.gz
niri-e0ec6e5b11bc222c252157beb064104f4c60dfbe.tar.bz2
niri-e0ec6e5b11bc222c252157beb064104f4c60dfbe.zip
Make vertical touchpad swipe inertial
Values and implementation are heavily inspired by AdwSwipeTracker.
Diffstat (limited to 'src/swipe_tracker.rs')
-rw-r--r--src/swipe_tracker.rs87
1 files changed, 87 insertions, 0 deletions
diff --git a/src/swipe_tracker.rs b/src/swipe_tracker.rs
new file mode 100644
index 00000000..d7f42778
--- /dev/null
+++ b/src/swipe_tracker.rs
@@ -0,0 +1,87 @@
+use std::collections::VecDeque;
+use std::time::Duration;
+
+const HISTORY_LIMIT: Duration = Duration::from_millis(150);
+const DECELERATION_TOUCHPAD: f64 = 0.997;
+
+#[derive(Debug)]
+pub struct SwipeTracker {
+ history: VecDeque<Event>,
+ pos: f64,
+}
+
+#[derive(Debug, Clone, Copy)]
+struct Event {
+ delta: f64,
+ timestamp: Duration,
+}
+
+impl SwipeTracker {
+ #[allow(clippy::new_without_default)]
+ pub fn new() -> Self {
+ Self {
+ history: VecDeque::new(),
+ pos: 0.,
+ }
+ }
+
+ /// Pushes a new reading into the tracker.
+ pub fn push(&mut self, delta: f64, timestamp: Duration) {
+ // For the events that we care about, timestamps should always increase
+ // monotonically.
+ if let Some(last) = self.history.back() {
+ if timestamp < last.timestamp {
+ trace!(
+ "ignoring event with timestamp {timestamp:?} earlier than last {:?}",
+ last.timestamp
+ );
+ return;
+ }
+ }
+
+ self.history.push_back(Event { delta, timestamp });
+ self.pos += delta;
+
+ self.retain_recent();
+ }
+
+ /// Returns the current gesture position.
+ pub fn pos(&self) -> f64 {
+ self.pos
+ }
+
+ /// Computes the current gesture velocity.
+ pub fn velocity(&self) -> f64 {
+ let (Some(first), Some(last)) = (self.history.front(), self.history.back()) else {
+ return 0.;
+ };
+
+ let total_time = (last.timestamp - first.timestamp).as_secs_f64();
+ if total_time == 0. {
+ return 0.;
+ }
+
+ let total_delta = self.history.iter().map(|event| event.delta).sum::<f64>();
+ total_delta / total_time
+ }
+
+ /// Computes the gesture end position after decelerating to a halt.
+ pub fn projected_end_pos(&self) -> f64 {
+ let vel = self.velocity();
+ self.pos - vel / (1000. * DECELERATION_TOUCHPAD.ln())
+ }
+
+ fn retain_recent(&mut self) {
+ let Some(&Event { timestamp, .. }) = self.history.back() else {
+ return;
+ };
+
+ while let Some(first) = self.history.front() {
+ if timestamp <= first.timestamp + HISTORY_LIMIT {
+ break;
+ }
+
+ let _ = self.history.pop_front();
+ }
+ }
+}