Skip to main content

ferroui_linuxframebuffer/input/lib_input/
lib_input_native_unsafe_methods.rs

1//! The functions and enumerations of `libinput` (the port of
2//! `LibInputNativeUnsafeMethods.cs`).
3//!
4//! The reference declares platform invokes of `libinput.so.10`, which its
5//! runtime resolves when one is first called. Here the library is opened
6//! with `dlopen` and every function is resolved when the backend starts
7//! (`native_library.rs`; docs/porting/linux-framebuffer-platform.md,
8//! section 3): nothing links the library, and a machine without it gets an
9//! error that names it.
10
11#![allow(non_camel_case_types)]
12
13/// `libinput_event_type`.
14pub mod lib_input_event_type {
15    pub const LIBINPUT_EVENT_NONE: i32 = 0;
16    pub const LIBINPUT_EVENT_DEVICE_ADDED: i32 = 1;
17    pub const LIBINPUT_EVENT_DEVICE_REMOVED: i32 = 2;
18    pub const LIBINPUT_EVENT_KEYBOARD_KEY: i32 = 300;
19    pub const LIBINPUT_EVENT_POINTER_MOTION: i32 = 400;
20    pub const LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: i32 = 401;
21    pub const LIBINPUT_EVENT_POINTER_BUTTON: i32 = 402;
22    pub const LIBINPUT_EVENT_POINTER_AXIS: i32 = 403;
23    pub const LIBINPUT_EVENT_POINTER_SCROLL_WHEEL: i32 = 404;
24    pub const LIBINPUT_EVENT_POINTER_SCROLL_FINGER: i32 = 405;
25    pub const LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS: i32 = 406;
26    pub const LIBINPUT_EVENT_TOUCH_DOWN: i32 = 500;
27    pub const LIBINPUT_EVENT_TOUCH_UP: i32 = 501;
28    pub const LIBINPUT_EVENT_TOUCH_MOTION: i32 = 502;
29    pub const LIBINPUT_EVENT_TOUCH_CANCEL: i32 = 503;
30    pub const LIBINPUT_EVENT_TOUCH_FRAME: i32 = 504;
31    pub const LIBINPUT_EVENT_TABLET_TOOL_AXIS: i32 = 600;
32    pub const LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: i32 = 601;
33    pub const LIBINPUT_EVENT_TABLET_TOOL_TIP: i32 = 602;
34    pub const LIBINPUT_EVENT_TABLET_TOOL_BUTTON: i32 = 603;
35    pub const LIBINPUT_EVENT_TABLET_PAD_BUTTON: i32 = 700;
36    pub const LIBINPUT_EVENT_TABLET_PAD_RING: i32 = 701;
37    pub const LIBINPUT_EVENT_TABLET_PAD_STRIP: i32 = 702;
38    pub const LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: i32 = 800;
39    pub const LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: i32 = 801;
40    pub const LIBINPUT_EVENT_GESTURE_SWIPE_END: i32 = 802;
41    pub const LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: i32 = 803;
42    pub const LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: i32 = 804;
43    pub const LIBINPUT_EVENT_GESTURE_PINCH_END: i32 = 805;
44    pub const LIBINPUT_EVENT_SWITCH_TOGGLE: i32 = 900;
45}
46
47/// `libinput_pointer_axis_source`.
48pub mod lib_input_pointer_axis_source {
49    /// The event is caused by the rotation of a wheel.
50    pub const LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: i32 = 1;
51    /// The event is caused by the movement of one or more fingers on a device.
52    pub const LIBINPUT_POINTER_AXIS_SOURCE_FINGER: i32 = 2;
53    /// The event is caused by the motion of some device.
54    pub const LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: i32 = 3;
55    /// The event is caused by the tilting of a mouse wheel rather than
56    /// its rotation. Deprecated as of libinput 1.16.
57    pub const LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT: i32 = 4;
58}
59
60/// `libinput_pointer_axis`.
61pub mod lib_input_pointer_axis {
62    pub const LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL: i32 = 0;
63    pub const LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL: i32 = 1;
64}
65
66#[cfg(target_os = "linux")]
67pub use imp::LibInput;
68
69#[cfg(target_os = "linux")]
70mod imp {
71    use std::ffi::{c_char, c_double, c_int, c_void, CString};
72
73    /// `struct libinput_interface`: how the library opens and closes the
74    /// devices.
75    #[repr(C)]
76    pub(crate) struct libinput_interface {
77        open_restricted: unsafe extern "C" fn(path: *const c_char, flags: c_int, user_data: *mut c_void) -> c_int,
78        close_restricted: unsafe extern "C" fn(fd: c_int, user_data: *mut c_void),
79    }
80
81    unsafe extern "C" fn open_restricted(path: *const c_char, flags: c_int, _user_data: *mut c_void) -> c_int {
82        // SAFETY: the library passes a valid C string.
83        let fd = unsafe { libc::open(path, flags, 0) };
84        if fd == -1 {
85            return -std::io::Error::last_os_error().raw_os_error().unwrap_or(libc::EIO);
86        }
87        fd
88    }
89
90    unsafe extern "C" fn close_restricted(fd: c_int, _user_data: *mut c_void) {
91        // SAFETY: the library closes a descriptor `open_restricted` gave it.
92        unsafe {
93            libc::close(fd);
94        }
95    }
96
97    static INTERFACE: libinput_interface = libinput_interface { open_restricted, close_restricted };
98
99    ferroui_base::native_functions! {
100        /// The functions of the library, resolved.
101        pub struct LibInput(lib) {
102            lib::libinput_path_create_context: unsafe extern "C" fn(*const libinput_interface, *mut c_void) -> *mut c_void;
103            lib::libinput_path_add_device: unsafe extern "C" fn(*mut c_void, *const c_char) -> *mut c_void;
104            lib::libinput_path_remove_device: unsafe extern "C" fn(*mut c_void);
105            lib::libinput_device_config_calibration_set_matrix: unsafe extern "C" fn(*mut c_void, *const f32) -> c_int;
106            lib::libinput_get_fd: unsafe extern "C" fn(*mut c_void) -> c_int;
107            lib::libinput_dispatch: unsafe extern "C" fn(*mut c_void) -> c_int;
108            lib::libinput_get_event: unsafe extern "C" fn(*mut c_void) -> *mut c_void;
109            lib::libinput_event_get_type: unsafe extern "C" fn(*mut c_void) -> c_int;
110            lib::libinput_event_destroy: unsafe extern "C" fn(*mut c_void);
111            lib::libinput_event_get_touch_event: unsafe extern "C" fn(*mut c_void) -> *mut c_void;
112            lib::libinput_event_touch_get_slot: unsafe extern "C" fn(*mut c_void) -> c_int;
113            lib::libinput_event_touch_get_time_usec: unsafe extern "C" fn(*mut c_void) -> u64;
114            lib::libinput_event_touch_get_x_transformed: unsafe extern "C" fn(*mut c_void, u32) -> c_double;
115            lib::libinput_event_touch_get_y_transformed: unsafe extern "C" fn(*mut c_void, u32) -> c_double;
116            lib::libinput_event_get_pointer_event: unsafe extern "C" fn(*mut c_void) -> *mut c_void;
117            lib::libinput_event_pointer_get_time_usec: unsafe extern "C" fn(*mut c_void) -> u64;
118            lib::libinput_event_pointer_get_absolute_x_transformed: unsafe extern "C" fn(*mut c_void, u32) -> c_double;
119            lib::libinput_event_pointer_get_absolute_y_transformed: unsafe extern "C" fn(*mut c_void, u32) -> c_double;
120            lib::libinput_event_pointer_get_button: unsafe extern "C" fn(*mut c_void) -> u32;
121            lib::libinput_event_pointer_get_button_state: unsafe extern "C" fn(*mut c_void) -> c_int;
122            lib::libinput_event_pointer_get_axis_source: unsafe extern "C" fn(*mut c_void) -> c_int;
123            lib::libinput_event_pointer_get_axis_value_discrete: unsafe extern "C" fn(*mut c_void, c_int) -> c_double;
124            lib::libinput_event_pointer_get_scroll_value_v120: unsafe extern "C" fn(*mut c_void, c_int) -> c_double;
125        }
126    }
127
128    /// A context of the library with the path backend. It belongs to the
129    /// thread that reads it.
130    pub struct Context<'a> {
131        lib: &'a LibInput,
132        ctx: *mut c_void,
133    }
134
135    /// An event of a context, destroyed when dropped.
136    pub struct Event<'a> {
137        lib: &'a LibInput,
138        ev: *mut c_void,
139    }
140
141    impl LibInput {
142        /// `libinput_path_create_context` with the interface of this
143        /// module.
144        pub fn path_create_context(&self) -> Option<Context<'_>> {
145            // SAFETY: the interface is a static of the layout the library
146            // expects; the user data is not used.
147            let ctx = unsafe { (self.libinput_path_create_context)(&INTERFACE, std::ptr::null_mut()) };
148            (!ctx.is_null()).then_some(Context { lib: self, ctx })
149        }
150    }
151
152    // The calls below pass pointers the library returned for this context
153    // (or events of it that are alive), which is what each function
154    // requires; a context is used by one thread.
155    impl<'a> Context<'a> {
156        /// Adds a device; returns whether the library took it. The
157        /// calibration matrix (six numbers, the first two rows) is set on
158        /// a device that was taken.
159        pub fn path_add_device(&self, path: &str, calibration: &[f32; 6]) -> bool {
160            let Ok(path) = CString::new(path) else {
161                return false;
162            };
163            // SAFETY: see above; `path` is a valid C string.
164            let device = unsafe { (self.lib.libinput_path_add_device)(self.ctx, path.as_ptr()) };
165            if device.is_null() {
166                return false;
167            }
168            // SAFETY: see above; the matrix has the six numbers the
169            // function reads.
170            unsafe {
171                (self.lib.libinput_device_config_calibration_set_matrix)(device, calibration.as_ptr());
172            }
173            true
174        }
175
176        pub fn get_fd(&self) -> i32 {
177            // SAFETY: see above.
178            unsafe { (self.lib.libinput_get_fd)(self.ctx) }
179        }
180
181        pub fn dispatch(&self) {
182            // SAFETY: see above.
183            unsafe {
184                (self.lib.libinput_dispatch)(self.ctx);
185            }
186        }
187
188        pub fn get_event(&self) -> Option<Event<'a>> {
189            // SAFETY: see above.
190            let ev = unsafe { (self.lib.libinput_get_event)(self.ctx) };
191            (!ev.is_null()).then_some(Event { lib: self.lib, ev })
192        }
193    }
194
195    impl Event<'_> {
196        pub fn get_type(&self) -> i32 {
197            // SAFETY: see above.
198            unsafe { (self.lib.libinput_event_get_type)(self.ev) }
199        }
200
201        /// The slot and the time (in microseconds) of a touch event, and
202        /// its position as fractions of a range of `range` when
203        /// `with_position` (a down or a motion).
204        pub fn touch(&self, with_position: bool, range: u32) -> Option<(i32, u64, f64, f64)> {
205            // SAFETY: see above; the accessors are called for the event
206            // types they are defined for (the caller checks the type).
207            unsafe {
208                let tev = (self.lib.libinput_event_get_touch_event)(self.ev);
209                if tev.is_null() {
210                    return None;
211                }
212                let slot = (self.lib.libinput_event_touch_get_slot)(tev);
213                let time = (self.lib.libinput_event_touch_get_time_usec)(tev);
214                let (x, y) = if with_position {
215                    (
216                        (self.lib.libinput_event_touch_get_x_transformed)(tev, range),
217                        (self.lib.libinput_event_touch_get_y_transformed)(tev, range),
218                    )
219                } else {
220                    (0.0, 0.0)
221                };
222                Some((slot, time, x, y))
223            }
224        }
225
226        fn pointer(&self) -> *mut c_void {
227            // SAFETY: see above.
228            unsafe { (self.lib.libinput_event_get_pointer_event)(self.ev) }
229        }
230
231        pub fn pointer_time_usec(&self) -> u64 {
232            // SAFETY: see above.
233            unsafe { (self.lib.libinput_event_pointer_get_time_usec)(self.pointer()) }
234        }
235
236        /// The position of an absolute motion in a range of `range`.
237        pub fn pointer_absolute(&self, range: u32) -> (f64, f64) {
238            // SAFETY: see above; called for absolute motion only.
239            unsafe {
240                let pev = self.pointer();
241                (
242                    (self.lib.libinput_event_pointer_get_absolute_x_transformed)(pev, range),
243                    (self.lib.libinput_event_pointer_get_absolute_y_transformed)(pev, range),
244                )
245            }
246        }
247
248        /// The button and its state of a button event.
249        pub fn pointer_button(&self) -> (u32, i32) {
250            // SAFETY: see above; called for button events only.
251            unsafe {
252                let pev = self.pointer();
253                (
254                    (self.lib.libinput_event_pointer_get_button)(pev),
255                    (self.lib.libinput_event_pointer_get_button_state)(pev),
256                )
257            }
258        }
259
260        /// The source of an axis event and, for a wheel, its vertical
261        /// value in steps.
262        pub fn pointer_axis(&self) -> (i32, f64) {
263            // SAFETY: see above; called for axis events only, and the
264            // discrete value is read for wheels only.
265            unsafe {
266                let pev = self.pointer();
267                let source = (self.lib.libinput_event_pointer_get_axis_source)(pev);
268                let value = if source == super::lib_input_pointer_axis_source::LIBINPUT_POINTER_AXIS_SOURCE_WHEEL {
269                    (self.lib.libinput_event_pointer_get_axis_value_discrete)(
270                        pev,
271                        super::lib_input_pointer_axis::LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
272                    )
273                } else {
274                    0.0
275                };
276                (source, value)
277            }
278        }
279
280        /// The vertical value of a wheel scroll event, in 120ths of a step.
281        pub fn pointer_scroll_value_v120(&self) -> f64 {
282            // SAFETY: see above; called for wheel scroll events only.
283            unsafe {
284                (self.lib.libinput_event_pointer_get_scroll_value_v120)(
285                    self.pointer(),
286                    super::lib_input_pointer_axis::LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL,
287                )
288            }
289        }
290    }
291
292    impl Drop for Event<'_> {
293        fn drop(&mut self) {
294            // SAFETY: the event is destroyed once.
295            unsafe {
296                (self.lib.libinput_event_destroy)(self.ev);
297            }
298        }
299    }
300}