From fc2e88add283e6b54358b77b6bc08421949fbf2e Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Sun, 26 Nov 2023 13:03:19 -0500 Subject: [PATCH] Set up a GTK simulator for the bike lights engine --- Cargo.lock | 35 ++++ Cargo.toml | 2 + bike-lights/core/Cargo.toml | 8 + bike-lights/core/src/lib.rs | 14 ++ bike-lights/simulator/Cargo.toml | 15 ++ bike-lights/simulator/src/main.rs | 295 ++++++++++++++++++++++++++++++ 6 files changed, 369 insertions(+) create mode 100644 bike-lights/core/Cargo.toml create mode 100644 bike-lights/core/src/lib.rs create mode 100644 bike-lights/simulator/Cargo.toml create mode 100644 bike-lights/simulator/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4118f22..66bcce8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,6 +330,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "backtrace" version = "0.3.69" @@ -1117,6 +1123,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "fixed" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c69ce7e7c0f17aa18fdd9d0de39727adb9c6281f2ad12f57cbe54ae6e76e7d" +dependencies = [ + "az", + "bytemuck", + "half", + "typenum", +] + [[package]] name = "flate2" version = "1.0.28" @@ -2303,6 +2321,10 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "lights-core" +version = "0.1.0" + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -3751,6 +3773,19 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simulator" +version = "0.1.0" +dependencies = [ + "cairo-rs", + "fixed", + "gio", + "glib", + "gtk4", + "libadwaita", + "pango", +] + [[package]] name = "siphasher" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 538b62b..47cc89e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ resolver = "2" members = [ "authdb", + "bike-lights/core", + "bike-lights/simulator", "changeset", "config", "config-derive", diff --git a/bike-lights/core/Cargo.toml b/bike-lights/core/Cargo.toml new file mode 100644 index 0000000..4c709a8 --- /dev/null +++ b/bike-lights/core/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "lights-core" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/bike-lights/core/src/lib.rs b/bike-lights/core/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/bike-lights/core/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/bike-lights/simulator/Cargo.toml b/bike-lights/simulator/Cargo.toml new file mode 100644 index 0000000..0a93f0f --- /dev/null +++ b/bike-lights/simulator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "simulator" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] } +cairo-rs = { version = "0.18" } +fixed = { version = "1" } +gio = { version = "0.18" } +glib = { version = "0.18" } +gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] } +pango = { version = "*" } diff --git a/bike-lights/simulator/src/main.rs b/bike-lights/simulator/src/main.rs new file mode 100644 index 0000000..97fcdee --- /dev/null +++ b/bike-lights/simulator/src/main.rs @@ -0,0 +1,295 @@ +use adw::prelude::*; +use glib::Object; +use gtk::subclass::prelude::*; +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 DashboardLightsPrivate { + lights: Rc>, +} + +#[glib::object_subclass] +impl ObjectSubclass for DashboardLightsPrivate { + const NAME: &'static str = "DashboardLights"; + type Type = DashboardLights; + type ParentType = gtk::DrawingArea; + + fn new() -> Self { + Self { + lights: Rc::new(RefCell::new([ + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + RGB { r: 0, g: 0, b: 0 }, + ])), + } + } +} + +impl ObjectImpl for DashboardLightsPrivate {} +impl WidgetImpl for DashboardLightsPrivate {} +impl DrawingAreaImpl for DashboardLightsPrivate {} + +glib::wrapper! { + pub struct DashboardLights(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; +} + +impl DashboardLights { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + + s.set_width_request(WIDTH); + s.set_height_request(100); + + s.set_draw_func({ + let s = s.clone(); + move |_, context, width, _| { + let start = width as f64 / 2. - 150.; + 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, + ); + context.rectangle(start + 100. * i as f64, 10., 80., 80.); + let _ = context.fill(); + } + } + }); + + s + } + + pub fn set_lights(&self, lights: [RGB; 3]) { + *self.imp().lights.borrow_mut() = lights; + self.queue_draw(); + } +} + +pub struct BikeLightsPrivate { + lights: Rc>, +} + +#[glib::object_subclass] +impl ObjectSubclass for BikeLightsPrivate { + const NAME: &'static str = "BikeLights"; + type Type = BikeLights; + type ParentType = gtk::DrawingArea; + + fn new() -> Self { + Self { + lights: Rc::new(RefCell::new([ + 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 }, + ])), + } + } +} + +impl ObjectImpl for BikeLightsPrivate {} +impl WidgetImpl for BikeLightsPrivate {} +impl DrawingAreaImpl for BikeLightsPrivate {} + +glib::wrapper! { + pub struct BikeLights(ObjectSubclass) @extends gtk::DrawingArea, gtk::Widget; +} + +impl BikeLights { + pub fn new() -> Self { + let s: Self = Object::builder().build(); + + s.set_width_request(WIDTH); + s.set_height_request(200); + + s.set_draw_func({ + let s = s.clone(); + move |_, context, _, _| { + 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, + ); + 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, + ); + context.rectangle(5. + 20. * i as f64, 30., 15., 15.); + let _ = context.fill(); + } + } + }); + + s + } + + pub fn set_lights(&self, lights: [RGB; 60]) { + *self.imp().lights.borrow_mut() = lights; + self.queue_draw(); + } +} + +fn main() { + let app = adw::Application::builder() + .application_id("com.luminescent-dreams.bike-light-simulator") + .build(); + + app.connect_activate(move |app| { + let window = adw::ApplicationWindow::builder() + .application(app) + .default_width(WIDTH) + .default_height(HEIGHT) + .build(); + let layout = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .build(); + let controls = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + + let dashboard_lights = DashboardLights::new(); + let bike_lights = BikeLights::new(); + + let left_button = gtk::Button::builder().label("L").build(); + 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); + layout.append(&controls); + + let pattern_controls = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .build(); + + let previous_pattern = gtk::Button::builder().label("Previous").build(); + let next_pattern = gtk::Button::builder().label("Next").build(); + + pattern_controls.append(&previous_pattern); + pattern_controls.append(&next_pattern); + layout.append(&pattern_controls); + + layout.append(&dashboard_lights); + layout.append(&bike_lights); + + window.set_content(Some(&layout)); + window.present(); + }); + + let args: Vec = env::args().collect(); + ApplicationExtManual::run_with_args(&app, &args); +}