Skip to main content

plat/
evq.rs

1// evq -- the platform event ring (shared by every platform and the script runner)
2//                                                                        [src/plat/evq.s]
3//
4//   evq_push(ev) -> false when the event was dropped (ring and overflow full)
5//   evq_pop() -> Some(event)        evq_count()        evq_clear()
6//   evq_dropped() -> events dropped so far (tests)
7//   evq_pen_data(pressure, type, tilt_x, tilt_y) -> u32   (PN1) Event::data of a pointer
8//        event: pressure -1 = the device has none (-> 0), else clamped to 1..PRESSURE_ONE;
9//        type PTR_*; tilts in degrees, clamped to -63..63.  0 for PTR_MOUSE and while pen
10//        input is off.
11//   finger(win, phase, id, x, y, age_ms) -> false when it was dropped: a host's raw finger, any
12//        platform (the UI's gesture recogniser takes it: ui/touch.rs); finger_event: its Event
13//   pen_enable(on)    pen input on (default) / off: off = pens report plain mouse events
14//   pen_enabled() -> bool
15//
16// The OS layer pushes from its message handlers, the UI core pops between frames (one
17// thread; the lock only makes the static safe).
18//
19// A burst larger than the ring (one socket read of synthetic keys on X11 / Wayland, a client
20// flooding input: their dispatch must read on to an awaited reply, so it cannot stop at a
21// full ring) waits in a second-level queue on the heap, in order; the ring takes from it as
22// it drains.  Only beyond EVQ_MORE_MAX is an event dropped: counted (evq_dropped) and
23// logged once per burst.  Windows keeps WM_CHAR bursts in its own message queue instead
24// (wait() stops pumping at EVQ_CAP - 64).
25
26use alloc::collections::VecDeque;
27use core::sync::atomic::{AtomicBool, Ordering};
28
29use rt::sync::SpinLock;
30
31use crate::{Event, EVQ_CAP};
32
33/// events waiting behind a full ring at most (16 bytes each: 1 MiB)
34const EVQ_MORE_MAX: usize = 1 << 16;
35
36pub const PD_PRESSURE: u32 = 0xFFFF; // bits 0..15 pressure 1..PRESSURE_ONE, 0 = none (full)
37pub const PD_TYPE_SHIFT: u32 = 16; // bits 16..17 PTR_*
38pub const PD_TILTX_SHIFT: u32 = 18; // bits 18..24 i7 tilt x in degrees (-63..63, + = right)
39pub const PD_TILTY_SHIFT: u32 = 25; // bits 25..31 i7 tilt y in degrees (+ = towards the user)
40// a finger (finger_event, PTR_TOUCH) has no tilt: bits 18..30 how old the sample was when it was
41// pushed (ms), bit 31 it was cancelled; Event::btn is the finger's id
42pub const PD_AGE_SHIFT: u32 = 18;
43pub const PD_AGE_MASK: u32 = 0x1FFF;
44pub const PD_CANCEL: u32 = 1 << 31;
45pub const PRESSURE_ONE: i32 = 65535;
46
47struct Ring {
48    ev: [Event; EVQ_CAP],
49    r: u32,
50    w: u32,
51    /// the newer events of a burst while the ring is full (never used otherwise)
52    more: VecDeque<Event>,
53    /// events dropped beyond EVQ_MORE_MAX; this burst was logged
54    dropped: u32,
55    logged: bool,
56}
57
58static RING: SpinLock<Ring> = SpinLock::new(Ring {
59    ev: [Event { typ: 0, btn: 0, mods: 0, win: 0, x: 0, y: 0, data: 0 }; EVQ_CAP],
60    r: 0,
61    w: 0,
62    more: VecDeque::new(),
63    dropped: 0,
64    logged: false,
65});
66static PEN_OFF: AtomicBool = AtomicBool::new(false);
67
68/// append an event (behind a full ring: the overflow queue) -> false when it was dropped
69/// (more than EVQ_MORE_MAX events wait)
70pub fn evq_push(e: Event) -> bool {
71    let mut q = RING.lock();
72    if q.more.is_empty() && (q.w.wrapping_sub(q.r) as usize) < EVQ_CAP {
73        let i = q.w as usize & (EVQ_CAP - 1);
74        q.ev[i] = e;
75        q.w = q.w.wrapping_add(1);
76        return true;
77    }
78    if q.more.len() < EVQ_MORE_MAX {
79        q.more.push_back(e);
80        return true;
81    }
82    q.dropped = q.dropped.wrapping_add(1);
83    let first = !q.logged;
84    q.logged = true;
85    drop(q);
86    if first {
87        crate::log_warn("plat", "event queue full: more than 65536 events wait, input is dropped");
88    }
89    false
90}
91
92/// the oldest queued event
93pub fn evq_pop() -> Option<Event> {
94    let mut q = RING.lock();
95    if q.r == q.w {
96        return None; // (the overflow queue only fills behind a full ring)
97    }
98    let e = q.ev[q.r as usize & (EVQ_CAP - 1)];
99    q.r = q.r.wrapping_add(1);
100    if let Some(m) = q.more.pop_front() {
101        // the oldest waiting event takes the free place
102        let i = q.w as usize & (EVQ_CAP - 1);
103        q.ev[i] = m;
104        q.w = q.w.wrapping_add(1);
105        if q.more.is_empty() {
106            q.logged = false;
107            if q.more.capacity() > EVQ_CAP {
108                q.more = VecDeque::new(); // a big burst's memory goes with it
109            }
110        }
111    }
112    Some(e)
113}
114
115/// events queued (the ring and the overflow queue)
116pub fn evq_count() -> u32 {
117    let q = RING.lock();
118    q.w.wrapping_sub(q.r).wrapping_add(q.more.len() as u32)
119}
120
121pub fn evq_clear() {
122    let mut q = RING.lock();
123    q.r = q.w;
124    q.more = VecDeque::new();
125    q.logged = false;
126}
127
128/// events dropped since the start because more than EVQ_MORE_MAX waited (tests)
129pub fn evq_dropped() -> u32 {
130    RING.lock().dropped
131}
132
133/// Event::data of a pen pointer event (0 for a mouse or while pen input is off)
134pub fn evq_pen_data(pressure: i32, typ: u32, tilt_x: i32, tilt_y: i32) -> u32 {
135    if PEN_OFF.load(Ordering::Relaxed) {
136        return 0;
137    }
138    let t = typ & 3;
139    if t == 0 {
140        return 0; // PTR_MOUSE: no data
141    }
142    let mut v = 0u32;
143    if pressure >= 0 {
144        v = pressure.clamp(1, PRESSURE_ONE) as u32;
145    }
146    v |= t << PD_TYPE_SHIFT;
147    v |= ((tilt_x.clamp(-63, 63) & 127) as u32) << PD_TILTX_SHIFT;
148    v |= ((tilt_y.clamp(-63, 63) & 127) as u32) << PD_TILTY_SHIFT;
149    v
150}
151
152/// what happens to a finger (finger)
153#[derive(Clone, Copy, PartialEq, Eq, Debug)]
154pub enum FingerPhase {
155    Down,
156    Move,
157    Up,
158    /// the system took it: no click, no fling
159    Cancel,
160}
161
162/// the Event of a raw finger: E_DOWN / E_MOVE / E_UP with PTR_TOUCH in its data.  id: the host's
163/// number of this finger while it is down (a second one has another); age_ms: how old the sample
164/// is now (the velocity needs it; 0 when the host does not know)
165pub fn finger_event(win: u8, phase: FingerPhase, id: u8, x: i32, y: i32, age_ms: u32) -> Event {
166    let typ = match phase {
167        FingerPhase::Down => crate::E_DOWN,
168        FingerPhase::Move => crate::E_MOVE,
169        FingerPhase::Up | FingerPhase::Cancel => crate::E_UP,
170    };
171    let mut data = crate::PTR_TOUCH << PD_TYPE_SHIFT | age_ms.min(PD_AGE_MASK) << PD_AGE_SHIFT;
172    if phase == FingerPhase::Cancel {
173        data |= PD_CANCEL;
174    }
175    Event { typ, btn: id, mods: 0, win, x, y, data }
176}
177
178/// A host's raw finger, in px of its window (Event::win as for every pointer event): this is all a
179/// platform does for touch; pan, fling, tap, long press and what takes the finger are decided in
180/// ui/touch.rs.  Every finger goes through here, in the order they happen; a finger that goes away
181/// without an Up gets a Cancel.  The browser and Android, whose windows live in one surface, come
182/// through jfgui_finger instead.  -> false when the event was dropped
183pub fn finger(win: u8, phase: FingerPhase, id: u8, x: i32, y: i32, age_ms: u32) -> bool {
184    evq_push(finger_event(win, phase, id, x, y, age_ms))
185}
186
187/// (Wayland, X11) how old a sample with the display server's time stamp t (ms) is: that clock's zero
188/// is unknown, so the difference to ours is taken against the smallest one seen (the sample that came at
189/// once).  offset: the host keeps it
190#[cfg(target_os = "linux")]
191pub(crate) fn finger_age(offset: &mut Option<i32>, t: u32) -> u32 {
192    let d = ((crate::time_us() / 1000) as u32).wrapping_sub(t) as i32;
193    let o = match *offset {
194        Some(o) if d.wrapping_sub(o) >= 0 => o,
195        _ => d,
196    };
197    *offset = Some(o);
198    let age = d.wrapping_sub(o) as u32;
199    if age > 5000 { 0 } else { age }
200}
201
202/// pen input on (default) / off (pens become plain mouse events)
203pub fn pen_enable(on: bool) {
204    PEN_OFF.store(!on, Ordering::Relaxed);
205}
206
207pub fn pen_enabled() -> bool {
208    !PEN_OFF.load(Ordering::Relaxed)
209}