diff --git a/linux/src/input.rs b/linux/src/input.rs new file mode 100644 index 0000000..1e17910 --- /dev/null +++ b/linux/src/input.rs @@ -0,0 +1,108 @@ +use std::os::fd::RawFd; +use std::mem::size_of; +use std::ffi::CString; + +use libc::{ open, close, read, poll, pollfd, input_event, timeval, __u16, __s32, c_void }; + +//https://stackoverflow.com/questions/15949163/read-from-dev-input#15949311 +//https://www.man7.org/linux/man-pages/man2/poll.2.html + +#[allow(non_camel_case_types)] +#[repr(u16)] +#[derive(Clone, Copy, PartialEq)] +pub enum EventType { + EV_SYN = 0, //event sep + EV_KEY, + EV_REL, + EV_ABS, + //nothing below will probably ever be relevant to ming-wm + EV_MSC, //misc + EV_SW, //switch/toggle + EV_LED, + EV_SND, + EV_REP, + EV_FF, + EV_PWR, + EV_FF_STATUS, + Unknown(__u16), +} + +impl TryFrom<__u16> for EventType { + type Error = (); + + fn try_from(value: __u16) -> Result { + //if the list is any longer should probably somehow make this a macro + let values = [Self::EV_SYN, Self::EV_KEY, Self::EV_REL, Self::EV_ABS, Self::EV_MSC, Self::EV_SW, Self::EV_LED, Self::EV_SND, Self::EV_REP, Self::EV_FF, Self::EV_PWR, Self::EV_FF_STATUS]; + let value = value as usize; + if value >= values.len() { + Err(()) + } else { + Ok(values[value]) + } + } +} + +//we do not care about time. no one cares about time (probably) +pub struct InputEvent { + pub type_: EventType, + pub code: __u16, //depends on EventType + pub value: __s32, +} + +pub struct Input(RawFd); + +impl Input { + pub fn new(input_name: &str) -> Result { + let input_name = CString::new(input_name).unwrap(); + let fd = unsafe { open(input_name.as_ptr(), libc::O_RDONLY | libc::O_NONBLOCK) }; + if fd == -1 { + Err(()) + } else { + Ok(Self(fd)) + } + } +} + +impl Iterator for Input { + type Item = InputEvent; + + fn next(&mut self) -> Option { + //wait until there is something available + let mut fds = vec![pollfd { + fd: self.0, + events: libc::POLLIN, //return when "there is data to read" + revents: 0, + }]; + let poll_result = unsafe { poll(fds.as_mut_ptr(), 1, -1) }; //neg num means no timeout + if poll_result == -1 { + return None; + } + //now read the event + let ie_size = size_of::(); + let mut ie = input_event { + time: timeval { + tv_sec: 0, + tv_usec: 0, + }, + type_: 0, + code: 0, + value: 0, + }; + let read_result = unsafe { read(self.0, &mut ie as *mut _ as *mut c_void, ie_size) }; + if read_result == -1 { + return None; + } + let type_ = ie.type_.try_into().unwrap_or(EventType::Unknown(ie.type_)); + Some(Self::Item { + type_, + code: ie.code, + value: ie.value, + }) + } +} + +impl Drop for Input { + fn drop(&mut self) { + unsafe { close(self.0) }; + } +} diff --git a/linux/src/lib.rs b/linux/src/lib.rs index 45ae7c0..2f4b4b7 100644 --- a/linux/src/lib.rs +++ b/linux/src/lib.rs @@ -2,3 +2,4 @@ pub mod fb; pub mod raw; pub mod keys; pub mod pty; +pub mod input; diff --git a/src/bin/wm.rs b/src/bin/wm.rs index e097794..8b57f92 100644 --- a/src/bin/wm.rs +++ b/src/bin/wm.rs @@ -1,14 +1,14 @@ -use std::process::{ Command, Stdio }; use std::sync::mpsc; use std::thread; use std::time::Duration; -use std::io::{ stdin, stdout, BufReader, BufRead, Write }; +use std::io::{ stdin, stdout, Write }; use std::process::exit; use std::env; use linux::fb::Framebuffer; use linux::raw::RawStdout; use linux::keys::{ RawStdin, Key }; +use linux::input::{ Input, EventType }; use wm::framebuffer::{ FramebufferWriter, FramebufferInfo }; use wm::window_manager::WindowManager; @@ -112,35 +112,35 @@ fn init(framebuffer: Framebuffer, framebuffer_info: FramebufferInfo) { thread::spawn(move || { //spawn evtest, parse it for touch coords if touch { - let mut evtest = Command::new("evtest").arg("/dev/input/by-path/first-touchscreen").stdout(Stdio::piped()).spawn().unwrap(); - let reader = BufReader::new(evtest.stdout.as_mut().unwrap()); + let mut events = Input::new("/dev/input/by-path/first-touchscreen").unwrap(); //panics in threads don't matter in this case let mut x: Option = None; let mut y: Option = None; - for line in reader.lines() { - let line = line.unwrap(); - println!(" "); //without any stdout, on my phone, for some reason the framebuffer doesn't get redrawn to the screen - if line.contains("ABS_X), value ") || line.contains("ABS_Y), value ") { - let value: Vec<_> = line.split("), value ").collect(); - let value = value[value.len() - 1].parse::().unwrap(); - if line.contains("ABS_X") { - x = Some(value); - } else { - y = Some(value); - } - if x.is_some() && y.is_some() { - let (x2, y2) = if rotate { - (dimensions[0] - y.unwrap(), x.unwrap()) + loop { + let event = events.next(); + if let Some(event) = event { + //ABS_X = 0, ABS_Y = 1 + if event.type_ == EventType::EV_ABS && (event.code == 0 || event.code == 1) { + if event.code == 0 { + x = Some(event.value as usize); //event.value is u16 so this should be fine. unless usize is u8, lmao } else { - (x.unwrap(), y.unwrap()) - }; - //top right, clear - //useful sometimes, I think. - if x2 > dimensions[0] - 100 && y2 < 100 { - tx1.send(ThreadMessage::Clear).unwrap(); + y = Some(event.value as usize); + } + if x.is_some() && y.is_some() { + let (x2, y2) = if rotate { + (dimensions[0] - y.unwrap(), x.unwrap()) + } else { + (x.unwrap(), y.unwrap()) + }; + //top right, clear + //useful sometimes, I think. + if x2 > dimensions[0] - 100 && y2 < 100 { + tx1.send(ThreadMessage::Clear).unwrap(); + } + println!(" "); //without any stdout, on my phone, for some reason the framebuffer doesn't get redrawn to the screen + tx1.send(ThreadMessage::Touch(x2, y2)).unwrap(); + x = None; + y = None; } - tx1.send(ThreadMessage::Touch(x2, y2)).unwrap(); - x = None; - y = None; } } thread::sleep(Duration::from_millis(1));