ui_events/pointer/mod.rs
1// Copyright 2025 the UI Events Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Pointer Event Types
5
6mod buttons;
7
8pub use buttons::{PointerButton, PointerButtons};
9
10extern crate alloc;
11use alloc::vec::Vec;
12
13use core::num::NonZeroU64;
14
15use dpi::{PhysicalPosition, PhysicalSize};
16use keyboard_types::Modifiers;
17
18use crate::ScrollDelta;
19
20/// A unique identifier for the pointer.
21///
22/// PointerId(1) is reserved for the primary pointer.
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
24pub struct PointerId(NonZeroU64);
25
26impl PointerId {
27 /// The id of the primary pointer.
28 pub const PRIMARY: Self = Self(NonZeroU64::MIN);
29
30 /// Make a new `PointerId` from a `u64`.
31 #[inline(always)]
32 pub fn new(n: u64) -> Option<Self> {
33 NonZeroU64::new(n).map(PointerId)
34 }
35}
36
37/// An identifier for the pointing device that is stable across the session.
38///
39/// PointerId(1) is reserved for the primary pointer.
40#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub struct PersistentDeviceId(NonZeroU64);
42
43impl PersistentDeviceId {
44 /// Make a new `PersistentDeviceId` from a `u64`.
45 #[inline(always)]
46 pub fn new(n: u64) -> Option<Self> {
47 NonZeroU64::new(n).map(PersistentDeviceId)
48 }
49}
50
51/// The type of device that has generated a pointer event.
52#[non_exhaustive]
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
54#[repr(u8)]
55pub enum PointerType {
56 /// The type of device could not be determined.
57 #[default]
58 Unknown,
59 /// A mouse.
60 Mouse,
61 /// A pen or stylus.
62 Pen,
63 /// A touch contact.
64 Touch,
65}
66
67/// Identifying information about a pointer, stable across states.
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
69pub struct PointerInfo {
70 /// Pointer ID.
71 ///
72 /// [`PointerId::PRIMARY`] is reserved for the primary pointer,
73 /// so when converting platform pointer IDs on a platform that
74 /// does not reserve this value, add an offset to avoid collision.
75 ///
76 /// `None` is for events not originating from a pointing device.
77 pub pointer_id: Option<PointerId>,
78 /// Persistent device ID.
79 ///
80 /// This should be set when the platform can identify a given pointing
81 /// device during the whole session, and associate it with events.
82 /// If this is not possible for the given event, it should be `None`.
83 pub persistent_device_id: Option<PersistentDeviceId>,
84 /// Pointer type.
85 pub pointer_type: PointerType,
86}
87
88/// Orientation of a pointer.
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub struct PointerOrientation {
91 /// Spherical altitude.
92 ///
93 /// 0 is parallel to the surface, π/2 is perpendicular.
94 pub altitude: f32,
95 /// Spherical azimuth.
96 ///
97 /// 0 is the positive x axis, π/2 is positive y.
98 pub azimuth: f32,
99}
100
101impl Default for PointerOrientation {
102 fn default() -> Self {
103 Self {
104 altitude: core::f32::consts::FRAC_PI_2,
105 azimuth: core::f32::consts::FRAC_PI_2,
106 }
107 }
108}
109
110/// The size of an input, usually touch.
111///
112/// If this is not provided by the underlying API, platform, or device,
113/// then it should be a single pixel.
114pub type ContactGeometry = PhysicalSize<f64>;
115
116/// A single pointer state.
117#[derive(Clone, Debug, PartialEq)]
118pub struct PointerState {
119 /// `u64` nanoseconds real time.
120 ///
121 /// The base time is not important, except by convention, and should
122 /// generally be the same at least for states originating from the
123 /// same device.
124 pub time: u64,
125 /// Position.
126 pub position: PhysicalPosition<f64>,
127 /// Pressed buttons.
128 pub buttons: PointerButtons,
129 /// Modifiers state.
130 pub modifiers: Modifiers,
131 /// Click or tap count associated with the pointer.
132 pub count: u8,
133 /// The size of an input, usually touch.
134 ///
135 /// If this is not provided by the underlying API, platform, or device,
136 /// then it should be a single pixel.
137 pub contact_geometry: ContactGeometry,
138 /// Orientation.
139 pub orientation: PointerOrientation,
140 /// Normalized pressure in range 0..1.
141 ///
142 /// Where pressure is not reported by the platform, it
143 /// is always 0.5 when activated and 0.0 when not.
144 pub pressure: f32,
145 /// Normalized ‘tangential pressure’ in range -1..1.
146 ///
147 /// This is an arbitrary parameter and, despite its name,
148 /// it may not originate from a pressure sensitive control.
149 /// This is often controlled by something like a wheel on the
150 /// barrel of an ‘airbrush’ style pen.
151 pub tangential_pressure: f32,
152}
153
154impl Default for PointerState {
155 fn default() -> Self {
156 Self {
157 time: 0,
158 position: PhysicalPosition::<f64>::default(),
159 buttons: PointerButtons::default(),
160 modifiers: Modifiers::default(),
161 count: 0,
162 contact_geometry: ContactGeometry {
163 width: 1.0,
164 height: 1.0,
165 },
166 orientation: PointerOrientation::default(),
167 // No buttons pressed, therefore no pressure.
168 pressure: 0.0,
169 tangential_pressure: 0.0,
170 }
171 }
172}
173
174/// A pointer update, along with coalesced and predicted states.
175#[derive(Clone, Debug, PartialEq)]
176pub struct PointerUpdate {
177 /// Identifying information about pointer.
178 pub pointer: PointerInfo,
179 /// Current state.
180 pub current: PointerState,
181 /// Coalesced states, ordered by `time`.
182 ///
183 /// Coalescing is application-specific.
184 /// On the web, the browser does its own coalescing, whereas
185 /// on other platforms you may do your own, or forego it
186 /// altogether, delivering every state.
187 pub coalesced: Vec<PointerState>,
188 /// Predicted states, ordered by `time`.
189 ///
190 /// Some platforms provide predicted states directly,
191 /// and you may choose to add your own predictor.
192 pub predicted: Vec<PointerState>,
193}
194
195/// A standard `PointerEvent`.
196///
197/// This is intentionally limited to standard pointer events,
198/// and it is expected that applications and frameworks that
199/// support more event types will use this as a base and add
200/// what they need in a conversion.
201#[derive(Clone, Debug)]
202pub enum PointerEvent {
203 /// A [`PointerButton`] was pressed.
204 Down {
205 /// The [`PointerButton`] that was pressed..
206 button: Option<PointerButton>,
207 /// Identity of the pointer.
208 pointer: PointerInfo,
209 /// The state of the pointer (i.e. position, pressure, etc.).
210 state: PointerState,
211 },
212 /// A [`PointerButton`] was released.
213 Up {
214 /// The [`PointerButton`] that was released.
215 button: Option<PointerButton>,
216 /// Identity of the pointer.
217 pointer: PointerInfo,
218 /// The state of the pointer (i.e. position, pressure, etc.).
219 state: PointerState,
220 },
221 /// Pointer moved.
222 Move(PointerUpdate),
223 /// Pointer motion was cancelled.
224 ///
225 /// Usually this is a touch which was taken over somewhere else.
226 /// You should try to undo the effect of the gesture when you receive this.
227 Cancel(PointerInfo),
228 /// Pointer entered the area that receives this event.
229 Enter(PointerInfo),
230 /// Pointer left the area that receives these events.
231 Leave(PointerInfo),
232 /// A scroll was requested at the pointer location.
233 ///
234 /// Usually this is caused by a mouse wheel or a touchpad.
235 Scroll {
236 /// Identity of the pointer.
237 pointer: PointerInfo,
238 /// The delta of the scroll.
239 delta: ScrollDelta,
240 /// The state of the pointer (i.e. position, pressure, etc.).
241 state: PointerState,
242 },
243}