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}