Something went wrong. Try again.
Fork of plato: document reader for Kobo https://github.com/baskerville/plato
Something went wrong. Try again.
Rust
at screensaver
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511use std::mem::{self, MaybeUninit};use std::ptr;use std::slice;use std::thread;use std::io::Read;use std::fs::File;use std::sync::mpsc::{self, Sender, Receiver};use std::os::unix::io::AsRawFd;use std::ffi::CString;use fxhash::FxHashMap;use crate::framebuffer::Display;use crate::settings::ButtonScheme;use crate::device::CURRENT_DEVICE;use crate::geom::{Point, LinearDir};use anyhow::{Error, Context};
// Event typespub const EV_SYN: u16 = 0x00;pub const EV_KEY: u16 = 0x01;pub const EV_ABS: u16 = 0x03;pub const EV_MSC: u16 = 0x04;
// Event codespub const ABS_MT_TRACKING_ID: u16 = 0x39;pub const ABS_MT_POSITION_X: u16 = 0x35;pub const ABS_MT_POSITION_Y: u16 = 0x36;pub const ABS_MT_PRESSURE: u16 = 0x3a;pub const ABS_MT_TOUCH_MAJOR: u16 = 0x30;pub const ABS_X: u16 = 0x00;pub const ABS_Y: u16 = 0x01;pub const ABS_PRESSURE: u16 = 0x18;pub const MSC_RAW: u16 = 0x03;pub const SYN_REPORT: u16 = 0x00;
// Event valuespub const MSC_RAW_GSENSOR_PORTRAIT_DOWN: i32 = 0x17;pub const MSC_RAW_GSENSOR_PORTRAIT_UP: i32 = 0x18;pub const MSC_RAW_GSENSOR_LANDSCAPE_RIGHT: i32 = 0x19;pub const MSC_RAW_GSENSOR_LANDSCAPE_LEFT: i32 = 0x1a;// pub const MSC_RAW_GSENSOR_BACK: i32 = 0x1b;// pub const MSC_RAW_GSENSOR_FRONT: i32 = 0x1c;
// The indices of this clockwise ordering of the sensor values match the Forma's rotation values.pub const GYROSCOPE_ROTATIONS: [i32; 4] = [MSC_RAW_GSENSOR_LANDSCAPE_LEFT, MSC_RAW_GSENSOR_PORTRAIT_UP, MSC_RAW_GSENSOR_LANDSCAPE_RIGHT, MSC_RAW_GSENSOR_PORTRAIT_DOWN];
pub const VAL_RELEASE: i32 = 0;pub const VAL_PRESS: i32 = 1;pub const VAL_REPEAT: i32 = 2;
// Key codespub const KEY_POWER: u16 = 116;pub const KEY_HOME: u16 = 102;pub const KEY_LIGHT: u16 = 90;pub const KEY_BACKWARD: u16 = 193;pub const KEY_FORWARD: u16 = 194;pub const PEN_ERASE: u16 = 331;pub const PEN_HIGHLIGHT: u16 = 332;pub const SLEEP_COVER: [u16; 2] = [59, 35];// Synthetic touch buttonpub const BTN_TOUCH: u16 = 330;// The following key codes are fake, and are used to support// software toggles within this designpub const KEY_ROTATE_DISPLAY: u16 = 0xffff;pub const KEY_BUTTON_SCHEME: u16 = 0xfffe;
pub const SINGLE_TOUCH_CODES: TouchCodes = TouchCodes { pressure: ABS_PRESSURE, x: ABS_X, y: ABS_Y,};
pub const MULTI_TOUCH_CODES_A: TouchCodes = TouchCodes { pressure: ABS_MT_TOUCH_MAJOR, x: ABS_MT_POSITION_X, y: ABS_MT_POSITION_Y,};
pub const MULTI_TOUCH_CODES_B: TouchCodes = TouchCodes { pressure: ABS_MT_PRESSURE, .. MULTI_TOUCH_CODES_A};
#[repr(C)]pub struct InputEvent { pub time: libc::timeval, pub kind: u16, // type pub code: u16, pub value: i32,}
// Handle different touch protocols#[derive(Debug)]pub struct TouchCodes { pressure: u16, x: u16, y: u16,}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]pub enum TouchProto { Single, MultiA, MultiB, // Pressure won't indicate a finger release. MultiC,}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]pub enum FingerStatus { Down, Motion, Up,}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]pub enum ButtonStatus { Pressed, Released, Repeated,}
impl ButtonStatus { pub fn try_from_raw(value: i32) -> Option<ButtonStatus> { match value { VAL_RELEASE => Some(ButtonStatus::Released), VAL_PRESS => Some(ButtonStatus::Pressed), VAL_REPEAT => Some(ButtonStatus::Repeated), _ => None, } }}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]pub enum ButtonCode { Power, Home, Light, Backward, Forward, Erase, Highlight, Raw(u16),}
impl ButtonCode { fn from_raw(code: u16, rotation: i8, button_scheme: ButtonScheme) -> ButtonCode { match code { KEY_POWER => ButtonCode::Power, KEY_HOME => ButtonCode::Home, KEY_LIGHT => ButtonCode::Light, KEY_BACKWARD => resolve_button_direction(LinearDir::Backward, rotation, button_scheme), KEY_FORWARD => resolve_button_direction(LinearDir::Forward, rotation, button_scheme), PEN_ERASE => ButtonCode::Erase, PEN_HIGHLIGHT => ButtonCode::Highlight, _ => ButtonCode::Raw(code) } }}
fn resolve_button_direction(mut direction: LinearDir, rotation: i8, button_scheme: ButtonScheme) -> ButtonCode { if (CURRENT_DEVICE.should_invert_buttons(rotation)) ^ (button_scheme == ButtonScheme::Inverted) { direction = direction.opposite(); }
if direction == LinearDir::Forward { return ButtonCode::Forward; }
ButtonCode::Backward}
pub fn display_rotate_event(n: i8) -> InputEvent { let mut tp = libc::timeval { tv_sec: 0, tv_usec: 0 }; unsafe { libc::gettimeofday(&mut tp, ptr::null_mut()); } InputEvent { time: tp, kind: EV_KEY, code: KEY_ROTATE_DISPLAY, value: n as i32, }}
pub fn button_scheme_event(v: i32) -> InputEvent { let mut tp = libc::timeval { tv_sec: 0, tv_usec: 0 }; unsafe { libc::gettimeofday(&mut tp, ptr::null_mut()); } InputEvent { time: tp, kind: EV_KEY, code: KEY_BUTTON_SCHEME, value: v, }}
#[derive(Debug, Copy, Clone)]pub enum DeviceEvent { Finger { id: i32, time: f64, status: FingerStatus, position: Point, }, Button { time: f64, code: ButtonCode, status: ButtonStatus, }, Plug(PowerSource), Unplug(PowerSource), RotateScreen(i8), CoverOn, CoverOff, NetUp, UserActivity,}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]pub enum PowerSource { Host, Wall,}
pub fn seconds(time: libc::timeval) -> f64 { time.tv_sec as f64 + time.tv_usec as f64 / 1e6}
pub fn raw_events(paths: Vec<String>) -> (Sender<InputEvent>, Receiver<InputEvent>) { let (tx, rx) = mpsc::channel(); let tx2 = tx.clone(); thread::spawn(move || parse_raw_events(&paths, &tx)); (tx2, rx)}
pub fn parse_raw_events(paths: &[String], tx: &Sender<InputEvent>) -> Result<(), Error> { let mut files = Vec::new(); let mut pfds = Vec::new();
for path in paths.iter() { let file = File::open(path) .with_context(|| format!("can't open input file {}", path))?; let fd = file.as_raw_fd(); files.push(file); pfds.push(libc::pollfd { fd, events: libc::POLLIN, revents: 0, }); }
loop { let ret = unsafe { libc::poll(pfds.as_mut_ptr(), pfds.len() as libc::nfds_t, -1) }; if ret < 0 { break; } for (pfd, mut file) in pfds.iter().zip(&files) { if pfd.revents & libc::POLLIN != 0 { let mut input_event = MaybeUninit::<InputEvent>::uninit(); unsafe { let event_slice = slice::from_raw_parts_mut(input_event.as_mut_ptr() as *mut u8, mem::size_of::<InputEvent>()); if file.read_exact(event_slice).is_err() { break; } tx.send(input_event.assume_init()).ok(); } } } }
Ok(())}
pub fn usb_events() -> Receiver<DeviceEvent> { let (tx, rx) = mpsc::channel(); thread::spawn(move || parse_usb_events(&tx)); rx}
fn parse_usb_events(tx: &Sender<DeviceEvent>) { let path = CString::new("/tmp/nickel-hardware-status").unwrap(); let fd = unsafe { libc::open(path.as_ptr(), libc::O_NONBLOCK | libc::O_RDWR) };
if fd < 0 { return; }
let mut pfd = libc::pollfd { fd, events: libc::POLLIN, revents: 0, };
const BUF_LEN: usize = 256;
loop { let ret = unsafe { libc::poll(&mut pfd as *mut libc::pollfd, 1, -1) };
if ret < 0 { break; }
let buf = CString::new(vec![1; BUF_LEN]).unwrap(); let c_buf = buf.into_raw();
if pfd.revents & libc::POLLIN != 0 { let n = unsafe { libc::read(fd, c_buf as *mut libc::c_void, BUF_LEN as libc::size_t) }; let buf = unsafe { CString::from_raw(c_buf) }; if n > 0 { if let Ok(s) = buf.to_str() { for msg in s[..n as usize].lines() { if msg == "usb plug add" { tx.send(DeviceEvent::Plug(PowerSource::Host)).ok(); } else if msg == "usb plug remove" { tx.send(DeviceEvent::Unplug(PowerSource::Host)).ok(); } else if msg == "usb ac add" { tx.send(DeviceEvent::Plug(PowerSource::Wall)).ok(); } else if msg == "usb ac remove" { tx.send(DeviceEvent::Unplug(PowerSource::Wall)).ok(); } else if msg.starts_with("network bound") { tx.send(DeviceEvent::NetUp).ok(); } } } } else { break; } } }}
pub fn device_events(rx: Receiver<InputEvent>, display: Display, button_scheme: ButtonScheme) -> Receiver<DeviceEvent> { let (ty, ry) = mpsc::channel(); thread::spawn(move || parse_device_events(&rx, &ty, display, button_scheme)); ry}
struct TouchState { position: Point, pressure: i32,}
impl Default for TouchState { fn default() -> Self { TouchState { position: Point::default(), pressure: 0, } }}
pub fn parse_device_events(rx: &Receiver<InputEvent>, ty: &Sender<DeviceEvent>, display: Display, button_scheme: ButtonScheme) { let mut id = 0; let mut last_activity = -60; let Display { mut dims, mut rotation } = display; let mut fingers: FxHashMap<i32, Point> = FxHashMap::default(); let mut packets: FxHashMap<i32, TouchState> = FxHashMap::default(); let proto = CURRENT_DEVICE.proto;
let mut tc = match proto { TouchProto::Single => SINGLE_TOUCH_CODES, TouchProto::MultiA => MULTI_TOUCH_CODES_A, TouchProto::MultiB => MULTI_TOUCH_CODES_B, TouchProto::MultiC => MULTI_TOUCH_CODES_B, };
if proto == TouchProto::Single { packets.insert(id, TouchState::default()); }
let (mut mirror_x, mut mirror_y) = CURRENT_DEVICE.should_mirror_axes(rotation); if CURRENT_DEVICE.should_swap_axes(rotation) { mem::swap(&mut tc.x, &mut tc.y); }
let mut button_scheme = button_scheme;
while let Ok(evt) = rx.recv() { if evt.kind == EV_ABS { if evt.code == ABS_MT_TRACKING_ID { if evt.value >= 0 { id = evt.value; packets.insert(id, TouchState::default()); } } else if evt.code == tc.x { if let Some(state) = packets.get_mut(&id) { state.position.x = if mirror_x { dims.0 as i32 - 1 - evt.value } else { evt.value }; } } else if evt.code == tc.y { if let Some(state) = packets.get_mut(&id) { state.position.y = if mirror_y { dims.1 as i32 - 1 - evt.value } else { evt.value }; } } else if evt.code == tc.pressure { if let Some(state) = packets.get_mut(&id) { state.pressure = evt.value; if proto == TouchProto::Single && CURRENT_DEVICE.mark() == 3 && state.pressure == 0 { state.position.x = dims.0 as i32 - 1 - state.position.x; mem::swap(&mut state.position.x, &mut state.position.y); } } } } else if evt.kind == EV_SYN && evt.code == SYN_REPORT { // The absolute value accounts for the wrapping around that might occur, // since `tv_sec` can't grow forever. if (evt.time.tv_sec - last_activity).abs() >= 60 { last_activity = evt.time.tv_sec; ty.send(DeviceEvent::UserActivity).ok(); }
if proto == TouchProto::MultiB { fingers.retain(|other_id, other_position| { packets.contains_key(&other_id) || ty.send(DeviceEvent::Finger { id: *other_id, time: seconds(evt.time), status: FingerStatus::Up, position: *other_position, }).is_err() }); }
for (&id, state) in &packets { if let Some(&pos) = fingers.get(&id) { if state.pressure > 0 { if state.position != pos { ty.send(DeviceEvent::Finger { id, time: seconds(evt.time), status: FingerStatus::Motion, position: state.position, }).unwrap(); fingers.insert(id, state.position); } } else { ty.send(DeviceEvent::Finger { id, time: seconds(evt.time), status: FingerStatus::Up, position: state.position, }).unwrap(); fingers.remove(&id); } } else if state.pressure > 0 { ty.send(DeviceEvent::Finger { id, time: seconds(evt.time), status: FingerStatus::Down, position: state.position, }).unwrap(); fingers.insert(id, state.position); } }
if proto != TouchProto::Single { packets.clear(); } } else if evt.kind == EV_KEY { if SLEEP_COVER.contains(&evt.code) { if evt.value == VAL_PRESS { ty.send(DeviceEvent::CoverOn).ok(); } else if evt.value == VAL_RELEASE { ty.send(DeviceEvent::CoverOff).ok(); } else if evt.value == VAL_REPEAT { ty.send(DeviceEvent::CoverOn).ok(); } } else if evt.code == KEY_BUTTON_SCHEME { if evt.value == VAL_PRESS { button_scheme = ButtonScheme::Inverted; } else { button_scheme = ButtonScheme::Natural; } } else if evt.code == KEY_ROTATE_DISPLAY { let next_rotation = evt.value as i8; if next_rotation != rotation { let delta = (rotation - next_rotation).abs(); if delta % 2 == 1 { mem::swap(&mut tc.x, &mut tc.y); mem::swap(&mut dims.0, &mut dims.1); } rotation = next_rotation; let should_mirror = CURRENT_DEVICE.should_mirror_axes(rotation); mirror_x = should_mirror.0; mirror_y = should_mirror.1; } } else if evt.code != BTN_TOUCH { if let Some(button_status) = ButtonStatus::try_from_raw(evt.value) { ty.send(DeviceEvent::Button { time: seconds(evt.time), code: ButtonCode::from_raw(evt.code, rotation, button_scheme), status: button_status, }).unwrap(); } } } else if evt.kind == EV_MSC && evt.code == MSC_RAW { if evt.value >= MSC_RAW_GSENSOR_PORTRAIT_DOWN && evt.value <= MSC_RAW_GSENSOR_LANDSCAPE_LEFT { let next_rotation = GYROSCOPE_ROTATIONS.iter().position(|&v| v == evt.value) .map(|i| CURRENT_DEVICE.transformed_gyroscope_rotation(i as i8)); if let Some(next_rotation) = next_rotation { ty.send(DeviceEvent::RotateScreen(next_rotation)).ok(); } } } }}