Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
This commit is contained in:
Frank Villaro-Dixon 2024-05-05 23:35:20 +02:00
parent 6b3a2cfbfc
commit f5d307e5bc
6 changed files with 231 additions and 75 deletions

25
Cargo.lock generated
View file

@ -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"

View file

@ -18,3 +18,5 @@ image = { version = "0.24.0", default-features = false, features = [
"jpeg",
"png",
] }
evdev-rs = "0.6.1"
nix = { version = "0.28.0", features = ["fs"] }

View file

@ -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::<web_sys::HtmlCanvasElement>()
.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,
);

160
src/hid.rs Normal file
View file

@ -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<evdev::FetchEventsSynced<'a>>,
}
impl Beo5Device {
pub fn new_autodiscover() -> Option<Beo5Device> {
let devices = evdev::enumerate().map(|t| t.1).collect::<Vec<_>>();
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<Beo5DeviceEvents> {
let events = &mut self.device.fetch_events()?;
Ok(Beo5DeviceEvents {
events: Some(events),
})
}
pub fn get_event_nonblocking(&mut self) -> Option<Beo5Event> {
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;
}
}

View file

@ -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<OpenGl>,
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<usize> = 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<T: Renderer>(canvas: &mut Canvas<T>, 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<T: Renderer>(canvas: &mut Canvas<T>, 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);

View file

@ -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