Skip to main content

ferroui_linuxframebuffer/
native_unsafe_methods.rs

1//! The kernel's structures, request numbers and codes of the framebuffer
2//! and input interfaces (the port of `NativeUnsafeMethods.cs`), and the
3//! system calls on files as safe functions.
4//!
5//! The functions of the C library are those of the `libc` crate. The
6//! structures of `linux/fb.h` are declared here, since that crate does not
7//! have them; the tests at the end hold them to the kernel's sizes.
8
9#![allow(non_camel_case_types)]
10
11/// The request numbers of the framebuffer device.
12pub mod fb_io_ctl {
13    pub const FBIOGET_VSCREENINFO: u32 = 0x4600;
14    pub const FBIOPUT_VSCREENINFO: u32 = 0x4601;
15    pub const FBIOGET_FSCREENINFO: u32 = 0x4602;
16    pub const FBIOGET_VBLANK: u32 = 0x8020_4612;
17    pub const FBIO_WAITFORVSYNC: u32 = 0x4004_4620;
18    pub const FBIOPAN_DISPLAY: u32 = 0x4606;
19}
20
21/// The flags of [`fb_vblank`].
22pub mod v_blank_flags {
23    /// currently in a vertical blank
24    pub const FB_VBLANK_VBLANKING: u32 = 0x001;
25    /// currently in a horizontal blank
26    pub const FB_VBLANK_HBLANKING: u32 = 0x002;
27    /// vertical blanks can be detected
28    pub const FB_VBLANK_HAVE_VBLANK: u32 = 0x004;
29    /// horizontal blanks can be detected
30    pub const FB_VBLANK_HAVE_HBLANK: u32 = 0x008;
31    /// global retrace counter is available
32    pub const FB_VBLANK_HAVE_COUNT: u32 = 0x010;
33    /// the vcount field is valid
34    pub const FB_VBLANK_HAVE_VCOUNT: u32 = 0x020;
35    /// the hcount field is valid
36    pub const FB_VBLANK_HAVE_HCOUNT: u32 = 0x040;
37    /// currently in a vsync
38    pub const FB_VBLANK_VSYNCING: u32 = 0x080;
39    /// vertical syncs can be detected
40    pub const FB_VBLANK_HAVE_VSYNC: u32 = 0x100;
41}
42
43#[repr(C)]
44#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
45pub struct fb_vblank {
46    /// FB_VBLANK flags
47    pub flags: u32,
48    /// counter of retraces since boot
49    pub count: u32,
50    /// current scanline position
51    pub vcount: u32,
52    /// current scandot position
53    pub hcount: u32,
54    /// reserved for future compatibility
55    pub reserved: [u32; 4],
56}
57
58#[repr(C)]
59#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
60pub struct fb_fix_screeninfo {
61    /// identification string eg "TT Builtin"
62    pub id: [u8; 16],
63    /// Start of frame buffer mem (physical address)
64    pub smem_start: usize,
65    /// Length of frame buffer mem
66    pub smem_len: u32,
67    /// see FB_TYPE_*
68    pub type_: u32,
69    /// Interleave for interleaved Planes
70    pub type_aux: u32,
71    /// see FB_VISUAL_*
72    pub visual: u32,
73    /// zero if no hardware panning
74    pub xpanstep: u16,
75    /// zero if no hardware panning
76    pub ypanstep: u16,
77    /// zero if no hardware ywrap
78    pub ywrapstep: u16,
79    /// length of a line in bytes
80    pub line_length: u32,
81    /// Start of Memory Mapped I/O (physical address)
82    pub mmio_start: usize,
83    /// Length of Memory Mapped I/O
84    pub mmio_len: u32,
85    /// Type of acceleration available
86    pub accel: u32,
87    /// Reserved for future compatibility
88    pub reserved: [u16; 3],
89}
90
91impl fb_fix_screeninfo {
92    /// The identification string up to its terminator.
93    pub fn id_string(&self) -> String {
94        let idlen = self.id.iter().position(|&b| b == 0).unwrap_or(self.id.len());
95        self.id[..idlen].iter().map(|&b| if b.is_ascii() { b as char } else { '?' }).collect()
96    }
97}
98
99#[repr(C)]
100#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
101pub struct fb_bitfield {
102    /// beginning of bitfield
103    pub offset: u32,
104    /// length of bitfield
105    pub length: u32,
106    /// != 0 : Most significant bit is right
107    pub msb_right: u32,
108}
109
110#[repr(C)]
111#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
112pub struct fb_var_screeninfo {
113    /// visible resolution
114    pub xres: u32,
115    pub yres: u32,
116    /// virtual resolution
117    pub xres_virtual: u32,
118    pub yres_virtual: u32,
119    /// offset from virtual to visible resolution
120    pub xoffset: u32,
121    pub yoffset: u32,
122    /// guess what
123    pub bits_per_pixel: u32,
124    /// != 0 Graylevels instead of colors
125    pub grayscale: u32,
126    /// bitfield in fb mem if true color, else only length is significant
127    pub red: fb_bitfield,
128    pub green: fb_bitfield,
129    pub blue: fb_bitfield,
130    /// transparency
131    pub transp: fb_bitfield,
132    /// != 0 Non standard pixel format
133    pub nonstd: u32,
134    /// see FB_ACTIVATE_*
135    pub activate: u32,
136    /// height of picture in mm
137    pub height: u32,
138    /// width of picture in mm
139    pub width: u32,
140    /// acceleration flags (hints)
141    pub accel_flags: u32,
142    /* Timing: All values in pixclocks, except pixclock (of course) */
143    /// pixel clock in ps (pico seconds)
144    pub pixclock: u32,
145    /// time from sync to picture
146    pub left_margin: u32,
147    /// time from picture to sync
148    pub right_margin: u32,
149    /// time from sync to picture
150    pub upper_margin: u32,
151    pub lower_margin: u32,
152    /// length of horizontal sync
153    pub hsync_len: u32,
154    /// length of vertical sync
155    pub vsync_len: u32,
156    /// see FB_SYNC_*
157    pub sync: u32,
158    /// see FB_VMODE_*
159    pub vmode: u32,
160    /// Reserved for future compatibility
161    pub reserved: [u32; 6],
162}
163
164/// The types of input events.
165pub mod ev_type {
166    pub const EV_SYN: u16 = 0x00;
167    pub const EV_KEY: u16 = 0x01;
168    pub const EV_REL: u16 = 0x02;
169    pub const EV_ABS: u16 = 0x03;
170    pub const EV_MSC: u16 = 0x04;
171    pub const EV_SW: u16 = 0x05;
172    pub const EV_LED: u16 = 0x11;
173    pub const EV_SND: u16 = 0x12;
174    pub const EV_REP: u16 = 0x14;
175    pub const EV_FF: u16 = 0x15;
176    pub const EV_PWR: u16 = 0x16;
177    pub const EV_FF_STATUS: u16 = 0x17;
178
179    /// Every type, in the order the reference enumerates them.
180    pub const ALL: [u16; 12] =
181        [EV_SYN, EV_KEY, EV_REL, EV_ABS, EV_MSC, EV_SW, EV_LED, EV_SND, EV_REP, EV_FF, EV_PWR, EV_FF_STATUS];
182}
183
184/// The codes of `EV_SYN` events. Not in the reference, which reads events
185/// through a library that handles them.
186pub mod syn_code {
187    pub const SYN_REPORT: u16 = 0;
188    pub const SYN_DROPPED: u16 = 3;
189}
190
191/// The codes of relative axes.
192pub mod axis_event_code {
193    pub const REL_X: u16 = 0x00;
194    pub const REL_Y: u16 = 0x01;
195    pub const REL_Z: u16 = 0x02;
196    pub const REL_RX: u16 = 0x03;
197    pub const REL_RY: u16 = 0x04;
198    pub const REL_RZ: u16 = 0x05;
199    pub const REL_HWHEEL: u16 = 0x06;
200    pub const REL_DIAL: u16 = 0x07;
201    pub const REL_WHEEL: u16 = 0x08;
202    pub const REL_MISC: u16 = 0x09;
203    pub const REL_MAX: u16 = 0x0f;
204}
205
206/// The codes of absolute axes.
207pub mod abs_axis {
208    pub const ABS_X: u16 = 0x00;
209    pub const ABS_Y: u16 = 0x01;
210    pub const ABS_Z: u16 = 0x02;
211    pub const ABS_RX: u16 = 0x03;
212    pub const ABS_RY: u16 = 0x04;
213    pub const ABS_RZ: u16 = 0x05;
214    pub const ABS_THROTTLE: u16 = 0x06;
215    pub const ABS_RUDDER: u16 = 0x07;
216    pub const ABS_WHEEL: u16 = 0x08;
217    pub const ABS_GAS: u16 = 0x09;
218    pub const ABS_BRAKE: u16 = 0x0a;
219    pub const ABS_HAT0X: u16 = 0x10;
220    pub const ABS_HAT0Y: u16 = 0x11;
221    pub const ABS_HAT1X: u16 = 0x12;
222    pub const ABS_HAT1Y: u16 = 0x13;
223    pub const ABS_HAT2X: u16 = 0x14;
224    pub const ABS_HAT2Y: u16 = 0x15;
225    pub const ABS_HAT3X: u16 = 0x16;
226    pub const ABS_HAT3Y: u16 = 0x17;
227    pub const ABS_PRESSURE: u16 = 0x18;
228    pub const ABS_DISTANCE: u16 = 0x19;
229    pub const ABS_TILT_X: u16 = 0x1a;
230    pub const ABS_TILT_Y: u16 = 0x1b;
231    pub const ABS_TOOL_WIDTH: u16 = 0x1c;
232    // Additions: the axes of the multi-touch protocol B.
233    pub const ABS_MT_SLOT: u16 = 0x2f;
234    pub const ABS_MT_POSITION_X: u16 = 0x35;
235    pub const ABS_MT_POSITION_Y: u16 = 0x36;
236    pub const ABS_MT_TRACKING_ID: u16 = 0x39;
237}
238
239/// The key codes the reference names: the buttons.
240pub mod ev_key {
241    pub const BTN_LEFT: u16 = 0x110;
242    pub const BTN_RIGHT: u16 = 0x111;
243    pub const BTN_MIDDLE: u16 = 0x112;
244    pub const BTN_TOUCH: u16 = 0x14a;
245}
246
247/// An input event as the kernel writes it, with the time as two 64-bit
248/// numbers on every architecture: the value the input thread hands to the
249/// UI thread.
250#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
251pub struct input_event {
252    pub tv_sec: i64,
253    pub tv_usec: i64,
254    pub type_: u16,
255    pub code: u16,
256    pub value: i32,
257}
258
259impl input_event {
260    pub fn new(type_: u16, code: u16, value: i32) -> Self {
261        Self { tv_sec: 0, tv_usec: 0, type_, code, value }
262    }
263
264    /// The time of the event in milliseconds.
265    pub fn timestamp(&self) -> u64 {
266        let ms = self.tv_usec as u64 / 1000;
267        let s = (self.tv_sec as u64).wrapping_mul(1000);
268        s.wrapping_add(ms)
269    }
270}
271
272#[repr(C)]
273#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
274pub struct input_absinfo {
275    pub value: i32,
276    pub minimum: i32,
277    pub maximum: i32,
278    pub fuzz: i32,
279    pub flat: i32,
280    pub resolution: i32,
281}
282
283/// The number of an `ioctl` request in the encoding most architectures
284/// use (x86, ARM, RISC-V): two bits of direction, fourteen of size, eight
285/// of type and eight of number. The request numbers of the reference
286/// (`FBIO_WAITFORVSYNC`, `FBIOGET_VBLANK`) are in this encoding too.
287pub const fn ioc(dir: u32, type_: u8, nr: u32, size: usize) -> u32 {
288    (dir << 30) | ((size as u32) << 16) | ((type_ as u32) << 8) | nr
289}
290
291pub const IOC_WRITE: u32 = 1;
292pub const IOC_READ: u32 = 2;
293
294/// The system calls, each as a safe function.
295#[cfg(target_os = "linux")]
296pub mod sys {
297    use std::ffi::CString;
298    use std::io;
299    use std::os::fd::RawFd;
300
301    /// `open(pathname, flags, 0)`.
302    pub fn open(pathname: &str, flags: i32) -> io::Result<RawFd> {
303        let path = CString::new(pathname).map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
304        // SAFETY: `path` is a valid C string for the call.
305        let fd = unsafe { libc::open(path.as_ptr(), flags, 0) };
306        if fd < 0 {
307            return Err(io::Error::last_os_error());
308        }
309        Ok(fd)
310    }
311
312    pub fn close(fd: RawFd) {
313        // SAFETY: closes a descriptor the caller owns and does not use again.
314        unsafe {
315            libc::close(fd);
316        }
317    }
318
319    /// An `ioctl` that reads or writes one plain structure.
320    ///
321    /// The request must be one whose argument is a pointer to a `T`; every
322    /// caller in this crate passes a request and the structure the kernel
323    /// documents for it.
324    pub fn ioctl_struct<T: Copy>(fd: RawFd, request: u32, arg: &mut T) -> io::Result<i32> {
325        // SAFETY: `arg` is a valid, exclusive place for a `T`, and the
326        // request reads or writes at most a `T` there (see above).
327        let rv = unsafe { libc::ioctl(fd, request as _, arg as *mut T) };
328        if rv == -1 {
329            return Err(io::Error::last_os_error());
330        }
331        Ok(rv)
332    }
333
334    /// An `ioctl` that fills a buffer of bytes whose length is part of the
335    /// request number.
336    pub fn ioctl_buffer(fd: RawFd, request: u32, buffer: &mut [u8]) -> io::Result<i32> {
337        // SAFETY: the callers encode `buffer.len()` in the request, so the
338        // kernel writes at most that many bytes.
339        let rv = unsafe { libc::ioctl(fd, request as _, buffer.as_mut_ptr()) };
340        if rv == -1 {
341            return Err(io::Error::last_os_error());
342        }
343        Ok(rv)
344    }
345
346    /// An `ioctl` without an argument (a null pointer), as the reference
347    /// calls `FBIO_WAITFORVSYNC`.
348    pub fn ioctl_null(fd: RawFd, request: u32) -> i32 {
349        // SAFETY: the kernel rejects the null argument where it needs one.
350        unsafe { libc::ioctl(fd, request as _, std::ptr::null_mut::<libc::c_void>()) }
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    // Not from the reference, which has no tests for this file: the sizes
357    // and offsets are those of `linux/fb.h` and `linux/input.h`.
358    use super::*;
359    use std::mem::{offset_of, size_of};
360
361    #[test]
362    fn the_framebuffer_structures_have_the_sizes_of_the_kernel() {
363        assert_eq!(size_of::<fb_bitfield>(), 12);
364        assert_eq!(size_of::<fb_var_screeninfo>(), 160);
365        assert_eq!(size_of::<fb_vblank>(), 32);
366        // 16 + long + 4 * 4 + 3 * 2 (+ 2) + 4 + long + 2 * 4 + 3 * 2 (+ 2).
367        let expected = if size_of::<usize>() == 8 { 80 } else { 68 };
368        assert_eq!(size_of::<fb_fix_screeninfo>(), expected);
369        assert_eq!(size_of::<input_absinfo>(), 24);
370    }
371
372    #[test]
373    fn the_fields_are_where_the_kernel_has_them() {
374        assert_eq!(offset_of!(fb_var_screeninfo, bits_per_pixel), 24);
375        assert_eq!(offset_of!(fb_var_screeninfo, red), 32);
376        assert_eq!(offset_of!(fb_var_screeninfo, transp), 68);
377        assert_eq!(offset_of!(fb_var_screeninfo, nonstd), 80);
378        assert_eq!(offset_of!(fb_var_screeninfo, reserved), 136);
379        if size_of::<usize>() == 8 {
380            assert_eq!(offset_of!(fb_fix_screeninfo, smem_start), 16);
381            assert_eq!(offset_of!(fb_fix_screeninfo, smem_len), 24);
382            assert_eq!(offset_of!(fb_fix_screeninfo, line_length), 48);
383            assert_eq!(offset_of!(fb_fix_screeninfo, mmio_start), 56);
384            assert_eq!(offset_of!(fb_fix_screeninfo, accel), 68);
385        }
386    }
387
388    #[test]
389    fn the_request_numbers_are_the_encoded_ones() {
390        // _IOW('F', 0x20, __u32) and _IOR('F', 0x12, struct fb_vblank).
391        assert_eq!(ioc(IOC_WRITE, b'F', 0x20, 4), fb_io_ctl::FBIO_WAITFORVSYNC);
392        assert_eq!(ioc(IOC_READ, b'F', 0x12, size_of::<fb_vblank>()), fb_io_ctl::FBIOGET_VBLANK);
393        assert_eq!(ioc(0, b'F', 0x00, 0), fb_io_ctl::FBIOGET_VSCREENINFO);
394    }
395
396    #[cfg(target_os = "linux")]
397    #[test]
398    fn the_absolute_axis_information_is_that_of_the_c_library() {
399        assert_eq!(size_of::<input_absinfo>(), size_of::<libc::input_absinfo>());
400    }
401
402    #[test]
403    fn the_timestamp_is_in_milliseconds() {
404        let ev = input_event { tv_sec: 12, tv_usec: 345_678, type_: 0, code: 0, value: 0 };
405        assert_eq!(ev.timestamp(), 12_345);
406    }
407
408    #[test]
409    fn the_identification_ends_at_its_terminator() {
410        let mut fix = fb_fix_screeninfo::default();
411        fix.id[..10].copy_from_slice(b"Virtual FB");
412        assert_eq!(fix.id_string(), "Virtual FB");
413        fix.id = *b"0123456789abcdef";
414        assert_eq!(fix.id_string(), "0123456789abcdef");
415    }
416}