Compare commits
4 commits
0313e7e6f3
...
fa132e2230
Author | SHA1 | Date | |
---|---|---|---|
fa132e2230 | |||
ff510a8692 | |||
8341b9d54c | |||
65088be75c |
6 changed files with 170 additions and 94 deletions
122
src/apps.rs
Normal file
122
src/apps.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
pub struct BeoApps {
|
||||
pub apps: Vec<Box<dyn App>>,
|
||||
}
|
||||
|
||||
struct MainMenu {
|
||||
pub last_id: usize,
|
||||
pub default_id: usize,
|
||||
pub names: Vec<String>,
|
||||
}
|
||||
|
||||
pub struct AppBase {
|
||||
name: String,
|
||||
pub main_menu: MainMenu,
|
||||
}
|
||||
|
||||
impl AppBase {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
pub trait App {
|
||||
fn base(&self) -> &AppBase;
|
||||
// fn main_menu(&self) -> &MainMenu;
|
||||
}
|
||||
|
||||
struct Spotify {
|
||||
base: AppBase,
|
||||
}
|
||||
|
||||
impl Spotify {
|
||||
fn new() -> Self {
|
||||
Spotify {
|
||||
base: AppBase {
|
||||
name: "Spotify".to_string(),
|
||||
main_menu: MainMenu {
|
||||
last_id: 0,
|
||||
default_id: 0,
|
||||
names: vec![
|
||||
"Playlists".to_string(),
|
||||
"Artists".to_string(),
|
||||
"Albums".to_string(),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for Spotify {
|
||||
fn base(&self) -> &AppBase {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
|
||||
// Similar implementations for other apps like Radio and Settings
|
||||
struct Radio {
|
||||
base: AppBase,
|
||||
}
|
||||
|
||||
impl Radio {
|
||||
fn new() -> Self {
|
||||
Radio {
|
||||
base: AppBase {
|
||||
name: "Radio".to_string(),
|
||||
main_menu: MainMenu {
|
||||
last_id: 0,
|
||||
default_id: 0,
|
||||
names: vec![
|
||||
"Favorites".to_string(),
|
||||
"Local".to_string(),
|
||||
"Global".to_string(),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for Radio {
|
||||
fn base(&self) -> &AppBase {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings {
|
||||
base: AppBase,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
fn new() -> Self {
|
||||
Settings {
|
||||
base: AppBase {
|
||||
name: "Settings".to_string(),
|
||||
main_menu: MainMenu {
|
||||
last_id: 0,
|
||||
default_id: 0,
|
||||
names: vec![
|
||||
"Display".to_string(),
|
||||
"Sound".to_string(),
|
||||
"Network".to_string(),
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl App for Settings {
|
||||
fn base(&self) -> &AppBase {
|
||||
&self.base
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_beo_apps() -> BeoApps {
|
||||
let apps: Vec<Box<dyn App>> = vec![
|
||||
Box::new(Spotify::new()),
|
||||
Box::new(Radio::new()),
|
||||
Box::new(Settings::new()),
|
||||
];
|
||||
BeoApps { apps }
|
||||
}
|
|
@ -21,11 +21,7 @@ use winit::{event_loop::EventLoop, window::WindowBuilder};
|
|||
mod perf_graph;
|
||||
pub use perf_graph::PerfGraph;
|
||||
|
||||
pub fn start(
|
||||
width: u32,
|
||||
height: u32,
|
||||
title: &'static str,
|
||||
) {
|
||||
pub fn start(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.
|
||||
#[cfg(all(debug_assertions, target_arch = "wasm32"))]
|
||||
|
@ -114,12 +110,5 @@ pub fn start(
|
|||
(canvas, window, gl_context, surface)
|
||||
};
|
||||
|
||||
|
||||
run(
|
||||
canvas,
|
||||
event_loop,
|
||||
context,
|
||||
surface,
|
||||
window,
|
||||
);
|
||||
run(canvas, event_loop, context, surface, window);
|
||||
}
|
||||
|
|
10
src/hid.rs
10
src/hid.rs
|
@ -1,16 +1,12 @@
|
|||
use core::panic;
|
||||
use nix::{
|
||||
fcntl::{FcntlArg, OFlag},
|
||||
sys::epoll,
|
||||
};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io,
|
||||
os::fd::{AsRawFd, FromRawFd, OwnedFd},
|
||||
};
|
||||
|
||||
use evdev;
|
||||
|
||||
const LASER_POINTER_MAX: i32 = 121;
|
||||
#[derive(Debug)]
|
||||
pub enum Beo5Event {
|
||||
|
@ -93,7 +89,7 @@ impl Beo5Device {
|
|||
}
|
||||
}
|
||||
Err(x) => {
|
||||
if (x.kind() == std::io::ErrorKind::WouldBlock) {
|
||||
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);
|
||||
|
@ -107,7 +103,7 @@ impl Beo5Device {
|
|||
if ev.is_some() {
|
||||
return Beo5Device::parse_event(ev.unwrap());
|
||||
}
|
||||
return None;
|
||||
None
|
||||
}
|
||||
|
||||
fn parse_event(ev: evdev::InputEvent) -> Option<Beo5Event> {
|
||||
|
@ -177,6 +173,6 @@ impl Beo5Device {
|
|||
}
|
||||
_ => {} //SYN et al
|
||||
}
|
||||
return None;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -1,6 +1,6 @@
|
|||
use apps::get_beo_apps;
|
||||
use femtovg::{
|
||||
renderer::OpenGl, Align, Baseline, Canvas, Color, FontId, ImageFlags, ImageId, Paint, Path,
|
||||
Renderer,
|
||||
renderer::OpenGl, Align, Baseline, Canvas, Color, FontId, ImageId, Paint, Path, Renderer,
|
||||
};
|
||||
use instant::Instant;
|
||||
use resource::resource;
|
||||
|
@ -24,6 +24,7 @@ fn main() {
|
|||
|
||||
use glutin::prelude::*;
|
||||
|
||||
mod apps;
|
||||
mod hid;
|
||||
mod roundy_math;
|
||||
mod ui;
|
||||
|
@ -49,10 +50,8 @@ fn run(
|
|||
.expect("Cannot add font"),
|
||||
};
|
||||
|
||||
let mut beo = ui::Beo::new();
|
||||
for app in &beo.apps {
|
||||
println!(">> {}", app.name());
|
||||
}
|
||||
let apps = get_beo_apps();
|
||||
let mut beo = ui::BeoUi::new(apps);
|
||||
|
||||
let start = Instant::now();
|
||||
let mut prevt = start;
|
||||
|
@ -74,7 +73,7 @@ fn run(
|
|||
let mut t = 0;
|
||||
|
||||
el.run(move |event, _, control_flow| {
|
||||
t = t + 1;
|
||||
t += 1;
|
||||
*control_flow = ControlFlow::Poll;
|
||||
|
||||
let hw_event = beo_device.get_event_nonblocking();
|
||||
|
|
|
@ -55,7 +55,7 @@ impl VirtualCircle {
|
|||
|
||||
pub fn get_x_on_circle(&self, y: f32) -> f32 {
|
||||
// Given an x coordinate, return the y coordinate so that it lies on the circle
|
||||
let x = self.center.x - (self.radius.powf(2.) - (y - self.center.y).powf(2.)).sqrt();
|
||||
x as f32
|
||||
|
||||
self.center.x - (self.radius.powf(2.) - (y - self.center.y).powf(2.)).sqrt()
|
||||
}
|
||||
}
|
||||
|
|
100
src/ui.rs
100
src/ui.rs
|
@ -2,10 +2,12 @@ use femtovg::{Align, Baseline, Canvas, Color, Paint, Path, Renderer};
|
|||
|
||||
use crate::{hid::Beo5Event, Fonts};
|
||||
|
||||
use crate::apps::{App, BeoApps};
|
||||
use crate::roundy_math;
|
||||
|
||||
pub struct Beo {
|
||||
pub apps: Vec<Box<dyn App>>,
|
||||
pub struct BeoUi {
|
||||
pub beo_apps: BeoApps,
|
||||
pub current_app_id: Option<usize>,
|
||||
pub laser_pct: f32,
|
||||
}
|
||||
|
||||
|
@ -19,10 +21,11 @@ fn laser_pct_to_y_pos(pct: f32) -> f32 {
|
|||
(pct * 1.5 - 0.25) * CANVAS_HEIGHT
|
||||
}
|
||||
|
||||
impl Beo {
|
||||
pub fn new() -> Beo {
|
||||
Beo {
|
||||
apps: get_apps(),
|
||||
impl BeoUi {
|
||||
pub fn new(apps: BeoApps) -> BeoUi {
|
||||
BeoUi {
|
||||
beo_apps: apps,
|
||||
current_app_id: None,
|
||||
laser_pct: 0.0,
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +34,7 @@ impl Beo {
|
|||
match event {
|
||||
Beo5Event::LaserPosition(pct) => {
|
||||
self.laser_pct = pct;
|
||||
println!("Laser moved to {}%", pct * 100.);
|
||||
self.choose_app_from_laser_pct();
|
||||
}
|
||||
_ => {
|
||||
// TODO: pass event to current app
|
||||
|
@ -41,9 +44,7 @@ impl Beo {
|
|||
|
||||
pub fn draw<T: Renderer>(&self, canvas: &mut Canvas<T>, fonts: &Fonts) {
|
||||
self.draw_main_menu(canvas, fonts);
|
||||
if let Some(selected_app_id) = self.lasered_application_id() {
|
||||
let selected_app = self.apps.get(selected_app_id).unwrap();
|
||||
|
||||
if let Some(selected_app) = self.current_app() {
|
||||
let mut paint_title = Paint::color(Color::hex("FFFFFF"));
|
||||
paint_title.set_font(&[fonts.bold]);
|
||||
paint_title.set_text_baseline(Baseline::Top);
|
||||
|
@ -52,16 +53,17 @@ impl Beo {
|
|||
let _ = canvas.fill_text(
|
||||
CANVAS_WIDTH / 2.,
|
||||
10.,
|
||||
format!("{}", selected_app.name()),
|
||||
selected_app.base().name(),
|
||||
&paint_title,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn lasered_application_id(&self) -> Option<usize> {
|
||||
let app_count = self.apps.len();
|
||||
fn choose_app_from_laser_pct(&mut self) {
|
||||
let app_count = self.beo_apps.apps.len();
|
||||
if app_count == 0 {
|
||||
return None;
|
||||
self.current_app_id = None;
|
||||
return;
|
||||
}
|
||||
|
||||
let dx = 100.0 / (app_count as f32 * 2.);
|
||||
|
@ -71,10 +73,16 @@ impl Beo {
|
|||
let evt_app_pos = dx * (i * 2 + 1) as f32 * CANVAS_HEIGHT / 100.0;
|
||||
let delta = (laser_y - evt_app_pos).abs();
|
||||
if delta < LASER_EPS_MATCH {
|
||||
return Some(i);
|
||||
self.current_app_id = Some(i);
|
||||
return;
|
||||
//Some(&self.beo_apps.apps[i]);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
self.current_app_id = None;
|
||||
}
|
||||
|
||||
fn current_app(&self) -> Option<&Box<dyn App>> {
|
||||
self.current_app_id.map(|id| &self.beo_apps.apps[id])
|
||||
}
|
||||
|
||||
fn draw_main_menu<T: Renderer>(&self, canvas: &mut Canvas<T>, fonts: &Fonts) {
|
||||
|
@ -95,7 +103,7 @@ impl Beo {
|
|||
y: canvas_height as f32,
|
||||
};
|
||||
|
||||
let apps = &self.apps;
|
||||
let apps = &self.beo_apps.apps;
|
||||
// draw the main apps in the circle
|
||||
let pts = main_menu_circle.get_equidistant_points(apps.len(), canvas_size);
|
||||
|
||||
|
@ -106,22 +114,16 @@ impl Beo {
|
|||
let mut paint_selected = Paint::color(Color::hex("D7612E"));
|
||||
paint_selected.set_font(&[fonts.bold]);
|
||||
paint_selected.set_text_baseline(Baseline::Top);
|
||||
for i in 0..apps.len() {
|
||||
if self.lasered_application_id() == Some(i) {
|
||||
let _ = canvas.fill_text(
|
||||
pts[i].x as f32,
|
||||
pts[i].y as f32,
|
||||
format!("{}", apps[i].name()),
|
||||
&paint_selected,
|
||||
);
|
||||
} else {
|
||||
let _ = canvas.fill_text(
|
||||
pts[i].x as f32,
|
||||
pts[i].y as f32,
|
||||
format!("{}", apps[i].name()),
|
||||
&paint_normal,
|
||||
);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
for app in apps {
|
||||
//if self.current_app == Some(app) {
|
||||
// let _ =
|
||||
// canvas.fill_text(pts[i].x, pts[i].y, apps[i].base().name(), &paint_selected);
|
||||
//} else {
|
||||
let _ = canvas.fill_text(pts[i].x, pts[i].y, app.base().name(), &paint_normal);
|
||||
// }
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// draw the laser
|
||||
|
@ -134,35 +136,3 @@ impl Beo {
|
|||
canvas.fill_path(&path, &ellipse_color);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait App {
|
||||
fn name(&self) -> &str;
|
||||
}
|
||||
|
||||
struct Spotify;
|
||||
impl App for Spotify {
|
||||
fn name(&self) -> &str {
|
||||
"Spotify"
|
||||
}
|
||||
}
|
||||
struct Radio;
|
||||
impl App for Radio {
|
||||
fn name(&self) -> &str {
|
||||
"Radio"
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings;
|
||||
impl App for Settings {
|
||||
fn name(&self) -> &str {
|
||||
"Settings"
|
||||
}
|
||||
}
|
||||
|
||||
fn get_apps() -> Vec<Box<dyn App>> {
|
||||
vec![
|
||||
Box::new(Spotify {}),
|
||||
Box::new(Radio {}),
|
||||
Box::new(Settings {}),
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue