From f5d307e5bc3799f65a5e850bb5c39ecbd6bb7169 Mon Sep 17 00:00:00 2001 From: Frank Villaro-Dixon Date: Sun, 5 May 2024 23:35:20 +0200 Subject: [PATCH] WIP Signed-off-by: Frank Villaro-Dixon --- Cargo.lock | 25 +++++++ Cargo.toml | 4 +- src/helpers/mod.rs | 38 ++--------- src/hid.rs | 160 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 64 ++++++++++-------- src/roundy_math.rs | 15 ++--- 6 files changed, 231 insertions(+), 75 deletions(-) create mode 100644 src/hid.rs diff --git a/Cargo.lock b/Cargo.lock index d8ead69..eb28c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,12 +71,14 @@ name = "beonew-5" version = "0.1.0" dependencies = [ "evdev", + "evdev-rs", "femtovg", "gilrs", "glutin", "glutin-winit", "image", "instant", + "nix 0.28.0", "raw-window-handle", "resource", "winit", @@ -284,6 +286,29 @@ dependencies = [ "thiserror", ] +[[package]] +name = "evdev-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9812d5790fb6fcce449333eb6713dad335e8c979225ed98755c84a3987e06dba" +dependencies = [ + "bitflags 1.3.2", + "evdev-sys", + "libc", + "log", +] + +[[package]] +name = "evdev-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ead42b547b15d47089c1243d907bcf0eb94e457046d3b315a26ac9c9e9ea6d" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "fdeflate" version = "0.3.4" diff --git a/Cargo.toml b/Cargo.toml index 69c093c..3c136a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,6 @@ resource = "0.5.0" image = { version = "0.24.0", default-features = false, features = [ "jpeg", "png", -] } \ No newline at end of file +] } +evdev-rs = "0.6.1" +nix = { version = "0.28.0", features = ["fs"] } diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs index 764a95e..2bbe0ed 100644 --- a/src/helpers/mod.rs +++ b/src/helpers/mod.rs @@ -22,10 +22,9 @@ mod perf_graph; pub use perf_graph::PerfGraph; pub fn start( - #[cfg(not(target_arch = "wasm32"))] width: u32, - #[cfg(not(target_arch = "wasm32"))] height: u32, - #[cfg(not(target_arch = "wasm32"))] title: &'static str, - #[cfg(not(target_arch = "wasm32"))] resizeable: bool, + width: u32, + height: u32, + title: &'static str, ) { // This provides better error messages in debug mode. // It's disabled in release mode so it doesn't bloat up the file size. @@ -34,11 +33,10 @@ pub fn start( let event_loop = EventLoop::new(); - #[cfg(not(target_arch = "wasm32"))] let (canvas, window, context, surface) = { let window_builder = WindowBuilder::new() .with_inner_size(winit::dpi::PhysicalSize::new(width, height)) - .with_resizable(resizeable) + .with_resizable(false) .with_title(title); let template = ConfigTemplateBuilder::new().with_alpha_size(8); @@ -116,39 +114,11 @@ pub fn start( (canvas, window, gl_context, surface) }; - #[cfg(target_arch = "wasm32")] - let (canvas, window) = { - use wasm_bindgen::JsCast; - - let canvas = web_sys::window() - .unwrap() - .document() - .unwrap() - .get_element_by_id("canvas") - .unwrap() - .dyn_into::() - .unwrap(); - - use winit::platform::web::WindowBuilderExtWebSys; - - let renderer = OpenGl::new_from_html_canvas(&canvas).expect("Cannot create renderer"); - - let window = WindowBuilder::new() - .with_canvas(Some(canvas)) - .build(&event_loop) - .unwrap(); - - let canvas = Canvas::new(renderer).expect("Cannot create canvas"); - - (canvas, window) - }; run( canvas, event_loop, - #[cfg(not(target_arch = "wasm32"))] context, - #[cfg(not(target_arch = "wasm32"))] surface, window, ); diff --git a/src/hid.rs b/src/hid.rs new file mode 100644 index 0000000..e7230e1 --- /dev/null +++ b/src/hid.rs @@ -0,0 +1,160 @@ +use core::panic; +use gilrs::ev; +use nix::{ + fcntl::{FcntlArg, OFlag}, + sys::epoll, +}; +use std::{ + io, + os::fd::{AsRawFd, FromRawFd, OwnedFd}, +}; + +use evdev; + +#[derive(Debug)] +pub enum Beo5Event { + LaserPosition(f32), + SelectionWheelRel(i32), + VolumeWheelRel(i32), + RightButtonPressed, + RightButtonReleased, + LeftButtonPressed, + LeftButtonReleased, + GoButtonPressed, + GoButtonReleased, + PowerButtonPressed, + PowerButtonReleased, +} + +pub struct Beo5Device { + device: evdev::Device, +} + +pub struct Beo5DeviceEvents<'a> { + events: Option>, +} + +impl Beo5Device { + pub fn new_autodiscover() -> Option { + let devices = evdev::enumerate().map(|t| t.1).collect::>(); + for (i, d) in devices.iter().enumerate() { + if d.name().unwrap_or("Unnamed device").contains("BeoSound 5") { + println!( + "Found BeoSound 5 device at index {}: {}", + i, + d.name().unwrap() + ); + // XXX Is there a better way than this into_iter.nth.unwrap ? + let mut d = devices.into_iter().nth(i).unwrap(); + + // From evdev's example + let raw_fd = d.as_raw_fd(); + // Set nonblocking + nix::fcntl::fcntl(raw_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)); //("Couldn't set nonblocking"); + + // Create epoll handle and attach raw_fd + //let epoll_fd = epoll::epoll_create1(epoll::EpollCreateFlags::EPOLL_CLOEXEC)?; + let epoll_fd = epoll::epoll_create1(epoll::EpollCreateFlags::EPOLL_CLOEXEC).ok()?; + let epoll_fd = unsafe { OwnedFd::from_raw_fd(epoll_fd) }; + let mut event = epoll::EpollEvent::new(epoll::EpollFlags::EPOLLIN, 0); + epoll::epoll_ctl( + epoll_fd.as_raw_fd(), + epoll::EpollOp::EpollCtlAdd, + raw_fd, + Some(&mut event), + ) + .unwrap(); + + //let events = d.fetch_events().unwrap(); + + return Some(Beo5Device { device: d }); + } + } + println!("Couldn't find a Beosound 5 device! Is the Beosound 5 kernel module loaded + device connected?"); + None + } + + pub fn fetch_events(&self) -> io::Result { + let events = &mut self.device.fetch_events()?; + Ok(Beo5DeviceEvents { + events: Some(events), + }) + } + + pub fn get_event_nonblocking(&mut self) -> Option { + if self.events.is_none() { + self.events = Some(self.device.fetch_events().unwrap()); + } + + for ev in self.events.unwrap().into_iter() { + println!("Beosound event: {ev:?}"); + // XXX What happens with the other events in the iterator? + match ev.kind() { + // Look at the beosound 5 kernel module for the correct event codes + evdev::InputEventKind::RelAxis(axis) => { + match axis { + evdev::RelativeAxisType::REL_Y => { + return Some(Beo5Event::SelectionWheelRel(ev.value())); + } + evdev::RelativeAxisType::REL_Z => { + return Some(Beo5Event::VolumeWheelRel(ev.value())); + } + // Shouldn't be any more relative axe + _ => {} + } + } + evdev::InputEventKind::AbsAxis(axis) => { + match axis { + evdev::AbsoluteAxisType::ABS_X => { + let pos_pct = ev.value() as f32 / 255.0; + return Some(Beo5Event::LaserPosition(pos_pct)); + } + // Shouldn't be any other absolute axe + _ => {} + } + } + evdev::InputEventKind::Switch(switch) => { + /* + match switch { + evdev::SwitchType::LE=> { + if ev.value() == 1 { + return Beo5Event::RightButtonPressed; + } else { + return Beo5Event::RightButtonReleased; + } + } + evdev::Switch::SW_LEFT => { + if ev.value() == 1 { + return Beo5Event::LeftButtonPressed; + } else { + return Beo5Event::LeftButtonReleased; + } + } + evdev::Switch::SW_GO => { + if ev.value() == 1 { + return Beo5Event::GoButtonPressed; + } else { + return Beo5Event::GoButtonReleased; + } + } + evdev::Switch::SW_POWER => { + if ev.value() == 1 { + return Beo5Event::PowerButtonPressed; + } else { + return Beo5Event::PowerButtonReleased; + } + } + // Shouldn't be any other switches + _ => { + panic!("Unknown switch event: {ev:?}") + } + } + */ + } + _ => {} //SYN et al + } + return None; + } + return None; + } +} diff --git a/src/main.rs b/src/main.rs index 89bb355..577cb6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -use core::panic; - use femtovg::{ renderer::OpenGl, Align, Baseline, Canvas, Color, FontId, ImageFlags, ImageId, Paint, Path, Renderer, @@ -21,14 +19,20 @@ struct Fonts { } fn main() { - helpers::start(1024, 768, "Text demo", false); + helpers::start(1024, 768, "Text demo"); } use glutin::prelude::*; +mod hid; mod roundy_math; mod ui; +enum UiEvent<'e> { + WinitEvent(winit::event::Event<'e, ()>), + HardwareEvent(evdev::InputEvent), +} + fn run( mut canvas: Canvas, el: EventLoop<()>, @@ -50,11 +54,6 @@ fn run( println!("{}", app.name()); } - let flags = ImageFlags::GENERATE_MIPMAPS | ImageFlags::REPEAT_X | ImageFlags::REPEAT_Y; - let image_id = canvas - .load_image_mem(&resource!("assets/pattern.jpg"), flags) - .expect("Cannot create image"); - let start = Instant::now(); let mut prevt = start; @@ -65,12 +64,26 @@ fn run( #[cfg(feature = "debug_inspector")] let mut font_texture_to_show: Option = None; - let mut x = 5.0; - let mut y = 380.0; + let mut beo_device = hid::Beo5Device::new_autodiscover().expect("Couldn't find Beo5 device"); + loop { + if let Some(ev) = beo_device.get_event_nonblocking() { + println!("HW Event: {:?}", ev); + } else { + break; + } + } el.run(move |event, _, control_flow| { *control_flow = ControlFlow::Poll; + let hw_event = beo_device.get_event_nonblocking(); + match hw_event { + Some(ev) => { + println!("HW Event: {:?}", ev); + } + None => {} + } + match event { Event::LoopDestroyed => *control_flow = ControlFlow::Exit, Event::WindowEvent { ref event, .. } => match event { @@ -91,22 +104,13 @@ fn run( }, .. } => { - println!("INput"); - if *keycode == VirtualKeyCode::W { - y -= 0.1; - } - - if *keycode == VirtualKeyCode::S { - y += 0.1; - } - - if *keycode == VirtualKeyCode::A { - x -= 0.1; - } - - if *keycode == VirtualKeyCode::D { - x += 0.1; + if keycode == &VirtualKeyCode::Escape { + *control_flow = ControlFlow::Exit; } + println!("INput {:?}", keycode); + //if *keycode == VirtualKeyCode::W { + // y -= 0.1; + //} if *keycode == VirtualKeyCode::NumpadAdd || *keycode == VirtualKeyCode::B { println!("Add"); @@ -136,10 +140,13 @@ fn run( delta: winit::event::MouseScrollDelta::LineDelta(_, y), .. } => { + println!("Scroll {:?}", y); font_size += *y / 2.0; font_size = font_size.max(2.0); } - _ => (), + _ => { + println!("{:?}", event); + } }, Event::RedrawRequested(_) => { let dpi_factor = window.scale_factor(); @@ -186,6 +193,7 @@ fn run( ); */ + // XXX Why the save/reset/restore ? canvas.save(); canvas.reset(); perf.render(&mut canvas, 500.0, 5.0); @@ -225,8 +233,6 @@ fn draw_main_menu(canvas: &mut Canvas, fonts: &Fonts, beo: &ui:: radius: 400.0, }; - println!("Canvas width: {}, height: {}", canvas_width, canvas_height); - let canvas_size = roundy_math::Point { x: canvas_width as f64, y: canvas_height as f64, @@ -234,7 +240,7 @@ fn draw_main_menu(canvas: &mut Canvas, fonts: &Fonts, beo: &ui:: let pts = main_menu_circle.get_equidistant_points(8, canvas_size); for i in 0..pts.len() { - println!(">>> {:?}", pts[i]); + //println!(">>> {:?}", pts[i]); let mut paint = Paint::color(Color::hex("B7410E")); paint.set_font(&[fonts.bold]); paint.set_text_baseline(Baseline::Top); diff --git a/src/roundy_math.rs b/src/roundy_math.rs index c821d45..8e68c9d 100644 --- a/src/roundy_math.rs +++ b/src/roundy_math.rs @@ -1,5 +1,3 @@ -use std::f64::consts::PI; -//use std::f64; #[derive(Debug)] pub struct Point { @@ -19,10 +17,6 @@ impl VirtualCircle { // because I'm not clever enough to figure out how to calculate the intersection points // in the general case. - println!( - "center: {:?}, radius: {}, viewport_size: {:?}", - self.center, self.radius, viewport_size - ); // Calculate the angles at which the circle intersects with the viewport let upper_x = self.center.x - (self.radius.powf(2.) - (0. - self.center.y).powf(2.)).sqrt(); let lower_x = self.center.x @@ -35,27 +29,26 @@ impl VirtualCircle { y: viewport_size.y, }; - println!("up/low: {:?} {:?}", upper_point, lower_point); + //println!("up/low: {:?} {:?}", upper_point, lower_point); let intersection_angles = ( (upper_point.x - self.center.x).atan2(upper_point.y - self.center.y), (lower_point.x - self.center.x).atan2(lower_point.y - self.center.y), ); - println!("int angles: {:?}", intersection_angles); + //println!("int angles: {:?}", intersection_angles); let mut points = Vec::new(); // Calculate the angle step to distribute points evenly let angle_step = (intersection_angles.1 - intersection_angles.0) / (n as f64); - println!("angle step: {}", angle_step); + //println!("angle step: {}", angle_step); // Generate the points for i in 0..n { let theta = intersection_angles.0 + angle_step * (i as f64 + 0.5); let x = theta.sin() * self.radius + viewport_size.x; let y = theta.cos() * self.radius + viewport_size.y - self.center.y; - println!("x: {}, y: {}", x, y); - points.push(Point { x: x, y: y }); + points.push(Point { x, y }); } points