diff --git a/Cargo.lock b/Cargo.lock index 0795fd3..688f411 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3637,6 +3637,7 @@ dependencies = [ "glib", "gtk4", "libadwaita", + "lights-core", "pango", ] diff --git a/bike-lights/core/src/lib.rs b/bike-lights/core/src/lib.rs index 7d12d9a..5c4ed49 100644 --- a/bike-lights/core/src/lib.rs +++ b/bike-lights/core/src/lib.rs @@ -1,14 +1,72 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +use std::time::Duration; + +mod patterns; +pub use patterns::*; + +mod types; +pub use types::{DashboardPattern, Pattern, RGB}; + +pub trait UI { + fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern); } -#[cfg(test)] -mod tests { - use super::*; +pub trait Animation { + fn tick(&self, frame: Duration) -> (DashboardPattern, Pattern); +} - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); +pub struct DefaultAnimation {} + +impl Animation for DefaultAnimation { + fn tick(&self, _: Duration) -> (DashboardPattern, Pattern) { + (PRIDE_DASHBOARD, PRIDE) + } +} + +#[derive(Clone)] +pub enum Event { + Brake, + BrakeRelease, + LeftBlinker, + LeftBlinkerRelease, + NextPattern, + PreviousPattern, + RightBlinker, + RightBlinkerRelease, +} + +#[derive(Clone)] +pub enum State { + Pattern(u8), + Brake, + LeftBlinker, + RightBlinker, + BrakeLeftBlinker, + BrakeRightBlinker, +} + +pub struct App { + ui: Box, + state: State, + current_animation: Box, + dashboard_lights: DashboardPattern, + lights: Pattern, +} + +impl App { + pub fn new(ui: Box) -> Self { + Self { + ui, + state: State::Pattern(0), + current_animation: Box::new(DefaultAnimation {}), + dashboard_lights: OFF_DASHBOARD, + lights: OFF, + } + } + + pub fn tick(&mut self, frame: Duration) { + let (dashboard, lights) = self.current_animation.tick(frame); + self.dashboard_lights = dashboard.clone(); + self.lights = lights.clone(); + self.ui.update_lights(dashboard, lights); } } diff --git a/bike-lights/core/src/patterns.rs b/bike-lights/core/src/patterns.rs new file mode 100644 index 0000000..063b4df --- /dev/null +++ b/bike-lights/core/src/patterns.rs @@ -0,0 +1,671 @@ +use crate::RGB; + +pub type DashboardPattern = [RGB; 3]; +pub type Pattern = [RGB; 60]; + +pub const OFF_DASHBOARD: DashboardPattern = [ + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, +]; + +pub const OFF: Pattern = [ + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, +]; + +pub const PRIDE_DASHBOARD: DashboardPattern = [ + RGB { r: 228, g: 3, b: 3 }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, +]; + +pub const PRIDE: Pattern = [ + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { r: 228, g: 3, b: 3 }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 140, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 255, + g: 237, + b: 0, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 0, + g: 128, + b: 38, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 36, + g: 64, + b: 142, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, + RGB { + r: 115, + g: 41, + b: 130, + }, +]; + +pub const TRANS_PRIDE_DASHBOARD: DashboardPattern = [ + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, +]; + +pub const TRANS_PRIDE: Pattern = [ + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 255, + g: 255, + b: 255, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 245, + g: 169, + b: 184, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, + RGB { + r: 91, + g: 206, + b: 250, + }, +]; diff --git a/bike-lights/core/src/types.rs b/bike-lights/core/src/types.rs new file mode 100644 index 0000000..07b1b8d --- /dev/null +++ b/bike-lights/core/src/types.rs @@ -0,0 +1,9 @@ +#[derive(Clone)] +pub struct RGB { + pub r: u8, + pub g: u8, + pub b: u8, +} + +pub type DashboardPattern = [RGB; 3]; +pub type Pattern = [RGB; 60]; diff --git a/bike-lights/simulator/Cargo.toml b/bike-lights/simulator/Cargo.toml index 0a93f0f..42787ae 100644 --- a/bike-lights/simulator/Cargo.toml +++ b/bike-lights/simulator/Cargo.toml @@ -12,4 +12,5 @@ fixed = { version = "1" } gio = { version = "0.18" } glib = { version = "0.18" } gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] } +lights-core = { path = "../core" } pango = { version = "*" } diff --git a/bike-lights/simulator/src/main.rs b/bike-lights/simulator/src/main.rs index 97fcdee..e959e68 100644 --- a/bike-lights/simulator/src/main.rs +++ b/bike-lights/simulator/src/main.rs @@ -1,19 +1,19 @@ use adw::prelude::*; -use glib::Object; +use glib::{Object, Sender}; use gtk::subclass::prelude::*; +use lights_core::{App, DashboardPattern, Pattern, RGB, UI}; use std::{cell::RefCell, env, rc::Rc}; const WIDTH: i32 = 640; const HEIGHT: i32 = 480; -pub struct RGB { - pub r: u8, - pub g: u8, - pub b: u8, +pub struct Update { + dashboard: DashboardPattern, + lights: Pattern, } pub struct DashboardLightsPrivate { - lights: Rc>, + lights: Rc>, } #[glib::object_subclass] @@ -55,9 +55,9 @@ impl DashboardLights { let lights = s.imp().lights.borrow(); for i in 0..3 { context.set_source_rgb( - lights[i].r as f64, - lights[i].g as f64, - lights[i].b as f64, + lights[i].r as f64 / 256., + lights[i].g as f64 / 256., + lights[i].b as f64 / 256., ); context.rectangle(start + 100. * i as f64, 10., 80., 80.); let _ = context.fill(); @@ -75,7 +75,7 @@ impl DashboardLights { } pub struct BikeLightsPrivate { - lights: Rc>, + lights: Rc>, } #[glib::object_subclass] @@ -173,18 +173,18 @@ impl BikeLights { let lights = s.imp().lights.borrow(); for i in 0..30 { context.set_source_rgb( - lights[i].r as f64, - lights[i].g as f64, - lights[i].b as f64, + lights[i].r as f64 / 256., + lights[i].g as f64 / 256., + lights[i].b as f64 / 256., ); context.rectangle(5. + 20. * i as f64, 5., 15., 15.); let _ = context.fill(); } for i in 0..30 { context.set_source_rgb( - lights[i + 30].r as f64, - lights[i + 30].g as f64, - lights[i + 30].b as f64, + lights[i + 30].r as f64 / 256., + lights[i + 30].g as f64 / 256., + lights[i + 30].b as f64 / 256., ); context.rectangle(5. + 20. * i as f64, 30., 15., 15.); let _ = context.fill(); @@ -201,14 +201,42 @@ impl BikeLights { } } +struct GTKUI { + tx: Rc>>>, +} + +impl UI for GTKUI { + fn update_lights(&self, dashboard_lights: DashboardPattern, lights: Pattern) { + if let Some(tx) = self.tx.borrow().as_ref() { + tx.send(Update { + dashboard: dashboard_lights, + lights, + }) + .unwrap(); + } + } +} + fn main() { - let app = adw::Application::builder() + let adw_app = adw::Application::builder() .application_id("com.luminescent-dreams.bike-light-simulator") .build(); - app.connect_activate(move |app| { + adw_app.connect_activate(move |adw_app| { + let (tx, rx) = gtk::glib::MainContext::channel::(gtk::glib::Priority::DEFAULT); + + std::thread::spawn(move || { + let mut bike_app = App::new(Box::new(GTKUI { + tx: Rc::new(RefCell::new(Some(tx))), + })); + loop { + bike_app.tick(std::time::Duration::from_millis(0)); + std::thread::sleep(std::time::Duration::from_millis(100)); + } + }); + let window = adw::ApplicationWindow::builder() - .application(app) + .application(adw_app) .default_width(WIDTH) .default_height(HEIGHT) .build(); @@ -226,47 +254,6 @@ fn main() { let brake_button = gtk::Button::builder().label("Brakes").build(); let right_button = gtk::Button::builder().label("R").build(); - left_button.connect_clicked({ - let dashboard_lights = dashboard_lights.clone(); - move |_| { - dashboard_lights.set_lights([ - RGB { - r: 128, - g: 128, - b: 0, - }, - RGB { r: 0, g: 0, b: 0 }, - RGB { r: 0, g: 0, b: 0 }, - ]); - } - }); - - brake_button.connect_clicked({ - let dashboard_lights = dashboard_lights.clone(); - move |_| { - dashboard_lights.set_lights([ - RGB { r: 128, g: 0, b: 0 }, - RGB { r: 128, g: 0, b: 0 }, - RGB { r: 128, g: 0, b: 0 }, - ]); - } - }); - - right_button.connect_clicked({ - let dashboard_lights = dashboard_lights.clone(); - move |_| { - dashboard_lights.set_lights([ - RGB { r: 0, g: 0, b: 0 }, - RGB { r: 0, g: 0, b: 0 }, - RGB { - r: 128, - g: 128, - b: 0, - }, - ]); - } - }); - controls.append(&left_button); controls.append(&brake_button); controls.append(&right_button); @@ -286,10 +273,20 @@ fn main() { layout.append(&dashboard_lights); layout.append(&bike_lights); + rx.attach(None, { + let dashboard_lights = dashboard_lights.clone(); + let bike_lights = bike_lights.clone(); + move |Update { dashboard, lights }| { + dashboard_lights.set_lights(dashboard); + bike_lights.set_lights(lights); + glib::ControlFlow::Continue + } + }); + window.set_content(Some(&layout)); window.present(); }); let args: Vec = env::args().collect(); - ApplicationExtManual::run_with_args(&app, &args); + ApplicationExtManual::run_with_args(&adw_app, &args); }