1#![allow(non_camel_case_types)]
10
11pub 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
21pub mod v_blank_flags {
23 pub const FB_VBLANK_VBLANKING: u32 = 0x001;
25 pub const FB_VBLANK_HBLANKING: u32 = 0x002;
27 pub const FB_VBLANK_HAVE_VBLANK: u32 = 0x004;
29 pub const FB_VBLANK_HAVE_HBLANK: u32 = 0x008;
31 pub const FB_VBLANK_HAVE_COUNT: u32 = 0x010;
33 pub const FB_VBLANK_HAVE_VCOUNT: u32 = 0x020;
35 pub const FB_VBLANK_HAVE_HCOUNT: u32 = 0x040;
37 pub const FB_VBLANK_VSYNCING: u32 = 0x080;
39 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 pub flags: u32,
48 pub count: u32,
50 pub vcount: u32,
52 pub hcount: u32,
54 pub reserved: [u32; 4],
56}
57
58#[repr(C)]
59#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
60pub struct fb_fix_screeninfo {
61 pub id: [u8; 16],
63 pub smem_start: usize,
65 pub smem_len: u32,
67 pub type_: u32,
69 pub type_aux: u32,
71 pub visual: u32,
73 pub xpanstep: u16,
75 pub ypanstep: u16,
77 pub ywrapstep: u16,
79 pub line_length: u32,
81 pub mmio_start: usize,
83 pub mmio_len: u32,
85 pub accel: u32,
87 pub reserved: [u16; 3],
89}
90
91impl fb_fix_screeninfo {
92 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 pub offset: u32,
104 pub length: u32,
106 pub msb_right: u32,
108}
109
110#[repr(C)]
111#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
112pub struct fb_var_screeninfo {
113 pub xres: u32,
115 pub yres: u32,
116 pub xres_virtual: u32,
118 pub yres_virtual: u32,
119 pub xoffset: u32,
121 pub yoffset: u32,
122 pub bits_per_pixel: u32,
124 pub grayscale: u32,
126 pub red: fb_bitfield,
128 pub green: fb_bitfield,
129 pub blue: fb_bitfield,
130 pub transp: fb_bitfield,
132 pub nonstd: u32,
134 pub activate: u32,
136 pub height: u32,
138 pub width: u32,
140 pub accel_flags: u32,
142 pub pixclock: u32,
145 pub left_margin: u32,
147 pub right_margin: u32,
149 pub upper_margin: u32,
151 pub lower_margin: u32,
152 pub hsync_len: u32,
154 pub vsync_len: u32,
156 pub sync: u32,
158 pub vmode: u32,
160 pub reserved: [u32; 6],
162}
163
164pub 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 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
184pub mod syn_code {
187 pub const SYN_REPORT: u16 = 0;
188 pub const SYN_DROPPED: u16 = 3;
189}
190
191pub 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
206pub 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 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
239pub 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#[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 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
283pub 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#[cfg(target_os = "linux")]
296pub mod sys {
297 use std::ffi::CString;
298 use std::io;
299 use std::os::fd::RawFd;
300
301 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 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 unsafe {
315 libc::close(fd);
316 }
317 }
318
319 pub fn ioctl_struct<T: Copy>(fd: RawFd, request: u32, arg: &mut T) -> io::Result<i32> {
325 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 pub fn ioctl_buffer(fd: RawFd, request: u32, buffer: &mut [u8]) -> io::Result<i32> {
337 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 pub fn ioctl_null(fd: RawFd, request: u32) -> i32 {
349 unsafe { libc::ioctl(fd, request as _, std::ptr::null_mut::<libc::c_void>()) }
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 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 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 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}