Compare commits
4 commits
dd7fa0ecce
...
1e4b6f7d16
Author | SHA1 | Date | |
---|---|---|---|
1e4b6f7d16 | |||
5456c6845e | |||
f0885efd27 | |||
376ddf53ac |
4 changed files with 137 additions and 93 deletions
114
src/apps.rs
114
src/apps.rs
|
@ -11,11 +11,29 @@ impl AppBase {
|
|||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn enter_submenu(&mut self, submenu_id: usize) {
|
||||
let mut current_menu = &mut self.main_menu;
|
||||
while let Some(ref mut selected_submenu) = current_menu.selected_submenu {
|
||||
current_menu = selected_submenu
|
||||
}
|
||||
current_menu.set_submenu_id(submenu_id)
|
||||
}
|
||||
|
||||
pub fn exit_submenu(&mut self) {
|
||||
let current_menu = &mut self.main_menu;
|
||||
while let Some(ref mut selected_submenu) = current_menu.selected_submenu {
|
||||
if selected_submenu.selected_submenu.is_none() {
|
||||
current_menu.selected_submenu = None;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Menu {
|
||||
pub submenus: Vec<Box<Menu>>,
|
||||
pub submenus: Vec<Menu>,
|
||||
current_submenu_id: usize,
|
||||
pub selected_submenu: Option<Box<Menu>>,
|
||||
pub name: String,
|
||||
|
@ -23,25 +41,21 @@ pub struct Menu {
|
|||
}
|
||||
|
||||
impl Menu {
|
||||
pub fn go_left(&mut self) {
|
||||
if self.current_submenu_id > 0 {
|
||||
self.current_submenu_id -= 1;
|
||||
pub fn set_submenu_id(&mut self, id: usize) {
|
||||
if id >= self.submenus.len() {
|
||||
panic!("Invalid submenu id");
|
||||
}
|
||||
}
|
||||
pub fn go_right(&mut self) {
|
||||
if self.current_submenu_id < self.submenus.len() - 1 {
|
||||
self.current_submenu_id += 1;
|
||||
}
|
||||
}
|
||||
fn select_submenu(&mut self) {
|
||||
self.selected_submenu = Some(self.submenus[self.current_submenu_id].clone());
|
||||
self.current_submenu_id = id;
|
||||
self.selected_submenu = Some(Box::new(self.submenus[self.current_submenu_id].clone()));
|
||||
}
|
||||
|
||||
pub fn get_deepest_selected_submenu(&self) -> &Menu {
|
||||
match &self.selected_submenu {
|
||||
Some(selected_submenu) => selected_submenu.get_deepest_selected_submenu(),
|
||||
None => self,
|
||||
// Use a loop to traverse to the deepest submenu
|
||||
let mut current_menu = self;
|
||||
while let Some(ref selected_submenu) = current_menu.selected_submenu {
|
||||
current_menu = selected_submenu;
|
||||
}
|
||||
current_menu
|
||||
}
|
||||
}
|
||||
pub trait App {
|
||||
|
@ -57,48 +71,74 @@ struct Spotify {
|
|||
impl Spotify {
|
||||
fn new() -> Self {
|
||||
let spotify_menus = vec![
|
||||
Box::new(Menu {
|
||||
submenus: vec![],
|
||||
Menu {
|
||||
submenus: vec![
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Liked Songs".to_string(),
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Recently Played".to_string(),
|
||||
},
|
||||
],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Playlists".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
submenus: vec![],
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Mike Oldfield".to_string(),
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "JM. Jarre".to_string(),
|
||||
},
|
||||
],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Artists".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Albums".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Songs".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Genres".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "New Releases".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Charts".to_string(),
|
||||
}),
|
||||
},
|
||||
];
|
||||
Spotify {
|
||||
base: AppBase {
|
||||
|
@ -131,24 +171,24 @@ struct Radio {
|
|||
impl Radio {
|
||||
fn new() -> Self {
|
||||
let radio_menus = vec![
|
||||
Box::new(Menu {
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Favorites".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Local".to_string(),
|
||||
}),
|
||||
Box::new(Menu {
|
||||
},
|
||||
Menu {
|
||||
submenus: vec![],
|
||||
current_submenu_id: 0,
|
||||
selected_submenu: None,
|
||||
name: "Global".to_string(),
|
||||
}),
|
||||
},
|
||||
];
|
||||
Radio {
|
||||
base: AppBase {
|
||||
|
|
16
src/hid.rs
16
src/hid.rs
|
@ -9,6 +9,7 @@ use std::{
|
|||
|
||||
const LASER_POINTER_MAX: i32 = 121;
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub enum Beo5Event {
|
||||
LaserPosition(f32),
|
||||
SelectionWheelRel(i32),
|
||||
|
@ -49,10 +50,12 @@ impl Beo5Device {
|
|||
nix::fcntl::fcntl(raw_fd, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)).unwrap();
|
||||
|
||||
// Create epoll handle and attach raw_fd
|
||||
#[allow(deprecated)]
|
||||
let epoll_fd = epoll::epoll_create1(epoll::EpollCreateFlags::EPOLL_CLOEXEC).ok()?;
|
||||
println!("epoll_fd: {epoll_fd}.");
|
||||
let epoll_fd = unsafe { OwnedFd::from_raw_fd(epoll_fd) };
|
||||
let mut event = epoll::EpollEvent::new(epoll::EpollFlags::EPOLLIN, 0);
|
||||
#[allow(deprecated)]
|
||||
epoll::epoll_ctl(
|
||||
epoll_fd.as_raw_fd(),
|
||||
epoll::EpollOp::EpollCtlAdd,
|
||||
|
@ -61,8 +64,6 @@ impl Beo5Device {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
//loop {}
|
||||
|
||||
return Some(Beo5Device {
|
||||
device: d,
|
||||
evts_in_queue: VecDeque::new(),
|
||||
|
@ -91,7 +92,8 @@ impl Beo5Device {
|
|||
if x.kind() == std::io::ErrorKind::WouldBlock {
|
||||
// Wait forever for bytes available on raw_fd
|
||||
let mut events = [epoll::EpollEvent::empty(); 2];
|
||||
epoll::epoll_wait(8, &mut events, -1);
|
||||
#[allow(deprecated)]
|
||||
let _ = epoll::epoll_wait(8, &mut events, -1);
|
||||
} else {
|
||||
println!("Error getting event: {x} - {}", x.kind());
|
||||
//panic!("Error getting event: {x}");
|
||||
|
@ -99,15 +101,14 @@ impl Beo5Device {
|
|||
}
|
||||
}
|
||||
let ev = self.evts_in_queue.pop_front();
|
||||
if ev.is_some() {
|
||||
return Beo5Device::parse_event(ev.unwrap());
|
||||
if let Some(e) = ev {
|
||||
return Beo5Device::parse_event(e);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_event(ev: evdev::InputEvent) -> Option<Beo5Event> {
|
||||
println!("Beosound event: {ev:?}");
|
||||
// XXX What happens with the other events in the iterator?
|
||||
#[allow(clippy::collapsible_match)]
|
||||
match ev.kind() {
|
||||
// Look at the beosound 5 kernel module for the correct event codes
|
||||
evdev::InputEventKind::RelAxis(axis) => {
|
||||
|
@ -123,6 +124,7 @@ impl Beo5Device {
|
|||
}
|
||||
}
|
||||
evdev::InputEventKind::AbsAxis(axis) => {
|
||||
#[allow(clippy::single_match)]
|
||||
match axis {
|
||||
evdev::AbsoluteAxisType::ABS_X => {
|
||||
let pos_pct = (ev.value() as f32 / LASER_POINTER_MAX as f32).min(1.0);
|
||||
|
|
30
src/main.rs
30
src/main.rs
|
@ -13,7 +13,14 @@ use winit::{
|
|||
mod helpers;
|
||||
use helpers::PerfGraph;
|
||||
|
||||
// XXX rename to smth else
|
||||
use glutin::prelude::*;
|
||||
|
||||
mod apps;
|
||||
mod hid;
|
||||
mod roundy_math;
|
||||
mod ui;
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct Fonts {
|
||||
sans: FontId,
|
||||
bold: FontId,
|
||||
|
@ -26,19 +33,6 @@ struct Fonts {
|
|||
fn main() {
|
||||
helpers::start(1024, 768, "Text demo");
|
||||
}
|
||||
|
||||
use glutin::prelude::*;
|
||||
|
||||
mod apps;
|
||||
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<()>,
|
||||
|
@ -112,11 +106,8 @@ fn run(
|
|||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
let hw_event = beo_device.get_event_nonblocking();
|
||||
match hw_event {
|
||||
Some(ev) => {
|
||||
beo.accept_event(ev);
|
||||
}
|
||||
None => {}
|
||||
if let Some(ev) = hw_event {
|
||||
beo.accept_event(ev);
|
||||
}
|
||||
|
||||
match event {
|
||||
|
@ -191,7 +182,6 @@ fn run(
|
|||
canvas.set_size(size.width, size.height, dpi_factor as f32);
|
||||
canvas.clear_rect(0, 0, size.width, size.height, Color::rgbf(0., 0., 0.));
|
||||
|
||||
let elapsed = start.elapsed().as_secs_f32();
|
||||
let now = Instant::now();
|
||||
let dt = (now - prevt).as_secs_f32();
|
||||
prevt = now;
|
||||
|
|
70
src/ui.rs
70
src/ui.rs
|
@ -1,4 +1,4 @@
|
|||
use femtovg::{Align, Baseline, Canvas, Color, Paint, Path, Renderer};
|
||||
use femtovg::{Baseline, Canvas, Color, Paint, Path, Renderer};
|
||||
|
||||
use crate::{hid::Beo5Event, Fonts};
|
||||
|
||||
|
@ -85,36 +85,31 @@ impl BeoUi {
|
|||
if let Some(selected_app) = self.current_app() {
|
||||
let selected_menu = selected_app.base().main_menu.get_deepest_selected_submenu();
|
||||
let menu_count = selected_menu.submenus.len();
|
||||
println!("helol menu_count: {}", menu_count);
|
||||
let max_angle = (menu_count - 1) as f32 * ANGLE_DEG_BETWEEN_MENU_ITEMS + 10.;
|
||||
println!("max_angle: {}", max_angle);
|
||||
|
||||
if self.angle_shift > max_angle {
|
||||
self.angle_shift = max_angle;
|
||||
}
|
||||
}
|
||||
|
||||
self.choose_app_wheel_angle();
|
||||
}
|
||||
|
||||
fn choose_app_wheel_angle(&mut self) {
|
||||
pub fn get_selected_wheel_angle_menu_id(&self) -> Option<usize> {
|
||||
println!("choose_app_wheel_angle");
|
||||
let angle = self.angle_shift;
|
||||
if let Some(selected_app) = self.current_app() {
|
||||
let selected_menu = selected_app.base().main_menu.get_deepest_selected_submenu();
|
||||
let menu_count = selected_menu.submenus.len();
|
||||
let max_angle = (menu_count - 1) as f32 * -ANGLE_DEG_BETWEEN_MENU_ITEMS - 10.;
|
||||
let actual_menu = selected_app.base().main_menu.get_deepest_selected_submenu();
|
||||
let menu_count = actual_menu.submenus.len();
|
||||
let max_angle = (menu_count - 1) as f32 * ANGLE_DEG_BETWEEN_MENU_ITEMS - 10.;
|
||||
|
||||
let angle = self.angle_shift;
|
||||
println!("Angle before: {}", angle);
|
||||
let angle = angle.max(max_angle);
|
||||
let angle = angle.min(0.);
|
||||
let angle = angle.min(max_angle);
|
||||
let angle = angle.max(0.);
|
||||
|
||||
println!("Angle: {}", angle);
|
||||
let menu_id_unrounded = (angle - max_angle) / ANGLE_DEG_BETWEEN_MENU_ITEMS;
|
||||
println!("Menu id unrounded: {}", menu_id_unrounded);
|
||||
let menu_id_unrounded = angle / ANGLE_DEG_BETWEEN_MENU_ITEMS;
|
||||
println!("Menu id unrounded bis : {}", menu_id_unrounded);
|
||||
let menu_id = (angle / ANGLE_DEG_BETWEEN_MENU_ITEMS).round() as usize;
|
||||
println!("Menu id: {}", menu_id);
|
||||
//selected_app.base_mut().main_menu.selected_id = menu_id;
|
||||
return Some(menu_id);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn accept_event(&mut self, event: Beo5Event) {
|
||||
|
@ -124,16 +119,25 @@ impl BeoUi {
|
|||
self.choose_app_from_laser_pct();
|
||||
}
|
||||
Beo5Event::LeftButtonPressed => {
|
||||
println!("Left button pressed");
|
||||
if let Some(selected_app) = self.current_app_mut() {
|
||||
selected_app.base_mut().main_menu.go_left();
|
||||
println!("Left button pressed. Will select submenu");
|
||||
let submenu_id = self.get_selected_wheel_angle_menu_id();
|
||||
match submenu_id {
|
||||
Some(submenu_id) => {
|
||||
self.current_app_mut()
|
||||
.unwrap()
|
||||
.base_mut()
|
||||
.enter_submenu(submenu_id);
|
||||
self.angle_shift = 0.0;
|
||||
}
|
||||
None => {
|
||||
println!("No submenu id found");
|
||||
}
|
||||
}
|
||||
}
|
||||
Beo5Event::RightButtonPressed => {
|
||||
// That means that we get out of a submenu
|
||||
self.current_app_mut().unwrap().base_mut().exit_submenu();
|
||||
println!("Right button pressed");
|
||||
if let Some(selected_app) = self.current_app_mut() {
|
||||
selected_app.base_mut().main_menu.go_right();
|
||||
}
|
||||
}
|
||||
Beo5Event::SelectionWheelRel(rel_angle_eps) => {
|
||||
self.time_without_wheel_spin = 0.0;
|
||||
|
@ -144,6 +148,15 @@ impl BeoUi {
|
|||
// TODO: pass event to current app
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"Current submenu: {:?}",
|
||||
self.current_app_mut()
|
||||
.unwrap()
|
||||
.base_mut()
|
||||
.main_menu
|
||||
.get_deepest_selected_submenu()
|
||||
.name
|
||||
);
|
||||
}
|
||||
|
||||
pub fn draw<T: Renderer>(&self, canvas: &mut Canvas<T>, fonts: &Fonts) {
|
||||
|
@ -214,12 +227,11 @@ impl BeoUi {
|
|||
paint_selected.set_text_baseline(Baseline::Top);
|
||||
|
||||
for appid in 0..apps.len() {
|
||||
let paint;
|
||||
if self.current_app_id == Some(appid) {
|
||||
paint = &paint_selected;
|
||||
let paint = if self.current_app_id == Some(appid) {
|
||||
&paint_selected
|
||||
} else {
|
||||
paint = &paint_normal;
|
||||
}
|
||||
&paint_normal
|
||||
};
|
||||
|
||||
let app = &apps[appid];
|
||||
let _ = canvas.fill_text(pts[appid].x, pts[appid].y, app.base().name(), paint);
|
||||
|
|
Loading…
Reference in a new issue