Compare commits
21 Commits
main
...
kifu/local
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | b36e152050 | |
Savanni D'Gerinel | 9e5bac3001 | |
Savanni D'Gerinel | 1e5341d0e6 | |
Savanni D'Gerinel | 4114e64b8e | |
Savanni D'Gerinel | ff1d117e8c | |
Savanni D'Gerinel | 5c7f7766de | |
Savanni D'Gerinel | 034bda502a | |
Savanni D'Gerinel | 15b1d4bfd6 | |
Savanni D'Gerinel | 8ceca454b1 | |
Savanni D'Gerinel | 30198f0038 | |
Savanni D'Gerinel | 1881fb99ce | |
Savanni D'Gerinel | 4cfc6425e1 | |
Savanni D'Gerinel | 137a88ad8e | |
Savanni D'Gerinel | f8c19b2d18 | |
Savanni D'Gerinel | 35c034b04b | |
Savanni D'Gerinel | 9699c63e50 | |
Savanni D'Gerinel | 822e88a8ce | |
Savanni D'Gerinel | a9d29e6518 | |
Savanni D'Gerinel | d95dd4de50 | |
Savanni D'Gerinel | 245f9d0997 | |
Savanni D'Gerinel | 22b772a8c7 |
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
|
@ -2,9 +2,6 @@
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"authdb",
|
"authdb",
|
||||||
"bike-lights/bike",
|
|
||||||
"bike-lights/core",
|
|
||||||
"bike-lights/simulator",
|
|
||||||
"changeset",
|
"changeset",
|
||||||
"config",
|
"config",
|
||||||
"config-derive",
|
"config-derive",
|
||||||
|
@ -21,14 +18,16 @@ members = [
|
||||||
"hex-grid",
|
"hex-grid",
|
||||||
"icon-test",
|
"icon-test",
|
||||||
"ifc",
|
"ifc",
|
||||||
|
"kifu/core",
|
||||||
|
"kifu/gtk",
|
||||||
|
"l10n",
|
||||||
"memorycache",
|
"memorycache",
|
||||||
"nom-training",
|
"nom-training",
|
||||||
"otg/core",
|
|
||||||
"otg/gtk",
|
|
||||||
"result-extended",
|
"result-extended",
|
||||||
"screenplay",
|
"screenplay",
|
||||||
"sgf",
|
"sgf",
|
||||||
"timezone-testing",
|
"timezone-testing",
|
||||||
"tree",
|
"tree",
|
||||||
"visions/server", "gm-dash/server",
|
"visions/server",
|
||||||
|
"xdg-test",
|
||||||
]
|
]
|
||||||
|
|
|
@ -19,8 +19,6 @@ clap = { version = "4", features = [ "derive" ] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
sha2 = { version = "0.10" }
|
sha2 = { version = "0.10" }
|
||||||
sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite" ] }
|
sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite" ] }
|
||||||
# sqlformat introduced a mistaken breaking change in 0.2.7
|
|
||||||
sqlformat = { version = "=0.2.6" }
|
|
||||||
thiserror = { version = "1" }
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1", features = [ "full" ] }
|
tokio = { version = "1", features = [ "full" ] }
|
||||||
uuid = { version = "0.4", features = [ "serde", "v4" ] }
|
uuid = { version = "0.4", features = [ "serde", "v4" ] }
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
[build]
|
|
||||||
target = "thumbv6m-none-eabi"
|
|
||||||
|
|
||||||
[target.thumbv6m-none-eabi]
|
|
||||||
rustflags = [
|
|
||||||
"-C", "link-arg=--nmagic",
|
|
||||||
"-C", "link-arg=-Tlink.x",
|
|
||||||
"-C", "inline-threshold=5",
|
|
||||||
"-C", "no-vectorize-loops",
|
|
||||||
]
|
|
||||||
|
|
||||||
runner = "elf2uf2-rs -d"
|
|
|
@ -1,18 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "bike"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
az = { version = "1" }
|
|
||||||
cortex-m-rt = { version = "0.7.3" }
|
|
||||||
cortex-m = { version = "0.7.7" }
|
|
||||||
embedded-alloc = { version = "0.5.1" }
|
|
||||||
embedded-hal = { version = "0.2.7" }
|
|
||||||
fixed = { version = "1" }
|
|
||||||
fugit = { version = "0.3.7" }
|
|
||||||
lights-core = { path = "../core" }
|
|
||||||
panic-halt = { version = "0.2.0" }
|
|
||||||
rp-pico = { version = "0.8.0" }
|
|
|
@ -1,241 +0,0 @@
|
||||||
#![no_main]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use az::*;
|
|
||||||
use core::cell::RefCell;
|
|
||||||
use cortex_m::delay::Delay;
|
|
||||||
use embedded_alloc::Heap;
|
|
||||||
use embedded_hal::{blocking::spi::Write, digital::v2::InputPin, digital::v2::OutputPin};
|
|
||||||
use fixed::types::I16F16;
|
|
||||||
use fugit::RateExtU32;
|
|
||||||
use lights_core::{App, BodyPattern, DashboardPattern, Event, Instant, FPS, UI};
|
|
||||||
use panic_halt as _;
|
|
||||||
use rp_pico::{
|
|
||||||
entry,
|
|
||||||
hal::{
|
|
||||||
clocks::init_clocks_and_plls,
|
|
||||||
gpio::{FunctionSio, Pin, PinId, PullDown, PullUp, SioInput, SioOutput},
|
|
||||||
pac::{CorePeripherals, Peripherals},
|
|
||||||
spi::{Enabled, Spi, SpiDevice, ValidSpiPinout},
|
|
||||||
watchdog::Watchdog,
|
|
||||||
Clock, Sio,
|
|
||||||
},
|
|
||||||
Pins,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[global_allocator]
|
|
||||||
static HEAP: Heap = Heap::empty();
|
|
||||||
|
|
||||||
const LIGHT_SCALE: I16F16 = I16F16::lit("256.0");
|
|
||||||
const DASHBOARD_BRIGHTESS: u8 = 1;
|
|
||||||
const BODY_BRIGHTNESS: u8 = 8;
|
|
||||||
|
|
||||||
struct DebouncedButton<P: PinId> {
|
|
||||||
debounce: Instant,
|
|
||||||
pin: Pin<P, FunctionSio<SioInput>, PullUp>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<P: PinId> DebouncedButton<P> {
|
|
||||||
fn new(pin: Pin<P, FunctionSio<SioInput>, PullUp>) -> Self {
|
|
||||||
Self {
|
|
||||||
debounce: Instant((0 as u32).into()),
|
|
||||||
pin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_low(&self, time: Instant) -> bool {
|
|
||||||
if time <= self.debounce {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
self.pin.is_low().unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_debounce(&mut self, time: Instant) {
|
|
||||||
self.debounce = time + Instant((250 as u32).into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct BikeUI<
|
|
||||||
D: SpiDevice,
|
|
||||||
P: ValidSpiPinout<D>,
|
|
||||||
LeftId: PinId,
|
|
||||||
RightId: PinId,
|
|
||||||
PreviousId: PinId,
|
|
||||||
NextId: PinId,
|
|
||||||
BrakeId: PinId,
|
|
||||||
> {
|
|
||||||
spi: RefCell<Spi<Enabled, D, P, 8>>,
|
|
||||||
left_blinker_button: DebouncedButton<LeftId>,
|
|
||||||
right_blinker_button: DebouncedButton<RightId>,
|
|
||||||
previous_animation_button: DebouncedButton<PreviousId>,
|
|
||||||
next_animation_button: DebouncedButton<NextId>,
|
|
||||||
brake_sensor: Pin<BrakeId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
|
|
||||||
brake_enabled: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
D: SpiDevice,
|
|
||||||
P: ValidSpiPinout<D>,
|
|
||||||
LeftId: PinId,
|
|
||||||
RightId: PinId,
|
|
||||||
PreviousId: PinId,
|
|
||||||
NextId: PinId,
|
|
||||||
BrakeId: PinId,
|
|
||||||
> BikeUI<D, P, LeftId, RightId, PreviousId, NextId, BrakeId>
|
|
||||||
{
|
|
||||||
fn new(
|
|
||||||
spi: Spi<Enabled, D, P, 8>,
|
|
||||||
left_blinker_button: Pin<LeftId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
right_blinker_button: Pin<RightId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
previous_animation_button: Pin<PreviousId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
next_animation_button: Pin<NextId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
brake_sensor: Pin<BrakeId, FunctionSio<SioInput>, PullUp>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
spi: RefCell::new(spi),
|
|
||||||
left_blinker_button: DebouncedButton::new(left_blinker_button),
|
|
||||||
right_blinker_button: DebouncedButton::new(right_blinker_button),
|
|
||||||
previous_animation_button: DebouncedButton::new(previous_animation_button),
|
|
||||||
next_animation_button: DebouncedButton::new(next_animation_button),
|
|
||||||
brake_sensor,
|
|
||||||
|
|
||||||
brake_enabled: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<
|
|
||||||
D: SpiDevice,
|
|
||||||
P: ValidSpiPinout<D>,
|
|
||||||
LeftId: PinId,
|
|
||||||
RightId: PinId,
|
|
||||||
PreviousId: PinId,
|
|
||||||
NextId: PinId,
|
|
||||||
BrakeId: PinId,
|
|
||||||
> UI for BikeUI<D, P, LeftId, RightId, PreviousId, NextId, BrakeId>
|
|
||||||
{
|
|
||||||
fn check_event(&mut self, current_time: Instant) -> Option<Event> {
|
|
||||||
if self.brake_sensor.is_high().unwrap_or(true) && !self.brake_enabled {
|
|
||||||
self.brake_enabled = true;
|
|
||||||
Some(Event::Brake)
|
|
||||||
} else if self.brake_sensor.is_low().unwrap_or(false) && self.brake_enabled {
|
|
||||||
self.brake_enabled = false;
|
|
||||||
Some(Event::BrakeRelease)
|
|
||||||
} else if self.left_blinker_button.is_low(current_time) {
|
|
||||||
self.left_blinker_button.set_debounce(current_time);
|
|
||||||
Some(Event::LeftBlinker)
|
|
||||||
} else if self.right_blinker_button.is_low(current_time) {
|
|
||||||
self.right_blinker_button.set_debounce(current_time);
|
|
||||||
Some(Event::RightBlinker)
|
|
||||||
} else if self.previous_animation_button.is_low(current_time) {
|
|
||||||
self.previous_animation_button.set_debounce(current_time);
|
|
||||||
Some(Event::PreviousPattern)
|
|
||||||
} else if self.next_animation_button.is_low(current_time) {
|
|
||||||
self.next_animation_button.set_debounce(current_time);
|
|
||||||
Some(Event::NextPattern)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern) {
|
|
||||||
let mut lights: [u8; 260] = [0; 260];
|
|
||||||
lights[256] = 0xff;
|
|
||||||
lights[257] = 0xff;
|
|
||||||
lights[258] = 0xff;
|
|
||||||
lights[259] = 0xff;
|
|
||||||
for (idx, rgb) in dashboard_lights.iter().enumerate() {
|
|
||||||
lights[(idx + 1) * 4 + 0] = 0xe0 + DASHBOARD_BRIGHTESS;
|
|
||||||
lights[(idx + 1) * 4 + 1] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as();
|
|
||||||
lights[(idx + 1) * 4 + 2] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as();
|
|
||||||
lights[(idx + 1) * 4 + 3] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as();
|
|
||||||
}
|
|
||||||
for (idx, rgb) in body_lights.iter().enumerate() {
|
|
||||||
lights[(idx + 4) * 4 + 0] = 0xe0 + BODY_BRIGHTNESS;
|
|
||||||
lights[(idx + 4) * 4 + 1] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as();
|
|
||||||
lights[(idx + 4) * 4 + 2] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as();
|
|
||||||
lights[(idx + 4) * 4 + 3] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as();
|
|
||||||
}
|
|
||||||
let mut spi = self.spi.borrow_mut();
|
|
||||||
spi.write(lights.as_slice());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
{
|
|
||||||
use core::mem::MaybeUninit;
|
|
||||||
const HEAP_SIZE: usize = 8096;
|
|
||||||
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
|
||||||
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pac = Peripherals::take().unwrap();
|
|
||||||
let core = CorePeripherals::take().unwrap();
|
|
||||||
let sio = Sio::new(pac.SIO);
|
|
||||||
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
|
||||||
|
|
||||||
let pins = Pins::new(
|
|
||||||
pac.IO_BANK0,
|
|
||||||
pac.PADS_BANK0,
|
|
||||||
sio.gpio_bank0,
|
|
||||||
&mut pac.RESETS,
|
|
||||||
);
|
|
||||||
|
|
||||||
let clocks = init_clocks_and_plls(
|
|
||||||
12_000_000u32,
|
|
||||||
pac.XOSC,
|
|
||||||
pac.CLOCKS,
|
|
||||||
pac.PLL_SYS,
|
|
||||||
pac.PLL_USB,
|
|
||||||
&mut pac.RESETS,
|
|
||||||
&mut watchdog,
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
|
||||||
let mut spi_clk = pins.gpio10.into_function();
|
|
||||||
let mut spi_sdo = pins.gpio11.into_function();
|
|
||||||
let spi = Spi::<_, _, _, 8>::new(pac.SPI1, (spi_sdo, spi_clk));
|
|
||||||
let mut spi = spi.init(
|
|
||||||
&mut pac.RESETS,
|
|
||||||
clocks.peripheral_clock.freq(),
|
|
||||||
1_u32.MHz(),
|
|
||||||
embedded_hal::spi::MODE_1,
|
|
||||||
);
|
|
||||||
|
|
||||||
let left_blinker_button = pins.gpio16.into_pull_up_input();
|
|
||||||
let right_blinker_button = pins.gpio17.into_pull_up_input();
|
|
||||||
let previous_animation_button = pins.gpio27.into_pull_up_input();
|
|
||||||
let next_animation_button = pins.gpio26.into_pull_up_input();
|
|
||||||
let brake_sensor = pins.gpio18.into_pull_up_input();
|
|
||||||
|
|
||||||
let mut led_pin = pins.led.into_push_pull_output();
|
|
||||||
|
|
||||||
let ui = BikeUI::new(
|
|
||||||
spi,
|
|
||||||
left_blinker_button,
|
|
||||||
right_blinker_button,
|
|
||||||
previous_animation_button,
|
|
||||||
next_animation_button,
|
|
||||||
brake_sensor,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut app = App::new(Box::new(ui));
|
|
||||||
|
|
||||||
led_pin.set_high();
|
|
||||||
|
|
||||||
let mut time = Instant::default();
|
|
||||||
let delay_ms = 1000 / (FPS as u32);
|
|
||||||
loop {
|
|
||||||
app.tick(time);
|
|
||||||
|
|
||||||
delay.delay_ms(delay_ms);
|
|
||||||
time = time + Instant(delay_ms.into());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
$fn = 50;
|
|
||||||
threshold = 0.1;
|
|
||||||
half_threshold = threshold / 2;
|
|
||||||
bevel = 0.5;
|
|
||||||
|
|
||||||
wire_radius = 1;
|
|
||||||
|
|
||||||
wall_thickness = 2;
|
|
||||||
cutout_threshold = 1;
|
|
||||||
|
|
||||||
battery_length = 71;
|
|
||||||
battery_width = 18.75;
|
|
||||||
|
|
||||||
cell_holder_length = battery_length + wall_thickness * 2;
|
|
||||||
cell_holder_width = battery_width + wall_thickness * 2;
|
|
||||||
cell_holder_height = battery_width + wall_thickness;
|
|
||||||
|
|
||||||
battery_contact_thickness = .6;
|
|
||||||
// battery_contact_thickness = 1;
|
|
||||||
battery_contact_width = 11;
|
|
||||||
battery_contact_length = 12.8;
|
|
||||||
battery_contact_spring_height = 10.5;
|
|
||||||
battery_contact_flange_height = 1.9;
|
|
||||||
|
|
||||||
converter_width = 11.25;
|
|
||||||
converter_length = 22.25;
|
|
||||||
converter_height = 5;
|
|
||||||
|
|
||||||
|
|
||||||
include <./common.scad>;
|
|
||||||
|
|
||||||
// box(20, 10, 10);
|
|
||||||
// color("blue", 0.5) cube([10, 20, 10], center = true);
|
|
||||||
|
|
||||||
module cell_cradle(width, height) {
|
|
||||||
difference() {
|
|
||||||
translate([0, 0, -height / 2]) cube([width,
|
|
||||||
wall_thickness,
|
|
||||||
height],
|
|
||||||
center = true);
|
|
||||||
color("red", 1) translate([0, 0, 0])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
cylinder(h = wall_thickness + cutout_threshold,
|
|
||||||
r = width / 2,
|
|
||||||
center = true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module cell_box() {
|
|
||||||
union() {
|
|
||||||
channel(cell_holder_length, cell_holder_width, cell_holder_height);
|
|
||||||
translate([0, -battery_length / 6, wall_thickness]) cell_cradle(cell_holder_width, cell_holder_height / 2);
|
|
||||||
translate([0, battery_length / 6, wall_thickness]) cell_cradle(cell_holder_width, cell_holder_height / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module contact_box() {
|
|
||||||
contact_thickness = battery_contact_flange_height * .75;
|
|
||||||
cutout_width = battery_contact_width * .8;
|
|
||||||
// box_thickness = contact_thickness_ + wall_thickness * 2;
|
|
||||||
// box_height = width + wall_thickness;
|
|
||||||
|
|
||||||
difference() {
|
|
||||||
box(wall_thickness * 2 + contact_thickness, cell_holder_width, cell_holder_height);
|
|
||||||
translate([0, contact_thickness, wall_thickness * 2])
|
|
||||||
cube([battery_contact_width,
|
|
||||||
wall_thickness * 2,
|
|
||||||
battery_contact_length + threshold],
|
|
||||||
center = true);
|
|
||||||
|
|
||||||
color("red", 1) translate([0,
|
|
||||||
-(wall_thickness + contact_thickness + threshold) / 2,
|
|
||||||
cell_holder_height / 2])
|
|
||||||
cube([5, wall_thickness + threshold * 2, cell_holder_height], center = true);
|
|
||||||
|
|
||||||
translate([0,
|
|
||||||
-(wall_thickness + contact_thickness + threshold) / 2 - wire_radius,
|
|
||||||
0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = cell_holder_width, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
color("green", 1) translate([-cell_holder_width / 2, 0, cell_holder_height / 2])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = 5, r = contact_thickness / 2, center = true);
|
|
||||||
|
|
||||||
color("green", 1) translate([cell_holder_width / 2, 0, cell_holder_height / 2])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = 5, r = contact_thickness / 2, center = true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module battery_slot() {
|
|
||||||
difference() {
|
|
||||||
union() {
|
|
||||||
translate([0, -cell_holder_length / 2, 0]) contact_box();
|
|
||||||
translate([0, wall_thickness, 0]) cell_box();
|
|
||||||
translate([0, cell_holder_length / 2 + wall_thickness * 2, 0])
|
|
||||||
rotate([0, 0, 180])
|
|
||||||
contact_box();
|
|
||||||
}
|
|
||||||
translate([cell_holder_width / 2, 1, 0]) rotate([90, 0, 0]) cylinder(h = cell_holder_length + wall_thickness * 4 + battery_contact_flange_height * 2, r = wire_radius, center = true);
|
|
||||||
translate([-cell_holder_width / 2, 1, 0]) rotate([90, 0, 0]) cylinder(h = cell_holder_length + wall_thickness * 4 + battery_contact_flange_height * 2, r = wire_radius, center = true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module converter_box() {
|
|
||||||
box_length = wall_thickness * 2 + converter_height;
|
|
||||||
box_width = cell_holder_width * 2 - wall_thickness;
|
|
||||||
difference() {
|
|
||||||
box(box_length, box_width, cell_holder_height);
|
|
||||||
|
|
||||||
translate([cell_holder_width - wire_radius, 0, 0])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
cylinder(h = box_length, r = wire_radius, center = true);
|
|
||||||
translate([cell_holder_width - wire_radius * 2, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = wall_thickness + threshold, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
translate([-cell_holder_width + wire_radius, 0, 0])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
cylinder(h = box_length, r = wire_radius, center = true);
|
|
||||||
translate([-cell_holder_width + wire_radius * 2, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = wall_thickness + threshold, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
translate([0, -box_length / 2, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
cylinder(h = cell_holder_width * 2 + wall_thickness, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
translate([-cell_holder_width * .75, (-box_length + wall_thickness) / 2, 0])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
cylinder(h = wall_thickness * 2, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
translate([cell_holder_width * .75, (-box_length + wall_thickness) / 2, 0])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
cylinder(h = wall_thickness * 2, r = wire_radius, center = true);
|
|
||||||
|
|
||||||
color("red", 1) translate([-box_width / 4, -(converter_height + wall_thickness) / 2, cell_holder_height / 2])
|
|
||||||
cube([5, wall_thickness + threshold * 2, cell_holder_height], center = true);
|
|
||||||
color("red", 1) translate([box_width / 4, -(converter_height + wall_thickness) / 2, cell_holder_height / 2])
|
|
||||||
cube([5, wall_thickness + threshold * 2, cell_holder_height], center = true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module battery_case() {
|
|
||||||
union() {
|
|
||||||
translate([-cell_holder_width / 2, 0, 0]) battery_slot();
|
|
||||||
translate([cell_holder_width / 2 - wall_thickness, 0, 0]) battery_slot();
|
|
||||||
translate([-wall_thickness / 2,
|
|
||||||
cell_holder_length / 2 + wall_thickness * 2 + battery_contact_flange_height + wall_thickness * 2 + wall_thickness / 2,
|
|
||||||
0])
|
|
||||||
converter_box();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
battery_case();
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
module pill(length, bevel) {
|
|
||||||
hull() {
|
|
||||||
translate([0, 0, (-length / 2) + bevel]) sphere(r = bevel);
|
|
||||||
translate([0, 0, (length / 2) - bevel]) sphere(r = bevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module box_face(dimensions, bevel = 0) {
|
|
||||||
x = dimensions[0];
|
|
||||||
y = dimensions[1];
|
|
||||||
z = dimensions[2];
|
|
||||||
|
|
||||||
if (bevel > 0) {
|
|
||||||
translate([0, 0, z / 2])
|
|
||||||
hull() {
|
|
||||||
pill(z, bevel);
|
|
||||||
translate([x, 0, 0])
|
|
||||||
pill(z, bevel);
|
|
||||||
translate([x, y, 0])
|
|
||||||
pill(z, bevel);
|
|
||||||
translate([0, y, 0])
|
|
||||||
pill(z, bevel);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cube(dimensions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module channel(length, width, height, bevel) {
|
|
||||||
union() {
|
|
||||||
box_face([length, width, wall_thickness], bevel);
|
|
||||||
|
|
||||||
translate([0, wall_thickness - bevel, bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
box_face([length, height, wall_thickness], bevel);
|
|
||||||
|
|
||||||
translate([0, width + bevel, bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
box_face([length, height, wall_thickness], bevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module box(length, width, height, bevel = 0) {
|
|
||||||
union() {
|
|
||||||
channel(length, width, height, bevel);
|
|
||||||
|
|
||||||
translate([-bevel, 0, bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
box_face([width, height, wall_thickness], bevel);
|
|
||||||
translate([length - wall_thickness + bevel, 0, bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
box_face([width, height, wall_thickness], bevel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
$fn = 50;
|
|
||||||
threshold = 0.1;
|
|
||||||
|
|
||||||
board_length = 92;
|
|
||||||
board_width = 72;
|
|
||||||
board_height = 21.5;
|
|
||||||
wall_thickness = 2;
|
|
||||||
bevel = 0.5;
|
|
||||||
|
|
||||||
hinge_radius = 2.5;
|
|
||||||
|
|
||||||
case_width = board_width + wall_thickness * 2;
|
|
||||||
case_length = board_length + wall_thickness * 2;
|
|
||||||
case_height = board_height + wall_thickness;
|
|
||||||
|
|
||||||
include <./common.scad>;
|
|
||||||
|
|
||||||
module hinge(length) {
|
|
||||||
difference() {
|
|
||||||
union() {
|
|
||||||
cylinder(h = length, r = hinge_radius);
|
|
||||||
translate([0, -hinge_radius, 0])
|
|
||||||
cube([hinge_radius, hinge_radius * 2, length]);
|
|
||||||
}
|
|
||||||
translate([0, 0, -threshold / 2]) cylinder(h = length + threshold, r = 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module main_case() {
|
|
||||||
hinge_length = board_length / 4;
|
|
||||||
hinge_y_offset = board_width + wall_thickness + hinge_radius;
|
|
||||||
hinge_z_offset = board_height;
|
|
||||||
|
|
||||||
difference() {
|
|
||||||
union() {
|
|
||||||
box(case_length,
|
|
||||||
case_width,
|
|
||||||
case_height,
|
|
||||||
bevel);
|
|
||||||
|
|
||||||
translate([0, -hinge_radius - bevel + threshold, hinge_z_offset + bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
hinge(case_length / 4);
|
|
||||||
|
|
||||||
translate([case_length - hinge_length, -hinge_radius - bevel + threshold, hinge_z_offset + bevel])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 90, 0])
|
|
||||||
hinge(case_length / 4);
|
|
||||||
|
|
||||||
translate([43, case_width, wall_thickness + 8])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 180, 0])
|
|
||||||
linear_extrude(1)
|
|
||||||
text("lights", size = 3);
|
|
||||||
|
|
||||||
translate([67, case_width, wall_thickness + 8])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 180, 0])
|
|
||||||
linear_extrude(1)
|
|
||||||
text("left", size = 3);
|
|
||||||
|
|
||||||
translate([55, case_width, wall_thickness + 8])
|
|
||||||
rotate([90, 0, 0])
|
|
||||||
rotate([0, 180, 0])
|
|
||||||
linear_extrude(1)
|
|
||||||
text("right", size = 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
color("green", 1) translate([8.5 + wall_thickness, case_width - wall_thickness - threshold, wall_thickness])
|
|
||||||
cube([60, wall_thickness * 2, 7]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module lid() {
|
|
||||||
lid_width = case_width + hinge_radius * 2 + wall_thickness;
|
|
||||||
union() {
|
|
||||||
difference() {
|
|
||||||
box_face([case_length,
|
|
||||||
lid_width,
|
|
||||||
wall_thickness],
|
|
||||||
bevel);
|
|
||||||
translate([(case_length - 60) / 2, 14 + hinge_radius * 2, -threshold / 2])
|
|
||||||
cube([60, 16, wall_thickness + threshold]);
|
|
||||||
}
|
|
||||||
|
|
||||||
translate([case_length / 4 + 1, hinge_radius - 0.4, -hinge_radius])
|
|
||||||
rotate([180, 0, 0])
|
|
||||||
rotate([0, 90, 0]) hinge(case_length / 2 - 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// main_case();
|
|
||||||
// color("red", 1) translate([0, 0, case_height + wall_thickness / 2]) lid();
|
|
||||||
// color("red", 1) translate([0, 0, 40]) lid();
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
include <./control_panel.scad>
|
|
||||||
|
|
||||||
main_case();
|
|
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
include <./control_panel.scad>
|
|
||||||
|
|
||||||
lid();
|
|
||||||
|
|
|
@ -1,481 +0,0 @@
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
use alloc::boxed::Box;
|
|
||||||
use az::*;
|
|
||||||
use core::{
|
|
||||||
clone::Clone,
|
|
||||||
cmp::PartialEq,
|
|
||||||
default::Default,
|
|
||||||
ops::{Add, Sub},
|
|
||||||
option::Option,
|
|
||||||
};
|
|
||||||
use fixed::types::{I48F16, I8F8, U128F0, U16F0};
|
|
||||||
|
|
||||||
mod patterns;
|
|
||||||
pub use patterns::*;
|
|
||||||
|
|
||||||
mod types;
|
|
||||||
pub use types::{BodyPattern, DashboardPattern, RGB};
|
|
||||||
|
|
||||||
fn calculate_frames(starting_time: U128F0, now: U128F0) -> U16F0 {
|
|
||||||
let frames_128 = (now - starting_time) / U128F0::from(FPS);
|
|
||||||
(frames_128 % U128F0::from(U16F0::MAX)).cast()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_slope(start: I8F8, end: I8F8, frames: U16F0) -> I8F8 {
|
|
||||||
let slope_i16f16 = (I48F16::from(end) - I48F16::from(start)) / I48F16::from(frames);
|
|
||||||
slope_i16f16.saturating_as()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn linear_ease(value: I8F8, frames: U16F0, slope: I8F8) -> I8F8 {
|
|
||||||
let value_i16f16 = I48F16::from(value) + I48F16::from(frames) * I48F16::from(slope);
|
|
||||||
value_i16f16.saturating_as()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
|
|
||||||
pub struct Instant(pub U128F0);
|
|
||||||
|
|
||||||
impl Default for Instant {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(U128F0::from(0 as u8))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add for Instant {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn add(self, r: Self) -> Self::Output {
|
|
||||||
Self(self.0 + r.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sub for Instant {
|
|
||||||
type Output = Self;
|
|
||||||
|
|
||||||
fn sub(self, r: Self) -> Self::Output {
|
|
||||||
Self(self.0 - r.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const FPS: u8 = 30;
|
|
||||||
|
|
||||||
pub trait UI {
|
|
||||||
fn check_event(&mut self, current_time: Instant) -> Option<Event>;
|
|
||||||
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Animation {
|
|
||||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub struct DefaultAnimation {}
|
|
||||||
|
|
||||||
impl Animation for DefaultAnimation {
|
|
||||||
fn tick(&mut self, _: Instant) -> (DashboardPattern, BodyPattern) {
|
|
||||||
(WATER_DASHBOARD, WATER_BODY)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub struct Fade {
|
|
||||||
starting_dashboard: DashboardPattern,
|
|
||||||
starting_lights: BodyPattern,
|
|
||||||
|
|
||||||
start_time: Instant,
|
|
||||||
dashboard_slope: [RGB<I8F8>; 3],
|
|
||||||
body_slope: [RGB<I8F8>; 60],
|
|
||||||
frames: U16F0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fade {
|
|
||||||
fn new(
|
|
||||||
dashboard: DashboardPattern,
|
|
||||||
lights: BodyPattern,
|
|
||||||
ending_dashboard: DashboardPattern,
|
|
||||||
ending_lights: BodyPattern,
|
|
||||||
frames: U16F0,
|
|
||||||
time: Instant,
|
|
||||||
) -> Self {
|
|
||||||
let mut dashboard_slope = [Default::default(); 3];
|
|
||||||
let mut body_slope = [Default::default(); 60];
|
|
||||||
for i in 0..3 {
|
|
||||||
let slope = RGB {
|
|
||||||
r: calculate_slope(dashboard[i].r, ending_dashboard[i].r, frames),
|
|
||||||
g: calculate_slope(dashboard[i].g, ending_dashboard[i].g, frames),
|
|
||||||
b: calculate_slope(dashboard[i].b, ending_dashboard[i].b, frames),
|
|
||||||
};
|
|
||||||
dashboard_slope[i] = slope;
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..60 {
|
|
||||||
let slope = RGB {
|
|
||||||
r: calculate_slope(lights[i].r, ending_lights[i].r, frames),
|
|
||||||
g: calculate_slope(lights[i].g, ending_lights[i].g, frames),
|
|
||||||
b: calculate_slope(lights[i].b, ending_lights[i].b, frames),
|
|
||||||
};
|
|
||||||
body_slope[i] = slope;
|
|
||||||
}
|
|
||||||
|
|
||||||
Self {
|
|
||||||
starting_dashboard: dashboard,
|
|
||||||
starting_lights: lights,
|
|
||||||
start_time: time,
|
|
||||||
dashboard_slope,
|
|
||||||
body_slope,
|
|
||||||
frames,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Animation for Fade {
|
|
||||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) {
|
|
||||||
let mut frames = calculate_frames(self.start_time.0, time.0);
|
|
||||||
if frames > self.frames {
|
|
||||||
frames = self.frames
|
|
||||||
}
|
|
||||||
let mut dashboard_pattern: DashboardPattern = OFF_DASHBOARD;
|
|
||||||
let mut body_pattern: BodyPattern = OFF_BODY;
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
dashboard_pattern[i].r = linear_ease(
|
|
||||||
self.starting_dashboard[i].r,
|
|
||||||
frames,
|
|
||||||
self.dashboard_slope[i].r,
|
|
||||||
);
|
|
||||||
dashboard_pattern[i].g = linear_ease(
|
|
||||||
self.starting_dashboard[i].g,
|
|
||||||
frames,
|
|
||||||
self.dashboard_slope[i].g,
|
|
||||||
);
|
|
||||||
dashboard_pattern[i].b = linear_ease(
|
|
||||||
self.starting_dashboard[i].b,
|
|
||||||
frames,
|
|
||||||
self.dashboard_slope[i].b,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..60 {
|
|
||||||
body_pattern[i].r =
|
|
||||||
linear_ease(self.starting_lights[i].r, frames, self.body_slope[i].r);
|
|
||||||
body_pattern[i].g =
|
|
||||||
linear_ease(self.starting_lights[i].g, frames, self.body_slope[i].g);
|
|
||||||
body_pattern[i].b =
|
|
||||||
linear_ease(self.starting_lights[i].b, frames, self.body_slope[i].b);
|
|
||||||
}
|
|
||||||
|
|
||||||
(dashboard_pattern, body_pattern)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum FadeDirection {
|
|
||||||
Transition,
|
|
||||||
FadeIn,
|
|
||||||
FadeOut,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum BlinkerDirection {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Blinker {
|
|
||||||
transition: Fade,
|
|
||||||
fade_in: Fade,
|
|
||||||
fade_out: Fade,
|
|
||||||
direction: FadeDirection,
|
|
||||||
|
|
||||||
start_time: Instant,
|
|
||||||
frames: U16F0,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Blinker {
|
|
||||||
fn new(
|
|
||||||
starting_dashboard: DashboardPattern,
|
|
||||||
starting_body: BodyPattern,
|
|
||||||
direction: BlinkerDirection,
|
|
||||||
time: Instant,
|
|
||||||
) -> Self {
|
|
||||||
let mut ending_dashboard = OFF_DASHBOARD.clone();
|
|
||||||
|
|
||||||
match direction {
|
|
||||||
BlinkerDirection::Left => {
|
|
||||||
ending_dashboard[0].r = LEFT_BLINKER_DASHBOARD[0].r;
|
|
||||||
ending_dashboard[0].g = LEFT_BLINKER_DASHBOARD[0].g;
|
|
||||||
ending_dashboard[0].b = LEFT_BLINKER_DASHBOARD[0].b;
|
|
||||||
}
|
|
||||||
BlinkerDirection::Right => {
|
|
||||||
ending_dashboard[2].r = RIGHT_BLINKER_DASHBOARD[2].r;
|
|
||||||
ending_dashboard[2].g = RIGHT_BLINKER_DASHBOARD[2].g;
|
|
||||||
ending_dashboard[2].b = RIGHT_BLINKER_DASHBOARD[2].b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ending_body = OFF_BODY.clone();
|
|
||||||
match direction {
|
|
||||||
BlinkerDirection::Left => {
|
|
||||||
for i in 0..30 {
|
|
||||||
ending_body[i].r = LEFT_BLINKER_BODY[i].r;
|
|
||||||
ending_body[i].g = LEFT_BLINKER_BODY[i].g;
|
|
||||||
ending_body[i].b = LEFT_BLINKER_BODY[i].b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BlinkerDirection::Right => {
|
|
||||||
for i in 30..60 {
|
|
||||||
ending_body[i].r = RIGHT_BLINKER_BODY[i].r;
|
|
||||||
ending_body[i].g = RIGHT_BLINKER_BODY[i].g;
|
|
||||||
ending_body[i].b = RIGHT_BLINKER_BODY[i].b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Blinker {
|
|
||||||
transition: Fade::new(
|
|
||||||
starting_dashboard.clone(),
|
|
||||||
starting_body.clone(),
|
|
||||||
ending_dashboard.clone(),
|
|
||||||
ending_body.clone(),
|
|
||||||
BLINKER_FRAMES,
|
|
||||||
time,
|
|
||||||
),
|
|
||||||
fade_in: Fade::new(
|
|
||||||
OFF_DASHBOARD.clone(),
|
|
||||||
OFF_BODY.clone(),
|
|
||||||
ending_dashboard.clone(),
|
|
||||||
ending_body.clone(),
|
|
||||||
BLINKER_FRAMES,
|
|
||||||
time,
|
|
||||||
),
|
|
||||||
fade_out: Fade::new(
|
|
||||||
ending_dashboard.clone(),
|
|
||||||
ending_body.clone(),
|
|
||||||
OFF_DASHBOARD.clone(),
|
|
||||||
OFF_BODY.clone(),
|
|
||||||
BLINKER_FRAMES,
|
|
||||||
time,
|
|
||||||
),
|
|
||||||
direction: FadeDirection::Transition,
|
|
||||||
start_time: time,
|
|
||||||
frames: BLINKER_FRAMES,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Animation for Blinker {
|
|
||||||
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern) {
|
|
||||||
let frames = calculate_frames(self.start_time.0, time.0);
|
|
||||||
if frames > self.frames {
|
|
||||||
match self.direction {
|
|
||||||
FadeDirection::Transition => {
|
|
||||||
self.direction = FadeDirection::FadeOut;
|
|
||||||
self.fade_out.start_time = time;
|
|
||||||
}
|
|
||||||
FadeDirection::FadeIn => {
|
|
||||||
self.direction = FadeDirection::FadeOut;
|
|
||||||
self.fade_out.start_time = time;
|
|
||||||
}
|
|
||||||
FadeDirection::FadeOut => {
|
|
||||||
self.direction = FadeDirection::FadeIn;
|
|
||||||
self.fade_in.start_time = time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.start_time = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.direction {
|
|
||||||
FadeDirection::Transition => self.transition.tick(time),
|
|
||||||
FadeDirection::FadeIn => self.fade_in.tick(time),
|
|
||||||
FadeDirection::FadeOut => self.fade_out.tick(time),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Event {
|
|
||||||
Brake,
|
|
||||||
BrakeRelease,
|
|
||||||
LeftBlinker,
|
|
||||||
NextPattern,
|
|
||||||
PreviousPattern,
|
|
||||||
RightBlinker,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
pub enum Pattern {
|
|
||||||
Water,
|
|
||||||
GayPride,
|
|
||||||
TransPride,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pattern {
|
|
||||||
fn previous(&self) -> Pattern {
|
|
||||||
match self {
|
|
||||||
Pattern::Water => Pattern::TransPride,
|
|
||||||
Pattern::GayPride => Pattern::Water,
|
|
||||||
Pattern::TransPride => Pattern::GayPride,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next(&self) -> Pattern {
|
|
||||||
match self {
|
|
||||||
Pattern::Water => Pattern::GayPride,
|
|
||||||
Pattern::GayPride => Pattern::TransPride,
|
|
||||||
Pattern::TransPride => Pattern::Water,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dashboard(&self) -> DashboardPattern {
|
|
||||||
match self {
|
|
||||||
Pattern::Water => WATER_DASHBOARD,
|
|
||||||
Pattern::GayPride => PRIDE_DASHBOARD,
|
|
||||||
Pattern::TransPride => TRANS_PRIDE_DASHBOARD,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn body(&self) -> BodyPattern {
|
|
||||||
match self {
|
|
||||||
Pattern::Water => OFF_BODY,
|
|
||||||
Pattern::GayPride => PRIDE_BODY,
|
|
||||||
Pattern::TransPride => TRANS_PRIDE_BODY,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum State {
|
|
||||||
Pattern(Pattern),
|
|
||||||
Brake,
|
|
||||||
LeftBlinker,
|
|
||||||
RightBlinker,
|
|
||||||
BrakeLeftBlinker,
|
|
||||||
BrakeRightBlinker,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct App {
|
|
||||||
ui: Box<dyn UI>,
|
|
||||||
state: State,
|
|
||||||
home_pattern: Pattern,
|
|
||||||
current_animation: Box<dyn Animation>,
|
|
||||||
dashboard_lights: DashboardPattern,
|
|
||||||
lights: BodyPattern,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl App {
|
|
||||||
pub fn new(ui: Box<dyn UI>) -> Self {
|
|
||||||
let pattern = Pattern::Water;
|
|
||||||
Self {
|
|
||||||
ui,
|
|
||||||
state: State::Pattern(pattern),
|
|
||||||
home_pattern: pattern,
|
|
||||||
current_animation: Box::new(Fade::new(
|
|
||||||
OFF_DASHBOARD,
|
|
||||||
OFF_BODY,
|
|
||||||
pattern.dashboard(),
|
|
||||||
pattern.body(),
|
|
||||||
DEFAULT_FRAMES,
|
|
||||||
Instant((0 as u32).into()),
|
|
||||||
)),
|
|
||||||
dashboard_lights: OFF_DASHBOARD,
|
|
||||||
lights: OFF_BODY,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_animation(&mut self, time: Instant) {
|
|
||||||
match self.state {
|
|
||||||
State::Pattern(ref pattern) => {
|
|
||||||
self.current_animation = Box::new(Fade::new(
|
|
||||||
self.dashboard_lights.clone(),
|
|
||||||
self.lights.clone(),
|
|
||||||
pattern.dashboard(),
|
|
||||||
pattern.body(),
|
|
||||||
DEFAULT_FRAMES,
|
|
||||||
time,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
State::Brake => {
|
|
||||||
self.current_animation = Box::new(Fade::new(
|
|
||||||
self.dashboard_lights.clone(),
|
|
||||||
self.lights.clone(),
|
|
||||||
BRAKES_DASHBOARD,
|
|
||||||
BRAKES_BODY,
|
|
||||||
BRAKES_FRAMES,
|
|
||||||
time,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
State::LeftBlinker => {
|
|
||||||
self.current_animation = Box::new(Blinker::new(
|
|
||||||
self.dashboard_lights.clone(),
|
|
||||||
self.lights.clone(),
|
|
||||||
BlinkerDirection::Left,
|
|
||||||
time,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
State::RightBlinker => {
|
|
||||||
self.current_animation = Box::new(Blinker::new(
|
|
||||||
self.dashboard_lights.clone(),
|
|
||||||
self.lights.clone(),
|
|
||||||
BlinkerDirection::Right,
|
|
||||||
time,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
State::BrakeLeftBlinker => (),
|
|
||||||
State::BrakeRightBlinker => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_state(&mut self, event: Event) {
|
|
||||||
match event {
|
|
||||||
Event::Brake => {
|
|
||||||
if self.state == State::Brake {
|
|
||||||
self.state = State::Pattern(self.home_pattern);
|
|
||||||
} else {
|
|
||||||
self.state = State::Brake;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Event::BrakeRelease => self.state = State::Pattern(self.home_pattern),
|
|
||||||
Event::LeftBlinker => match self.state {
|
|
||||||
State::Brake => self.state = State::BrakeLeftBlinker,
|
|
||||||
State::BrakeLeftBlinker => self.state = State::Brake,
|
|
||||||
State::LeftBlinker => self.state = State::Pattern(self.home_pattern),
|
|
||||||
_ => self.state = State::LeftBlinker,
|
|
||||||
},
|
|
||||||
Event::NextPattern => match self.state {
|
|
||||||
State::Pattern(ref pattern) => {
|
|
||||||
self.home_pattern = pattern.next();
|
|
||||||
self.state = State::Pattern(self.home_pattern);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
Event::PreviousPattern => match self.state {
|
|
||||||
State::Pattern(ref pattern) => {
|
|
||||||
self.home_pattern = pattern.previous();
|
|
||||||
self.state = State::Pattern(self.home_pattern);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
},
|
|
||||||
Event::RightBlinker => match self.state {
|
|
||||||
State::Brake => self.state = State::BrakeRightBlinker,
|
|
||||||
State::BrakeRightBlinker => self.state = State::Brake,
|
|
||||||
State::RightBlinker => self.state = State::Pattern(self.home_pattern),
|
|
||||||
_ => self.state = State::RightBlinker,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick(&mut self, time: Instant) {
|
|
||||||
match self.ui.check_event(time) {
|
|
||||||
Some(event) => {
|
|
||||||
self.update_state(event);
|
|
||||||
self.update_animation(time);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (dashboard, lights) = self.current_animation.tick(time);
|
|
||||||
self.dashboard_lights = dashboard.clone();
|
|
||||||
self.lights = lights.clone();
|
|
||||||
self.ui.update_lights(dashboard, lights);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,333 +0,0 @@
|
||||||
use crate::{BodyPattern, DashboardPattern, RGB};
|
|
||||||
use fixed::types::{I8F8, U16F0};
|
|
||||||
|
|
||||||
pub const RGB_OFF: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0"),
|
|
||||||
g: I8F8::lit("0"),
|
|
||||||
b: I8F8::lit("0"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const RGB_WHITE: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("1"),
|
|
||||||
g: I8F8::lit("1"),
|
|
||||||
b: I8F8::lit("1"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BRAKES_RED: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("1"),
|
|
||||||
g: I8F8::lit("0"),
|
|
||||||
b: I8F8::lit("0"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const BLINKER_AMBER: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("1"),
|
|
||||||
g: I8F8::lit("0.15"),
|
|
||||||
b: I8F8::lit("0"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_RED: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.95"),
|
|
||||||
g: I8F8::lit("0.00"),
|
|
||||||
b: I8F8::lit("0.00"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_ORANGE: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("1.0"),
|
|
||||||
g: I8F8::lit("0.25"),
|
|
||||||
b: I8F8::lit("0"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_YELLOW: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("1.0"),
|
|
||||||
g: I8F8::lit("0.85"),
|
|
||||||
b: I8F8::lit("0"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_GREEN: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0"),
|
|
||||||
g: I8F8::lit("0.95"),
|
|
||||||
b: I8F8::lit("0.05"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_INDIGO: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.04"),
|
|
||||||
g: I8F8::lit("0.15"),
|
|
||||||
b: I8F8::lit("0.55"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PRIDE_VIOLET: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.75"),
|
|
||||||
g: I8F8::lit("0.0"),
|
|
||||||
b: I8F8::lit("0.80"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TRANS_BLUE: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.06"),
|
|
||||||
g: I8F8::lit("0.41"),
|
|
||||||
b: I8F8::lit("0.98"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const TRANS_PINK: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.96"),
|
|
||||||
g: I8F8::lit("0.16"),
|
|
||||||
b: I8F8::lit("0.32"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const WATER_1: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.0"),
|
|
||||||
g: I8F8::lit("0.0"),
|
|
||||||
b: I8F8::lit("0.75"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const WATER_2: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.8"),
|
|
||||||
g: I8F8::lit("0.8"),
|
|
||||||
b: I8F8::lit("0.8"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const WATER_3: RGB<I8F8> = RGB {
|
|
||||||
r: I8F8::lit("0.00"),
|
|
||||||
g: I8F8::lit("0.75"),
|
|
||||||
b: I8F8::lit("0.75"),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const OFF_DASHBOARD: DashboardPattern = [RGB_OFF; 3];
|
|
||||||
pub const OFF_BODY: BodyPattern = [RGB_OFF; 60];
|
|
||||||
|
|
||||||
pub const DEFAULT_FRAMES: U16F0 = U16F0::lit("30");
|
|
||||||
|
|
||||||
pub const WATER_DASHBOARD: DashboardPattern = [WATER_1, WATER_2, WATER_3];
|
|
||||||
|
|
||||||
pub const WATER_BODY: BodyPattern = [RGB_OFF; 60];
|
|
||||||
|
|
||||||
pub const PRIDE_DASHBOARD: DashboardPattern = [PRIDE_RED, PRIDE_GREEN, PRIDE_INDIGO];
|
|
||||||
|
|
||||||
pub const PRIDE_BODY: BodyPattern = [
|
|
||||||
// Left Side
|
|
||||||
// Red
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
// Orange
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
// Yellow
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
// Green
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
// Indigo
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
// Violet
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
// Right Side
|
|
||||||
// Violet
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
PRIDE_VIOLET,
|
|
||||||
// Indigo
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
PRIDE_INDIGO,
|
|
||||||
// Green
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
PRIDE_GREEN,
|
|
||||||
// Yellow
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
PRIDE_YELLOW,
|
|
||||||
// Orange
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
PRIDE_ORANGE,
|
|
||||||
// Red
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
PRIDE_RED,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub const TRANS_PRIDE_DASHBOARD: DashboardPattern = [TRANS_BLUE, RGB_WHITE, TRANS_PINK];
|
|
||||||
|
|
||||||
pub const TRANS_PRIDE_BODY: BodyPattern = [
|
|
||||||
// Left Side
|
|
||||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_PINK, TRANS_PINK,
|
|
||||||
TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, RGB_WHITE, RGB_WHITE, RGB_WHITE, RGB_WHITE,
|
|
||||||
RGB_WHITE, RGB_WHITE, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK,
|
|
||||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE,
|
|
||||||
// Right side
|
|
||||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_PINK, TRANS_PINK,
|
|
||||||
TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, RGB_WHITE, RGB_WHITE, RGB_WHITE, RGB_WHITE,
|
|
||||||
RGB_WHITE, RGB_WHITE, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK, TRANS_PINK,
|
|
||||||
TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE, TRANS_BLUE,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub const BRAKES_FRAMES: U16F0 = U16F0::lit("15");
|
|
||||||
|
|
||||||
pub const BRAKES_DASHBOARD: DashboardPattern = [BRAKES_RED; 3];
|
|
||||||
|
|
||||||
pub const BRAKES_BODY: BodyPattern = [BRAKES_RED; 60];
|
|
||||||
|
|
||||||
pub const BLINKER_FRAMES: U16F0 = U16F0::lit("10");
|
|
||||||
|
|
||||||
pub const LEFT_BLINKER_DASHBOARD: DashboardPattern = [BLINKER_AMBER, RGB_OFF, RGB_OFF];
|
|
||||||
|
|
||||||
pub const LEFT_BLINKER_BODY: BodyPattern = [
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub const RIGHT_BLINKER_DASHBOARD: DashboardPattern = [RGB_OFF, RGB_OFF, BLINKER_AMBER];
|
|
||||||
|
|
||||||
pub const RIGHT_BLINKER_BODY: BodyPattern = [
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
RGB_OFF,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
BLINKER_AMBER,
|
|
||||||
];
|
|
|
@ -1,17 +0,0 @@
|
||||||
use core::default::Default;
|
|
||||||
use fixed::types::I8F8;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Default, Debug)]
|
|
||||||
pub struct RGB<T> {
|
|
||||||
pub r: T,
|
|
||||||
pub g: T,
|
|
||||||
pub b: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
const DASHBOARD_LIGHT_COUNT: usize = 3;
|
|
||||||
|
|
||||||
pub type DashboardPattern = [RGB<I8F8>; DASHBOARD_LIGHT_COUNT];
|
|
||||||
|
|
||||||
const BODY_LIGHT_COUNT: usize = 60;
|
|
||||||
|
|
||||||
pub type BodyPattern = [RGB<I8F8>; BODY_LIGHT_COUNT];
|
|
|
@ -1,16 +0,0 @@
|
||||||
[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" ] }
|
|
||||||
lights-core = { path = "../core" }
|
|
||||||
pango = { version = "*" }
|
|
|
@ -1,288 +0,0 @@
|
||||||
use adw::prelude::*;
|
|
||||||
use fixed::types::{I8F8, U128F0};
|
|
||||||
use glib::{Object, Sender};
|
|
||||||
use gtk::subclass::prelude::*;
|
|
||||||
use lights_core::{
|
|
||||||
App, BodyPattern, DashboardPattern, Event, Instant, FPS, OFF_BODY, OFF_DASHBOARD, RGB, UI,
|
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
cell::RefCell,
|
|
||||||
env,
|
|
||||||
rc::Rc,
|
|
||||||
sync::mpsc::{Receiver, TryRecvError},
|
|
||||||
};
|
|
||||||
|
|
||||||
const WIDTH: i32 = 640;
|
|
||||||
const HEIGHT: i32 = 480;
|
|
||||||
|
|
||||||
pub struct Update {
|
|
||||||
dashboard: DashboardPattern,
|
|
||||||
lights: BodyPattern,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DashboardLightsPrivate {
|
|
||||||
lights: Rc<RefCell<DashboardPattern>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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(OFF_DASHBOARD)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for DashboardLightsPrivate {}
|
|
||||||
impl WidgetImpl for DashboardLightsPrivate {}
|
|
||||||
impl DrawingAreaImpl for DashboardLightsPrivate {}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct DashboardLights(ObjectSubclass<DashboardLightsPrivate>) @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.into(),
|
|
||||||
lights[i].g.into(),
|
|
||||||
lights[i].b.into(),
|
|
||||||
);
|
|
||||||
context.rectangle(start + 100. * i as f64, 10., 80., 80.);
|
|
||||||
let _ = context.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_lights(&self, lights: DashboardPattern) {
|
|
||||||
*self.imp().lights.borrow_mut() = lights;
|
|
||||||
self.queue_draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct BikeLightsPrivate {
|
|
||||||
lights: Rc<RefCell<BodyPattern>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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(OFF_BODY)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjectImpl for BikeLightsPrivate {}
|
|
||||||
impl WidgetImpl for BikeLightsPrivate {}
|
|
||||||
impl DrawingAreaImpl for BikeLightsPrivate {}
|
|
||||||
|
|
||||||
glib::wrapper! {
|
|
||||||
pub struct BikeLights(ObjectSubclass<BikeLightsPrivate>) @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(640);
|
|
||||||
|
|
||||||
let center = WIDTH as f64 / 2.;
|
|
||||||
|
|
||||||
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.into(),
|
|
||||||
lights[i].g.into(),
|
|
||||||
lights[i].b.into(),
|
|
||||||
);
|
|
||||||
context.rectangle(center - 45., 5. + 20. * i as f64, 15., 15.);
|
|
||||||
let _ = context.fill();
|
|
||||||
}
|
|
||||||
for i in 0..30 {
|
|
||||||
context.set_source_rgb(
|
|
||||||
lights[i + 30].r.into(),
|
|
||||||
lights[i + 30].g.into(),
|
|
||||||
lights[i + 30].b.into(),
|
|
||||||
);
|
|
||||||
context.rectangle(center + 15., 5. + 20. * (30. - (i + 1) as f64), 15., 15.);
|
|
||||||
let _ = context.fill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_lights(&self, lights: [RGB<I8F8>; 60]) {
|
|
||||||
*self.imp().lights.borrow_mut() = lights;
|
|
||||||
self.queue_draw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GTKUI {
|
|
||||||
tx: Sender<Update>,
|
|
||||||
rx: Receiver<Event>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UI for GTKUI {
|
|
||||||
fn check_event(&mut self, _: Instant) -> Option<Event> {
|
|
||||||
match self.rx.try_recv() {
|
|
||||||
Ok(event) => Some(event),
|
|
||||||
Err(TryRecvError::Empty) => None,
|
|
||||||
Err(TryRecvError::Disconnected) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_lights(&self, dashboard_lights: DashboardPattern, lights: BodyPattern) {
|
|
||||||
self.tx
|
|
||||||
.send(Update {
|
|
||||||
dashboard: dashboard_lights,
|
|
||||||
lights,
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let adw_app = adw::Application::builder()
|
|
||||||
.application_id("com.luminescent-dreams.bike-light-simulator")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
adw_app.connect_activate(move |adw_app| {
|
|
||||||
let (update_tx, update_rx) =
|
|
||||||
gtk::glib::MainContext::channel::<Update>(gtk::glib::Priority::DEFAULT);
|
|
||||||
let (event_tx, event_rx) = std::sync::mpsc::channel();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let mut bike_app = App::new(Box::new(GTKUI {
|
|
||||||
tx: update_tx,
|
|
||||||
rx: event_rx,
|
|
||||||
}));
|
|
||||||
loop {
|
|
||||||
bike_app.tick(Instant(U128F0::from(
|
|
||||||
std::time::SystemTime::now()
|
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
|
||||||
.unwrap()
|
|
||||||
.as_millis(),
|
|
||||||
)));
|
|
||||||
std::thread::sleep(std::time::Duration::from_millis(1000 / (FPS as u64)));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let window = adw::ApplicationWindow::builder()
|
|
||||||
.application(adw_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 event_tx = event_tx.clone();
|
|
||||||
move |_| {
|
|
||||||
let _ = event_tx.send(Event::LeftBlinker);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
brake_button.connect_clicked({
|
|
||||||
let event_tx = event_tx.clone();
|
|
||||||
move |_| {
|
|
||||||
let _ = event_tx.send(Event::Brake);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
right_button.connect_clicked({
|
|
||||||
let event_tx = event_tx.clone();
|
|
||||||
move |_| {
|
|
||||||
let _ = event_tx.send(Event::RightBlinker);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
previous_pattern.connect_clicked({
|
|
||||||
let event_tx = event_tx.clone();
|
|
||||||
move |_| {
|
|
||||||
let _ = event_tx.send(Event::PreviousPattern);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
next_pattern.connect_clicked({
|
|
||||||
let event_tx = event_tx.clone();
|
|
||||||
move |_| {
|
|
||||||
let _ = event_tx.send(Event::NextPattern);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pattern_controls.append(&previous_pattern);
|
|
||||||
pattern_controls.append(&next_pattern);
|
|
||||||
layout.append(&pattern_controls);
|
|
||||||
|
|
||||||
layout.append(&dashboard_lights);
|
|
||||||
layout.append(&bike_lights);
|
|
||||||
|
|
||||||
update_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<String> = env::args().collect();
|
|
||||||
ApplicationExtManual::run_with_args(&adw_app, &args);
|
|
||||||
}
|
|
|
@ -35,7 +35,7 @@ macro_rules! define_config {
|
||||||
$($name($struct)),+
|
$($name($struct)),+
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
values: std::collections::HashMap<ConfigName, ConfigOption>,
|
values: std::collections::HashMap<ConfigName, ConfigOption>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,519 +0,0 @@
|
||||||
{
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#addr2line@0.21.0": "1jx0k3iwyqr8klqbzk6kjvr496yd94aspis10vwsj5wy7gib4c4a",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#adler32@1.2.0": "0d7jq7jsjyhsgbhnfq5fvrlh9j0i9g1fqrl2735ibv5f75yjgqda",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#adler@1.0.2": "1zim79cvzd5yrkzl3nyfx0avijwgk9fqv3yrscdy1cc79ih02qpj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.6": "0yn9i8nc6mmv28ig9w3dga571q09vg9f1f650mi5z8phx42r6hli",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.2": "1w510wnixvlgimkx1zjbvlxh6xps2vjgfqgwf5a6adlbjp5rv5mj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.16": "1iayppgq4wqbfbfcqmsbwgamj0s65012sskfvyx07pxavk3gyhh9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#android-tzdata@0.1.1": "1w7ynjxrfs97xg3qlcdns4kgfpwcdv824g611fq32cag4cdr96g9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#android_system_properties@0.1.5": "04b3wrz12837j7mdczqd95b732gw5q7q66cv4yn4646lvccp57l1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#annotate-snippets@0.9.2": "07p8r6jzb7nqydq0kr5pllckqcdxlyld2g275v425axnzffpxbyc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstream@0.6.5": "1dm1mdbs1x6y3m3pz0qlamgiskb50i4q859676kx0pz8r8pajr6n",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle-parse@0.2.3": "134jhzrz89labrdwxxnjxqjdg06qvaflj1wkfnmyapwyldfwcnn7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle-query@1.0.2": "0j3na4b1nma39g4x7cwvj009awxckjf3z2vkwhldgka44hqj72g2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle-wincon@3.0.2": "19v0fv400bmp4niqpzxnhg83vz12mmqv7l2l8vi80qcdxj0lpm8w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anstyle@1.0.4": "11yxw02b6parn29s757z96rgiqbn8qy0fk9a3p3bhczm85dhfybh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#anyhow@1.0.75": "1rmcjkim91c5mw7h9wn8nv0k6x118yz0xg0z1q18svgn42mqqrm4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#arrayvec@0.7.4": "04b7n722jij0v3fnm3qk072d5ysc2q30rl9fz33zpfhzah30mlwn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-channel@1.9.0": "0dbdlkzlncbibd3ij6y6jmvjd0cmdn48ydcfdpfhw09njd93r5c1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-channel@2.1.1": "1337ywc1paw03rdlwh100kh8pa0zyp0nrlya8bpsn6zdqi5kz8qw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-executor@1.8.0": "0z7rpayidhdqs4sdzjhh26z5155c1n94fycqni9793n4zjz5xbhp",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-global-executor@2.4.1": "1762s45cc134d38rrv0hyp41hv4iv6nmx59vswid2p0il8rvdc85",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-io@2.3.1": "0rggn074kbqxxajci1aq14b17gp75rw9l6rpbazcv9q0bc6ap5wg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-lock@3.3.0": "0yxflkfw46rad4lv86f59b5z555dlfmg1riz1n8830rgi0qb8d6h",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-std@1.13.0": "059nbiyijwbndyrz0050skvlvzhds0dmnl0biwmxwbw055glfd66",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-task@4.7.0": "16975vx6aqy5yf16fs9xz5vx1zq8mwkzfmykvcilc1j7b6c6xczv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#async-trait@0.1.77": "1adf1jh2yg39rkpmqjqyr9xyd6849p0d95425i6imgbhx0syx069",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#atoi@2.0.0": "0a05h42fggmy7h0ajjv6m7z72l924i7igbx13hk9d8pyign9k3gj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#atomic-waker@1.1.2": "1h5av1lw56m0jf0fd3bchxq8a30xv0b4wv8s4zkp4s0i7mfvs18m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#atomic-write-file@0.1.2": "0dl4x0srdwjxm3zz3fj1c7m44i3b7mjiad550fqklj1n4bfbxkgd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#autocfg@0.1.8": "0y4vw4l4izdxq1v0rrhvmlbqvalrqrmk60v1z0dqlgnlbzkl7phd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#autocfg@1.1.0": "1ylp3cb47ylzabimazvbz9ms6ap784zhb6syaz6c1jqpmcmq0s6l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#az@1.2.1": "0ww9k1w3al7x5qmb7f13v3s9c2pg1pdxbs8xshqy6zyrchj4qzkv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#backtrace@0.3.69": "0dsq23dhw4pfndkx2nsa1ml2g31idm7ss7ljxp8d57avygivg290",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bare-metal@0.2.5": "1cy5pbb92fznnri72y6drfpjxj4qdmd62f0rrlgy70dxlppn9ssx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#base64@0.21.5": "1y8x2xs9nszj5ix7gg4ycn5a6wy7ca74zxwqri3bdqzdjha6lqrm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#base64@0.9.3": "0hs62r35bgxslawyrn1vp9rmvrkkm76fqv0vqcwd048vs876r7a8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#base64ct@1.6.0": "0nvdba4jb8aikv60az40x2w1y96sjdq8z3yp09rwzmkhiwv1lg4c",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bindgen@0.69.4": "18194611hn3k1dkxlha7a52sr8vmfhl9blc54xhj08cahd8wh3d0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bit-set@0.5.3": "1wcm9vxi00ma4rcxkl3pzzjli6ihrpn9cfdi0c5b4cvga2mxs007",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bit-vec@0.6.3": "1ywqjnv60cdh1slhz67psnp422md6jdliji6alq0gmly2xm9p7rl",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bit_field@0.10.2": "0qav5rpm4hqc33vmf4vc4r0mh51yjx5vmd9zhih26n9yjs3730nw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bitfield@0.13.2": "06g7jb5r2b856vnhx76081fg90jvmy61kjqcfjysgmd5hclvvbs6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bitflags@1.3.2": "12ki6w8gn1ldq7yz9y680llwk5gmrhrzszaa17g1sbrw2r2qvwxy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bitflags@2.4.1": "01ryy3kd671b0ll4bhdvhsz67vwz1lz53fz504injrd7wpv64xrj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#block-buffer@0.10.4": "0w9sa2ypmrsqqvc20nhwr75wbb5cjr4kkyhpjm1z1lv2kdicfy1h",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#blocking@1.5.1": "064i3d6b8ln34fgdw49nmx9m36bwi3r3nv8c9xhcrpf4ilz92dva",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#build_html@2.4.0": "188nibbsv33vgjjiq9cn2irsgdb75gxfipavcavnyydcwxpzw21i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.14.0": "1v4arnv9kwk54v5d0qqpv4vyw2sgr660nk0w3apzixi1cm3yfc3z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bytemuck@1.14.0": "1ik1ma5n3bg700skkzhx50zjk7kj7mbsphi773if17l04pn2hk9p",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#byteorder@1.5.0": "0jzncxyf404mwqdbspihyzpkndfgda450l0893pz5xj685cg5l0z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#bytes@1.5.0": "08w2i8ac912l8vlvkv3q51cd4gr09pwlg3sjsjffcizlrb0i5gd2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cairo-rs@0.18.3": "18d80lk853bjhx36rjaj78clzfjrmlgi01863drnmshdgxi16dpk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cairo-sys-rs@0.18.2": "0lfsxl7ylw3phbnwmz3k58j1gnqi6kc2hdc7g3bb7f4hwnl9yp38",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cc@1.0.83": "1l643zidlb5iy1dskc5ggqs4wqa29a02f44piczqc8zcnsq4y5zi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cexpr@0.6.0": "0rl77bwhs5p979ih4r0202cn5jrfsrbgrksp40lkfz5vk1x3ib3g",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cfg-expr@0.15.5": "1cqicd9qi8mzzgh63dw03zhbdihqfl3lbiklrkynyzkq67s5m483",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0": "1za0vb97n4brpzpv8lsbnzmq5r8f2b0cpqqr0sy8h5bn751xxwds",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#chrono-tz-build@0.2.1": "03rmzd69cn7fp0fgkjr5042b3g54s2l941afjm3001ls7kqkjgj3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#chrono-tz@0.8.4": "0xhd3dsfs72im0sbc7w889lfy7bxgjlbvqhj5a1yvxhxwb08acg2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#chrono@0.4.31": "0f6vg67pipm8cziad2yms6a639pssnvysk1m05dd9crymmdnhb3z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clang-sys@1.8.1": "1x1r9yqss76z8xwpdanw313ss6fniwc1r7dzb5ycjn0ph53kj0hb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap@4.4.11": "1wj5gb2fnqls00zfahg3490bdfc36d9cwpl80qjacb5jyrqzdbxz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_builder@4.4.11": "1fxdsmw1ilgswz3lg2hjlvsdyyz04k78scjirlbd7c9bc83ba5m2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_derive@4.4.7": "0hk4hcxl56qwqsf4hmf7c0gr19r9fbxk0ah2bgkr36pmmaph966g",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#clap_lex@0.6.0": "1l8bragdvim7mva9flvd159dskn2bdkpl0jqrr41wnjfn8pcfbvh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cloudabi@0.0.3": "0kxcg83jlihy0phnd2g8c2c303px3l2p3pkjz357ll6llnd5pz6x",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#color_quant@1.1.0": "12q1n427h2bbmmm1mnglr57jaz2dj9apk0plcxw7nwqiai7qjyrx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#colorchoice@1.0.0": "1ix7w85kwvyybwi2jdkl3yva2r2bvdcc3ka2grjfzfgrapqimgxc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#concurrent-queue@2.4.0": "0qvk23ynj311adb4z7v89wk3bs65blps4n24q8rgl23vjk6lhq6i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#const-oid@0.9.6": "1y0jnqaq7p2wvspnx7qj76m7hjcqpz73qzvr9l2p9n2s51vr6if2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#convert_case@0.6.0": "1jn1pq6fp3rri88zyw6jlhwwgf6qiyc08d6gjv0qypgkl862n67c",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cookie-factory@0.3.3": "18mka6fk3843qq3jw1fdfvzyv05kx7kcmirfbs2vg2kbw9qzm1cq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cookie@0.17.0": "096c52jg9iq4lfcps2psncswv33fc30mmnaa2sbzzcfcw71kgyvy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cool_asserts@2.0.3": "1v18dg7ifx41k2f82j3gsnpm1fg9wk5s4zv7sf42c7pnad72b7zf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#core-foundation-sys@0.8.6": "13w6sdf06r0hn7bx2b45zxsg1mm2phz34jikm6xc5qrbr6djpsh6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#core-foundation@0.9.4": "13zvbbj07yk3b61b8fhwfzhy35535a583irf23vlcg59j7h9bqci",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cortex-m-rt-macros@0.7.0": "1iyki0wq8pj0qbjhw1mbq5njraihhyr7ydcbqzdzwg10dziz7xph",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cortex-m-rt@0.7.3": "1cfxg502gvcmaczmaij5maxbvaxnda5w6gp14cbin44ksl9yi17f",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cortex-m@0.7.7": "1fbca698v4gv57mv5fc48jrz8wcy6sv675n6fsrsah4qykc11ilf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#cpufeatures@0.2.11": "1l0gzsyy576n017g9bf0vkv5hhg9cpz1h1libxyfdlzcgbh0yhnf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crc-any@2.5.0": "0wzs26q5cf29fhfnrkrjsr8dpai0rlm4im8b53by8rbrbzzwjbm6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crc-catalog@2.4.0": "1xg7sz82w3nxp1jfn425fvn1clvbzb3zgblmxsyqpys0dckp9lqr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crc32fast@1.3.2": "03c8f29yx293yf43xar946xbls1g60c207m9drf8ilqhr25vsh5m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crc@3.0.1": "1zkx87a5x06xfd6xm5956w4vmdfs0wcxpsn7iwj5jbp2rcapmv46",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#critical-section@1.1.2": "05pj0pvkdyc9r30xxabam4n8zxdbzxcddr0gdypajcbqjgwgynbh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crossbeam-deque@0.8.4": "0la7fx9n1vbx3h23va0xmcy36hziql1pkik08s3j3asv4479ma7w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crossbeam-epoch@0.9.16": "1anr32r8px0vb65cgwbwp3zhqz69scz5dgq9bmx54w5qa59yjbrd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crossbeam-queue@0.3.9": "0lz17pgydh29w8brld8dysi1m4n5bxfpnj8w9bxk0q6xpyyzbg5r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crossbeam-utils@0.8.17": "13y7wh993i7q71kg6wcfj65w3rlmizzrz7cqgz1l9whlgw9rcvf0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crunchy@0.2.2": "1dx9mypwd5mpfbbajm78xcrg5lirqk7934ik980mmaffg3hdm0bs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#crypto-common@0.1.6": "1cvby95a6xg7kxdz5ln3rl9xh66nz66w46mm3g56ri1z5x815yqv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#data-encoding@2.5.0": "1rcbnwfmfxhlshzbn3r7srm3azqha3mn33yxyqxkzz2wpqcjm5ky",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#debug-helper@0.3.13": "0bhnpzpgmg8dkdr27g2b49slf6ca79m4idcb01z2krs0qkifhy7m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#deflate@0.8.6": "0x6iqlayg129w63999kz97m279m0jj4x4sm6gkqlvmp73y70yxvk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#der@0.7.8": "070bwiyr80800h31c5zd96ckkgagfjgnrrdmz3dzg2lccsd3dypz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#deranged@0.3.10": "1p4i64nkadamksa943d6gk39sl1kximz0xr69n408fvsl1q0vcwf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#digest@0.10.7": "14p2n6ih29x81akj097lvz7wi9b6b9hvls0lwrv7b6xwyy0s5ncy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.7.0": "09ky8s3higkf677lmyqg30hmj66gpg7hx907s6hfvbk2a9av05r5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#dimensioned@0.8.0": "15s3j4ry943xqlac63bp81sgdk9s3yilysabzww35j9ibmnaic50",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.4": "0p8pyg10csc782qlwx3znr6qx46ni96m1qh597kmyrf6s3s8axa8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#dotenvy@0.15.7": "16s3n973n5aqym02692i1npb079n5mb0fwql42ikmwn8wnrrbbqs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#either@1.9.0": "01qy3anr7jal5lpc20791vxrw0nl6vksb5j7x56q2fycgcyy8sm2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#embedded-alloc@0.5.1": "05gqqv9nyr33vbd0i8ab2bmfcc5kwgk0msk4pk7w5fncba8igbnx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#embedded-dma@0.2.0": "0ijld5jblcka4b95s1lwxd9k109nyaap34h44g122ddjbidpwkwr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#embedded-hal@0.2.7": "1zv6pkgg2yl0mzvh3jp326rhryqfnv4l27h78v7p7maag629i51m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#encoding_rs@0.8.33": "1qa5k4a0ipdrxq4xg9amms9r9pnnfn7nfh2i9m3mw0ka563b6s3j",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#env_logger@0.10.1": "1kmy9xmfjaqfvd4wkxr1f7d16ld3h9b487vqs2q9r0s8f3kg7cwm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.1": "1malmx5f4lkfvqasz319lq6gb3ddg19yzf9s8cykfsgzdmyq0hsl",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#errno@0.3.8": "0ia28ylfsp36i27g1qih875cyyy4by2grf80ki8vhgh6vinf8n52",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#etcetera@0.8.0": "0hxrsn75dirbjhwgkdkh0pnpqrnq17ypyhjpjaypgax1hd91nv8k",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#event-listener-strategy@0.4.0": "1lwprdjqp2ibbxhgm9khw7s7y7k4xiqj5i5yprqiks6mnrq4v3lm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#event-listener@2.5.3": "1q4w3pndc518crld6zsqvvpy9lkzwahp2zgza9kbzmmqh9gif1h2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#event-listener@4.0.1": "04k7qbi5kgs36s905gxijj41kcr78xs2s6cp6vbg50254z7wvwl4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#exr@1.71.0": "1a58k179b0h8zpf1cfgc2vl60j2syg7cdgdzp9j6cgmb6lgpcal3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fastrand@2.0.1": "19flpv5zbzpf0rk4x77z4zf25in0brg8l7m304d3yrf47qvwxjr5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fdeflate@0.3.1": "0s5885wdsih2hqx3hsl7l8cl3666fgsgiwvglifzy229hpydmmk4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#field-offset@0.3.6": "0zq5sssaa2ckmcmxxbly8qgz3sxpb8g1lwv90sdh1z74qif2gqiq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#finl_unicode@1.2.0": "1ipdx778849czik798sjbgk5yhwxqybydac18d2g9jb20dxdrkwg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fixed@1.24.0": "0zbfwzk4mrfbawpx2ahz533bkb97jzihv7fxiyhpmwf0wzkrrih2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#flate2@1.0.28": "03llhsh4gqdirnfxxb9g2w9n0721dyn4yjir3pz7z4vjaxb3yc26",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fluent-bundle@0.15.2": "1zbzm13rfz7fay7bps7jd4j1pdnlxmdzzfymyq2iawf9vq0wchp2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fluent-langneg@0.13.0": "152yxplc11vmxkslvmaqak9x86xnavnhdqyhrh38ym37jscd0jic",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fluent-syntax@0.11.0": "0y6ac7z7sbv51nsa6km5z8rkjj4nvqk91vlghq1ck5c3cjbyvay0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fluent@0.16.0": "19s7z0gw95qdsp9hhc00xcy11nwhnx93kknjmdvdnna435w97xk1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#flume@0.11.0": "10girdbqn77wi802pdh55lwbmymy437k7kklnvj12aaiwaflbb2m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fnv@1.0.7": "1hc2mcqha06aibcaza94vbi81j6pr9a1bbxrxjfhc91zin8yr7iz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#foreign-types-shared@0.1.1": "0jxgzd04ra4imjv8jgkmdq59kj8fsz6w4zxsbmlai34h26225c00",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#foreign-types@0.3.2": "1cgk0vyd7r45cj769jym4a6s7vwshvd0z4bqrb92q1fwibmkkwzn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.1": "0milh8x7nl4f450s3ddhg57a3flcv6yq8hlkyk6fyr3mcb128dp1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#frunk@0.4.2": "11v242h7zjka0lckxcffn5pjgr3jzxyljy7ffr0ppy8jkssm38qi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#frunk_core@0.4.2": "1mjqnn7dclwn8d5g0mrfkg360cgn70a7mm8arx6fc1xxn3x6j95g",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#frunk_derives@0.4.2": "0blsy6aq6rbvxcc0337g15083w24s8539fmv8rwp1qan2qprkymh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#frunk_proc_macro_helpers@0.1.2": "0b1xl4cfrfai7qi5cb4h9x0967miv3dvwvnsmr1vg4ljhgflmd9m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fuchsia-cprng@0.1.1": "1fnkqrbz7ixxzsb04bsz9p0zzazanma8znfdqjvh39n14vapfvx0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#fugit@0.3.7": "1rzp49521akq49vs9m8llgmdkk08zb77rry10a7srm9797b6l60p",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-channel@0.3.29": "1jxsifvrbqzdadk0svbax71cba5d3qg3wgjq8i160mxmd1kdckgz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-core@0.3.29": "1308bpj0g36nhx2y6bl4mm6f1gnh9xyvvw2q2wpdgnb6dv3247gb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-executor@0.3.29": "1g4pjni0sw28djx6mlcfz584abm2lpifz86cmng0kkxh7mlvhkqg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-intrusive@0.5.0": "0vwm08d1pli6bdaj0i7xhk3476qlx4pll6i0w03gzdnh7lh0r4qx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-io@0.3.29": "1ajsljgny3zfxwahba9byjzclrgvm1ypakca8z854k2w7cb4mwwb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-lite@2.2.0": "1flj85i6xm0rjicxixmajrp6rhq8i4bnbzffmrd6h23ln8jshns4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-macro@0.3.29": "1nwd18i8kvpkdfwm045hddjli0n96zi7pn6f99zi9c74j7ym7cak",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-sink@0.3.29": "05q8jykqddxzp8nwf00wjk5m5mqi546d7i8hsxma7hiqxrw36vg3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-task@0.3.29": "1qmsss8rb5ppql4qvd4r70h9gpfcpd0bg2b3qilxrnhdkc397lgg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures-util@0.3.29": "0141rkqh0psj4h8x8lgsl1p29dhqr7z2wcixkcbs60z74kb2d5d1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#futures@0.3.29": "0dak2ilpcmyjrb1j54fzy9hlw6vd10vqljq9gd59pbrq9dqr00ns",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gcd@2.3.0": "06l4fib4dh4m6gazdrzzzinhvcpcfh05r4i4gzscl03vnjhqnx8x",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gdk-pixbuf-sys@0.18.0": "1xya543c4ffd2n7aiwwrdxsyc9casdbasafi6ixcknafckm3k61z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gdk-pixbuf@0.18.3": "0b68ssdyapvq3bgsna9frabbzhjkvvzz8jld4mxkphr29nvk4vs4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gdk4-sys@0.7.2": "1w7yvir565sjrrw828lss07749hfpfsr19jdjzwivkx36brl7ayv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gdk4@0.7.3": "1xiacc63p73apr033gjrb9dsk0y4yxnsljwfxbwfry41snd03nvy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.11.2": "0a7w8w0rg47nmcinnfzv443lcyb8mplwc251p1jyr5xj2yh6wzv6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#generic-array@0.14.7": "16lyyrzrljfq424c3n8kfwkqihlimmsg5nhshbbp48np3yjrqr45",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#getrandom@0.2.11": "03q7120cc2kn7ry013i67zmjl2g9q73h1ks5z08hq5v9syz0d47y",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gif@0.11.4": "01hbw3isapzpzff8l6aw55jnaqx2bcscrbwyf3rglkbbfp397p9y",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gif@0.12.0": "0ibhjyrslfv9qm400gp4hd50v9ibva01j4ab9bwiq1aycy9jayc0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gimli@0.28.1": "0lv23wc8rxvmjia3mcxc6hj9vkqnv1bqq0h8nzjcgf71mrxx6wa2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gio-sys@0.18.1": "1lip8z35iy9d184x2qwjxlbxi64q9cpayy7v1p5y9xdsa3w6smip",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gio@0.18.4": "0wsc6mnx057s4ailacg99dwgna38dbqli5x7a6y9rdw75x9qzz6l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib-build-tools@0.16.3": "1z73bl10zmxwrv16v4f5wcky1f3z5a2v0hknca54al4k2p5ka695",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib-build-tools@0.17.10": "05p7ab2vn8962cbchi7a6hndhvw64nqk4w5kpg5z53iizsgdfrbs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib-build-tools@0.18.0": "0p5c2ayiam5bkp9wvq9f9ihwp06nqs5j801npjlwnhrl8rpwac9l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib-macros@0.18.3": "19crnw5a57w02njpbsmdqwbkncl6hw6g3mv554y8dqzcrri3jybj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib-sys@0.18.1": "164qhsfmlzd5mhyxs8123jzbdfldwxbikfpq5cysj3lddbmy4g06",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glib@0.18.4": "0kjws6ns6dym48nzxz9skhipk55flc2hy5q5kzg4w12wvizvs6wm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#glob@0.3.1": "16zca52nglanv23q5qrwd5jinw3d3as5ylya6y1pbx47vkxvrynj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gloo-timers@0.3.0": "1519157n7xppkk6pdw5w52vy1llzn5iljkqd7q1h5609jv7l7cdv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gobject-sys@0.18.0": "0i6fhp3m6vs3wkzyc22rk2cqj68qvgddxmpaai34l72da5xi4l08",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#graphene-rs@0.18.1": "00f4q1ra4haap5i7lazwhkdgnb49fs8adk2nm6ki6mjhl76jh8iv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#graphene-sys@0.18.1": "0n8zlg7z26lwpnvlqp1hjlgrs671skqwagdpm7r8i1zwx3748hfc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#grid@0.9.0": "0iswdcxggyxp9m1rz0m7bfg4xacinvn78zp2fgfp0l0079x10d06",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gsk4-sys@0.7.3": "0mbdlm9qi1hql48rr29vsj9vlqwc7gxg67wg1q19z67azwz9xg8j",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gsk4@0.7.3": "0zhzs2dkgiinhgc11akpn2harq3x5n1iq21dnc4h689g3lsqx58d",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gtk4-macros@0.7.2": "0bw3cchiycf7dw1bw4p8946gv38azxy05a5w0ndgcmxnz6fc8znm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gtk4-sys@0.7.3": "1f2ylskyqkjdik9fij2m46pra4jagnif5xyalbxfk3334fmc9n2l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#gtk4@0.7.3": "0hh8nzglmz94v1m1h6vy8z12m6fr7ia467ry0md5fa4p7sm53sss",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#h2@0.3.22": "0y41jlflvw8niifdirgng67zdmic62cjf5m2z69hzrpn5qr50qjd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#half@2.2.1": "1l1gdlzxgm7wc8xl5fxas20kfi1j35iyb7vfjkghbdzijcvazd02",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.14.3": "012nywlg0lj9kwanh69my5x67vjlfmzfi9a0rq4qvis2j8fil3r9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hashlink@0.8.4": "1xy8agkyp0llbqk9fcffc1xblayrrywlyrm2a7v93x8zygm4y2g8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#headers-core@0.2.0": "0ab469xfpd411mc3dhmjhmzrhqikzyj8a17jn5bkj9zfpy0n9xp7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#headers@0.3.9": "0w62gnwh2p1lml0zqdkrx9dp438881nhz32zrzdy61qa0a9kns06",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#heck@0.4.1": "1a7mqsnycv5z4z5vnv1k34548jzmc0ajic7c1j8jsaspnhw5ql4m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hermit-abi@0.3.9": "092hxjbjnq5fmz66grd9plxd0sh6ssg5fhgwwwqbrzgzkjwdycfj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hex-string@0.1.0": "02sgrgrbp693jv0v5iga7z47y6aj93cq0ia39finby9x17fw53l4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hex@0.4.3": "0w1a4davm1lgzpamwnba907aysmlrnygbqmfis2mqjx5m552a93z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hkdf@0.12.4": "1xxxzcarz151p1b858yn5skmhyrvn8fs4ivx5km3i1kjmnr8wpvv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hmac@0.12.1": "0pmbr069sfg76z7wsssfk5ddcqd9ncp79fyz6zcm6yn115yc6jbc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#home@0.5.9": "19grxyg35rqfd802pcc9ys1q3lafzlcjcv2pl2s5q8xpyr5kblg3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http-body@0.4.6": "1lmyjfk6bqk6k9gkn1dxq770sb78pqbqshga241hr5p995bb5skw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http@0.2.11": "1fwz3mhh86h5kfnr5767jlx9agpdggclq7xsqx930fflzakb2iw9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#http@1.0.0": "1sllw565jn8r5w7h928nsfqq33x586pyasdfr7vid01scwwgsamk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#httparse@1.8.0": "010rrfahm1jss3p022fqf3j3jmm72vhn4iqhykahb9ynpaag75yq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#httpdate@1.0.3": "1aa9rd2sac0zhjqh24c9xvir96g188zldkx0hr6dnnlx5904cfyz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#humantime@2.1.0": "1r55pfkkf5v0ji1x6izrjwdq9v6sc7bv99xj6srywcar37xmnfls",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper-tls@0.5.0": "01crgy13102iagakf6q4mb75dprzr7ps1gj0l5hxm1cvm7gks66n",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.10.16": "0wwjh9p3mzvg3fss2lqz5r7ddcgl1fh9w6my2j69d6k0lbcm41ha",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#hyper@0.14.28": "107gkvqx4h9bl17d602zkm2dgpfq86l2dr36yzfsi8l3xcsy35mz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone-haiku@0.1.2": "17r6jmj31chn7xs9698r122mapq85mfnv98bb4pg6spm0si2f67k",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#iana-time-zone@0.1.58": "081vcr8z8ddhl5r1ywif6grnswk01b2ac4nks2bhn8zzdimvh9l3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#idna@0.1.5": "0kl4gs5kaydn4v07c6ka33spm9qdh2np0x7iw7g5zd8z1c7rxw1q",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#idna@0.5.0": "1xhjrcjqq0l5bpzvdgylvpkgk94panxgsirzhjnnqfdgc4a9nkb3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#image@0.23.14": "18gn2f7xp30pf9aqka877knlq308khxqiwjvsccvzaa4f9zcpzr4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#image@0.24.7": "04d7f25b8nlszfv9a474n4a0al4m2sv9gqj3yiphhqr0syyzsgbg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#indent_write@2.2.0": "1hqjp80argdskrhd66g9sh542yxy8qi77j6rc69qd0l7l52rdzhc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#indexmap@2.1.0": "07rxrqmryr1xfnmhrjlz8ic6jw28v6h5cig3ws2c9d0wifhy2c6m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#intl-memoizer@0.5.1": "0vx6cji8ifw77zrgipwmvy1i3v43dcm58hwjxpb1h29i98z46463",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#intl_pluralrules@7.0.2": "0wprd3h6h8nfj62d8xk71h178q7zfn3srxm787w4sawsqavsg3h7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ipnet@2.9.0": "1hzrcysgwf0knf83ahb3535hrkw63mil88iqc6kjaryfblrqylcg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#iron@0.6.1": "1s4mf8395f693nhwsr0znw3j5frzn56gzllypyl50il85p50ily6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#is-terminal@0.4.9": "12xgvc7nsrp3pn8hcxajfhbli2l5wnh3679y2fmky88nhj4qj26b",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#itertools@0.10.5": "0ww45h7nxx5kj6z2y6chlskxd1igvs4j507anr6dzg99x1h25zdh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#itertools@0.12.0": "1c07gzdlc6a1c8p8jrvvw3gs52bss3y58cs2s21d9i978l36pnr5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.10": "0k7xjfki7mnv6yzjrbnbnjllg86acmbnk4izz2jmm1hx2wd6v95i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#jpeg-decoder@0.1.22": "1wnh0bmmswpgwhgmlizz545x8334nlbmkq8imy9k224ri3am7792",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#jpeg-decoder@0.3.0": "0gkv0zx95i4fr40fj1a10d70lqi6lfyia8r5q8qjxj8j4pj0005w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.66": "1ji9la5ydg0vy17q54i7dnwc0wwb9zkx662w1583pblylm6wdsff",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#kv-log-macro@1.0.7": "0zwp4bxkkp87rl7xy2dain77z977rvcry1gmr5bssdbn541v7s0d",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#language-tags@0.2.2": "16hrjdpa827carq5x4b8zhas24d8kg4s16m6nmmn1kb7cr5qh7d9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#lazy_static@1.4.0": "0in6ikhw8mgl33wjv6q6xfrb5b9jr16q8ygjy803fay4zcisvaz2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#lazycell@1.3.0": "0m8gw7dn30i0zjjpjdyf6pc16c34nl71lpv461mix50x3p70h3c3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#lebe@0.5.2": "1j2l6chx19qpa5gqcw434j83gyskq3g2cnffrbl3842ymlmpq203",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libadwaita-sys@0.5.3": "16n6xsy6jhbj0jbpz8yvql6c9b89a99v9vhdz5s37mg1inisl42y",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libadwaita@0.5.3": "174pzn9dwsk8ikvrhx13vkh0zrpvb3rhg9yd2q5d2zjh0q6fgrrg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libc@0.2.151": "1x28f0zgp4zcwr891p8n9ag9w371sbib30vp4y6hi2052frplb9h",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libloading@0.8.5": "194dvczq4sifwkzllfmw0qkgvilpha7m5xy90gd6i446vcpz4ya9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libm@0.2.8": "0n4hk1rs8pzw8hdfmwn96c4568s93kfxqgcqswr7sajd2diaihjf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libspa-sys@0.8.0": "07yh4i5grzbxkchg6dnxlwbdw2wm5jnd7ffbhl77jr0388b9f3dz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libspa@0.8.0": "044qs48yl0llp2dmrgwxj9y1pgfy09i6fhq661zqqb9a3fwa9wv5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#libsqlite3-sys@0.27.0": "05pp60ncrmyjlxxjj187808jkvpxm06w5lvvdwwvxd2qrmnj4kng",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#linked_list_allocator@0.10.5": "11k2dv6v5kq45kbvahll434f9iwfw0vsyaycp76q3vh5ahzldyls",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#linux-raw-sys@0.4.12": "0mhlla3gk1jgn6mrq9s255rvvq8a1w3yk2vpjiwsd6hmmy1imkf4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#lock_api@0.4.11": "0iggx0h4jx63xm35861106af3jkxq06fpqhpkhgw0axi2n38y5iw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#log@0.3.9": "0jq23hhn5h35k7pa8r7wqnsywji6x3wn1q5q7lif5q536if8v7p1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#log@0.4.20": "13rf7wphnwd61vazpxr7fiycin6cb1g8fmvgqg18i464p0y1drmm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#logger@0.4.0": "14xlxvkspcfnspjil0xi63qj5cybxn1hjmr5gq8m4v1g9k5p54bc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#matches@0.1.10": "1994402fq4viys7pjhzisj4wcw894l53g798kkm2y74laxk0jci5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#md-5@0.10.6": "1kvq5rnpm4fzwmyv5nmnxygdhhb2369888a06gdc9pxyrzh7x7nq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#memchr@2.6.4": "0rq1ka8790ns41j147npvxcqcl2anxyngsdimy85ag2api0fwrgn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#memoffset@0.9.0": "0v20ihhdzkfw1jx00a7zjpk2dcp5qjq6lz302nyqamd9c4f4nqss",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#mime@0.2.6": "1q1s1ax1gaz8ld3513nvhidfwnik5asbs1ma3hp6inp5dn56nqms",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#mime@0.3.17": "16hkibgvb9klh0w0jk5crr5xv90l3wlf77ggymzjmvl1818vnxv8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#mime_guess@1.8.8": "18qcd5aa3363mb742y7lf39j7ha88pkzbv9ff2qidlsdxsjjjs91",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#mime_guess@2.0.4": "1vs28rxnbfwil6f48hh58lfcx90klcvg68gxdc60spwa4cy2d4j1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#minimal-lexical@0.2.1": "16ppc5g84aijpri4jzv14rvcnslvlpphbszc7zzp6vfkddf4qdb8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.3.7": "0dblrhgbm0wa8jjl8cjp81akaj36yna92df4z1h9b26n3spal7br",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.4.4": "0jsfv00hl5rmx1nijn59sr9jmjd4rjnjhh4kdjy8d187iklih9d9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#miniz_oxide@0.7.1": "1ivl3rbbdm53bzscrd01g60l46lz5krl270487d8lhjvwl5hx0g7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#mio@1.0.2": "1v1cnnn44awxbcfm4zlavwgkvbyg7gp5zzjm8mqf1apkrwflvq40",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#modifier@0.1.0": "0n3fmgli1nsskl0whrfzm1gk0rmwwl6pw1q4nb9sqqmn5h8wkxa1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#multer@2.1.0": "1hjiphaypj3phqaj5igrzcia9xfmf4rr4ddigbh8zzb96k1bvb01",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#nary_tree@0.4.3": "1iqray1a716995l9mmvz5sfqrwg9a235bvrkpcn8bcqwjnwfv1pv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#native-tls@0.2.11": "0bmrlg0fmzxaycjpkgkchi93av07v2yf9k33gc12ca9gqdrn28h7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#nb@0.1.3": "0vyh31pbwrg21f8hz1ipb9i20qwnfwx47gz92i9frdhk0pd327c0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#nb@1.1.0": "179kbn9l6vhshncycagis7f8mfjppz4fhvgnmcikqz30mp23jm4d",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#nix@0.27.1": "0ly0kkmij5f0sqz35lx9czlbk6zpihb7yh1bsy4irzwfd2f4xc1f",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#no-std-compat@0.4.1": "132vrf710zsdp40yp1z3kgc2ss8pi0z4gmihsz3y7hl4dpd56f5r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#nom@7.1.3": "0jha9901wxam390jcf5pfa0qqfrgh8li787jx2ip0yk5b8y9hwyj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-bigint-dig@0.8.4": "0lb12df24wgxxbspz4gw1sf1kdqwvpdcpwq4fdlwg4gj41c1k16w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.45": "1ncwavvwdmsqzxnn65phv6c6nn72pnv9xhpmjd6a429mzf4k6p92",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-iter@0.1.43": "0lp22isvzmmnidbq9n5kbdh8gj0zm3yhxv1ddsn5rp65530fc0vx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-rational@0.3.2": "01sgiwny9iflyxh2xz02sak71v2isc3x608hfdpwwzxi3j5l5b0j",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-rational@0.4.1": "1c0rb8x4avxy3jvvzv764yk7afipzxncfnqlb10r3h53s34s2f06",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.17": "0z16bi5zwgfysz6765v3rd6whfbjpihx3mhsn4dg8dzj2c221qrr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num_cpus@1.16.0": "0hra6ihpnh06dvfvz9ipscys0xfqa9ca9hzp384d5m02ssvgqqa1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num_enum@0.5.11": "1japmqhcxwn1d3k7q8jw58y7xfby51s16nzd6dkj483cj2pnqr0z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#num_enum_derive@0.5.11": "16f7r4jila0ckcgdnfgqyhhb90w9m2pdbwayyqmwcci0j6ygkgyw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#object@0.32.1": "1c02x4kvqpnl3wn7gz9idm4jrbirbycyqjgiw6lm1g9k77fzkxcw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#once_cell@1.19.0": "14kvw7px5z96dk4dwdm1r9cqhhy2cyj1l5n5b29mynbb8yr15nrz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#openssl-macros@0.1.1": "173xxvfc63rr5ybwqwylsir0vq6xsj4kxiv4hmg4c3vscdmncj59",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#openssl-probe@0.1.5": "1kq18qm48rvkwgcggfkqq6pm948190czqc94d6bm2sir5hq1l0gz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#openssl-sys@0.9.97": "02s670ir38fsavphdna07144y41dkvrcfkwnjzg82zfrrlsavsn3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#openssl@0.10.61": "0idv3n9n9f2sxq8cqzxvq44633vg5sx4n9q1p3g6dn66ikf1k13b",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pango-sys@0.18.0": "1iaxalcaaj59cl9n10svh4g50v8jrc1a36kd7n9yahx8j7ikfrs3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pango@0.18.3": "1r5ygq7036sv7w32kp8yxr6vgggd54iaavh3yckanmq4xg0px8kw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#panic-halt@0.2.0": "04nqaa97ph20ppyy58grwr23hrbw83pn0gf7apf73rdx1q7595ny",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#parking@2.2.0": "1blwbkq6im1hfxp5wlbr475mw98rsyc0bbr2d5n16m38z253p0dv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#parking_lot@0.12.1": "13r2xk7mnxfc5g0g6dkdxqdqad99j7s7z8zhzz4npw5r0g0v4hip",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#parking_lot_core@0.9.9": "13h0imw1aq86wj28gxkblhkzx6z1gk8q18n0v76qmmj6cliajhjc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#parse-zoneinfo@0.3.0": "0h8g6jy4kckn2gk8sd5adaws180n1ip65xhzw5jxlq4w8ibg41f7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#paste@1.0.14": "0k7d54zz8zrz0623l3xhvws61z5q2wd3hkwim6gylk8212placfy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pem-rfc7468@0.7.0": "04l4852scl4zdva31c1z6jafbak0ni5pi0j38ml108zwzjdrrcw8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@1.0.1": "0cgq08v1fvr6bs5fvy390cz830lq4fak8havdasdacxcw790s09i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.1": "0gi8wgx0dcy8rnv1kywdv98lwcx67hz0a0zwpib5v2i08r88y573",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf@0.11.2": "1p03rsw66l7naqhpgr1a34r9yzi1gv9jh16g3fsk6wrwyfwdiqmd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf@0.7.24": "066xwv4dr6056a9adlkarwp4n94kbpwngbmd47ngm3cfbyw49nmk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.11.2": "0nia6h4qfwaypvfch3pnq1nd2qj64dif4a6kai3b7rjrsf49dlz8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_codegen@0.7.24": "0zjiblicfm0nrmr2xxrs6pnf6zz2394wgch6dcbd8jijkq98agmh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.11.2": "1c14pjyxbcpwkdgw109f7581cc5fa3fnkzdq1ikvx7mdq9jcrr28",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_generator@0.7.24": "0qi62gxk3x3whrmw5c4i71406icqk11qmpgln438p6qm7k4lqdh9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.11.2": "0azphb0a330ypqx3qvyffal5saqnks0xvl8rj73jlk3qxxgbkz4h",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#phf_shared@0.7.24": "18371fla0vsj7d6d5rlfb747xbr2in11ar9vgv5qna72bnhp2kr3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pin-project-internal@1.1.3": "01a4l3vb84brv9v7wl71chzxra2kynm6yvcjca66xv3ij6fgsna3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pin-project-lite@0.2.13": "0n0bwr5qxlf0mhn2xkl36sy55118s9qmvx2yl5f3ixkb007lbywa",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pin-project@1.1.3": "08k4cpy8q3j93qqgnrbzkcgpn7g0a88l4a9nm33kyghpdhffv97x",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pin-utils@0.1.0": "117ir7vslsl2z1a7qzhws4pd01cg2d3338c47swjyvqv2n60v1wb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pio@0.2.1": "1qvq03nbx6vjix7spr5fcxcbxw39flm1y72kxl1g728gnna9dq3n",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#piper@0.2.1": "1m45fkdq7q5l9mv3b0ra10qwm0kb67rjp2q8y91958gbqjqk33b6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pipewire-sys@0.8.0": "04hiy3rl8v3j2dfzp04gr7r8l5azzqqsvqdzwa7sipdij27ii7l4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pipewire@0.8.0": "1nldg1hz4v0qr26lzdxqpvrac4zbc3pb6436sl392425bjx4brh8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pkcs1@0.7.5": "0zz4mil3nchnxljdfs2k5ab1cjqn7kq5lqp62n9qfix01zqvkzy8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pkcs8@0.10.2": "1dx7w21gvn07azszgqd3ryjhyphsrjrmq5mmz1fbxkj5g0vv4l7r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pkg-config@0.3.27": "0r39ryh1magcq4cz5g9x88jllsnxnhcqr753islvyk4jp9h2h1r6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#plugin@0.2.6": "1q7nghkpvxxr168y2jnzh3w7qc9vfrby9n7ygy3xpj0bj71hsshs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#png@0.16.8": "1ipl44q3vy4kvx6j296vk7d4v8gvcg203lrkvvixwixq1j98fciw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#png@0.17.10": "0r5a8a25ad0jq2pkp2zbab3wwhpgp6jmdg6d0ybjnw6kilnvyxfx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#polling@3.4.0": "052am20b5r03nwhpnjw86rv3dwsdabvb07anv3fqxfbs65r4w19h",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#powerfmt@0.2.0": "14ckj2xdpkhv3h6l5sdmb9f1d57z8hbfpdldjc2vl5givq2y77j3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ppv-lite86@0.2.17": "1pp6g52aw970adv3x2310n7glqnji96z0a9wiamzw89ibf0ayh2v",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#pretty_env_logger@0.5.0": "076w9dnvcpx6d3mdbkqad8nwnsynb7c8haxmscyrz7g3vga28mw6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@1.3.1": "069r1k56bvgk0f58dm5swlssfcp79im230affwk6d9ck20g04k3z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-crate@2.0.1": "06jbv5w6s04dbjbwq0iv7zil12ildf3w8dvvb4pqvhig4gm5zp4p",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error-attr@1.0.4": "0sgq6m5jfmasmwwy8x4mjygx5l7kp8s4j60bv25ckv2j1qc41gm1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro-error@1.0.4": "1373bhxaf0pagd8zkyd03kkx6bchzf6g0dkwrwzsnal9z47lj9fs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.78": "1bjak27pqdn4f4ih1c9nr3manzyavsgqmf76ygw9k76q8pb2lhp2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#proptest@1.4.0": "1gzmw40pgmwzb7x6jsyr88z5w151snv5rp1g0dlcp1iw3h9pdd1i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#qoi@0.4.1": "00c0wkb112annn2wl72ixyd78mf56p4lxkhlmsggx65l3v3n8vbz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#quick-error@1.2.3": "1q6za3v78hsspisc197bg3g7rpc989qycy8ypr8ap8igv10ikl51",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#quote@1.0.35": "1vv8r2ncaz4pqdr78x7f138ka595sp2ncr1sa2plm4zxbsmwj7i9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand@0.3.23": "0v679h38pjjqj5h4md7v2slsvj6686qgcn7p9fbw3h43iwnk1b34",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand@0.4.6": "14qjfv3gggzhnma20k0sc1jf8y6pplsaq7n1j9ls5c8kf2wl0a2m",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand@0.6.5": "1jl4449jcl4wgmzld6ffwqj5gwxrp8zvx8w573g1z368qg6xlwbd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand@0.8.5": "013l6931nn7gkc23jz5mm3qdhf93jjf0fg64nz2lp4i51qd8vbrl",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.1.1": "1vxwyzs4fy1ffjc8l00fsyygpiss135irjf7nyxgq2v0lqf3lvam",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_chacha@0.3.1": "123x2adin558xbhvqb8w4f6syjsdkmqff8cxwhmjacpsl1ihmhg6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_core@0.3.1": "0jzdgszfa4bliigiy4hi66k7fs3gfwi2qxn8vik84ph77fwdwvvs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_core@0.4.2": "1p09ynysrq1vcdlmcqnapq4qakl2yd1ng3kxh3qscpx09k2a6cww",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_core@0.6.4": "0b4j2v4cb5krak1pv6kakv4sz6xcwbrmy2zckc32hsigbrwy82zc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_hc@0.1.0": "1i0vl8q5ddvvy0x8hf1zxny393miyzxkwqnw31ifg6p0gdy6fh3v",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_isaac@0.1.1": "027flpjr4znx2csxk7gxb7vrf9c7y5mydmvg5az2afgisp4rgnfy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_jitter@0.1.4": "16z387y46bfz3csc42zxbjq89vcr1axqacncvv8qhyy93p4xarhi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_os@0.1.3": "0wahppm0s64gkr2vmhcgwc0lij37in1lgfxg5rbgqlz0l5vgcxbv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_pcg@0.1.2": "0i0bdla18a8x4jn1w0fxsbs3jg7ajllz6azmch1zw33r06dv1ydb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_xorshift@0.1.1": "0p2x8nr00hricpi2m6ca5vysiha7ybnghz79yqhhx6sl4gkfkxyb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rand_xorshift@0.3.0": "13vcag7gmqspzyabfl1gr9ykvxd2142q2agrj8dkyjmfqmgg4nyj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rayon-core@1.12.0": "1vaq0q71yfvcwlmia0iqf6ixj2fibjcf2xjy92n1m1izv1mgpqsw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rayon@1.8.0": "1cfdnvchf7j4cpha5jkcrrsr61li9i9lp5ak7xdq6d3pvc1xn9ww",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rdrand@0.4.0": "1cjq0kwx1bk7jx3kzyciiish5gqsj7620dm43dc52sr8fzmm9037",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#redox_syscall@0.4.1": "1aiifyz5dnybfvkk4cdab9p2kmphag1yad6iknc7aszlxxldf8j7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.3": "0gs8q9yhd3kcg4pr00ag4viqxnh5l7jpyb9fsfr8hzh451w4r02z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.2": "17rd2s8xbiyf6lb4aj2nfi44zqlj98g2ays8zzj2vfs743k79360",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#regex@1.10.2": "0hxkd814n4irind8im5c9am221ri6bprx49nc7yxv02ykhd9a2rq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#remove_dir_all@0.5.3": "1rzqbsgkmr053bxxl04vmvsd1njyz0nxvly97aip6aa2cmb15k9s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#reqwest@0.11.23": "0hgvzb7r46656r9vqhl5qk1kbr2xzjb91yr2cb321160ka6sxc9p",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rp-pico@0.8.0": "0mmx8dyl0q1a9fgz12hrvwd7civqbd1j7g1w5c5i6pcfdwg7fhb3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rp2040-boot2@0.3.0": "08dv9ndvdzyjz4wdlxcikf1m1s6wwi80027ldkihx59zyr2g74kw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rp2040-hal-macros@0.1.0": "0piaczzlbrfdhidnqkg04xs1rzal3w3zjplrh6pf3vwpwiir0iw6",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rp2040-hal@0.9.2": "1jk725cf6nx6rhn06swbx47yaq3j134m0hpnv47p5mkdgspbkwhz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rp2040-pac@0.5.0": "0k3fm4fww6gcy7w7zwbmmqn9wzz4sih13l1m93sl7x8mb0vxin8j",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rsa@0.9.6": "1z0d1aavfm0v4pv8jqmqhhvvhvblla1ydzlvwykpc3mkzhj523jx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc-demangle@0.1.23": "0xnbk2bmyzshacjm2g1kd4zzv2y2az14bw3sjccq5qkpmsfvn9nn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc-hash@1.1.0": "1qkc5khrmv5pqi5l5ca9p5nl5hs742cagrndhbrlk3dhlrx3zm08",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc_version@0.2.3": "02h3x57lcr8l2pm0a645s9whdh33pn5cnrwvn5cb57vcrc53x3hk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustc_version@0.4.0": "0rpk9rcdk405xhbmgclsh4pai0svn49x35aggl4nhbkd4a2zb85z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rustix@0.38.28": "05m3vacvbqbg6r6ksmx9k5afpi0lppjdv712crrpsrfax2jp5rbj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#rusty-fork@0.3.0": "0kxwq5c480gg6q0j3bg4zzyfh2kwmc3v2ba94jw8ncjc8mpcqgfb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#ryu@1.0.16": "0k7b90xr48ag5bzmfjp82rljasw2fx28xr3bg1lrpx7b5sljm3gr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#safemem@0.3.3": "0wp0d2b2284lw11xhybhaszsczpbq1jbdklkxgifldcknmy3nw7g",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#schannel@0.1.22": "126zy5jb95fc5hvzyjwiq6lc81r08rdcn6affn00ispp9jzk6dqc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#scoped-tls@1.0.1": "15524h04mafihcvfpgxd8f4bgc3k95aclz8grjkg9a0rxcvn9kz1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#scoped_threadpool@0.1.9": "1a26d3lk40s9mrf4imhbik7caahmw2jryhhb6vqv6fplbbgzal8x",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#scopeguard@1.2.0": "0jcz9sd47zlsgcnm1hdw0664krxwb5gczlif4qngj2aif8vky54l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#security-framework-sys@2.9.1": "0yhciwlsy9dh0ps1gw3197kvyqx1bvc4knrhiznhid6kax196cp9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#security-framework@2.9.2": "1pplxk15s5yxvi2m1sz5xfmjibp96cscdcl432w9jzbk0frlzdh5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#self_cell@0.10.3": "0pci3zh23b7dg6jmlxbn8k4plb7hcg5jprd1qiz0rp04p1ilskp1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#self_cell@1.0.2": "1rmdglwnd77wcw2gv76finpgzjhkynx422d0jpahrf2fsqn37273",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#semver-parser@0.7.0": "18vhypw6zgccnrlm5ps1pwa0khz7ry927iznpr88b87cagr1v2iq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#semver@0.9.0": "00q4lkcj0rrgbhviv9sd4p6qmdsipkwkbra7rh11jrhq5kpvjzhx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#semver@1.0.20": "140hmbfa743hbmah1zjf07s8apavhvn04204qjigjiz5w6iscvw3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde@0.9.15": "1bsla8l5xr9pp5sirkal6mngxcq6q961km88jvf339j5ff8j7dil",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde@1.0.209": "029yqqbb3c8v3gc720fhxn49dhgvb88zbyprdg5621riwzzy1z4r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.209": "0w114ksg1ymnmqdisd0g1j3g8jgz6pam45xg6yb47dfpkybip0x5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.127": "1b99lgg1d986gwz5fbmmzmvjmqg5bx0lzmhy6rqp5gc2kxnw0hw0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_spanned@0.6.5": "1hgh6s3jjwyzhfk3xwb6pnnr1misq9nflwq0f026jafi37s24dpb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#serde_urlencoded@0.7.1": "1zgklbdaysj3230xivihs30qi5vkhigg323a9m62k8jwf4a1qjfk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sha1@0.10.6": "1fnnxlfg08xhkmwf2ahv634as30l1i3xhlhkvxflmasi5nd85gz3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sha2@0.10.8": "1j1x78zk9il95w9iv46dh9wm73r6xrgj32y6lzzw7bxws9dbfgbr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#shlex@1.3.0": "0r1y6bv26c1scpxvhg2cabimrmwgbp4p3wy6syj9n0c4s3q2znhg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#signal-hook-registry@1.4.1": "18crkkw5k82bvcx088xlf5g4n3772m24qhzgfan80nda7d3rn8nq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#signature@2.2.0": "1pi9hd5vqfr3q3k49k37z06p7gs5si0in32qia4mmr1dancr6m3p",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#simd-adler32@0.3.7": "1zkq40c3iajcnr5936gjp9jjh1lpzhy44p3dq3fiw75iwr1w2vfn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#siphasher@0.2.3": "1b53m53l24lyhr505lwqzrpjyq5qfnic71mynrcfvm43rybf938b",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#siphasher@0.3.11": "03axamhmwsrmh0psdw3gf7c0zc4fyl5yjxfifz9qfka6yhkqid9q",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#slab@0.4.9": "0rxvsgir0qw5lkycrqgb1cxsvxzjv9bmx73bk5y42svnzfba94lg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#smallvec@1.11.2": "0w79x38f7c0np7hqfmzrif9zmn0avjvvm31b166zdk9d1aad1k2d",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#snowflake@1.3.0": "1wadr7bxdxbmkbqkqsvzan6q1h3mxqpxningi3ss3v9jaav7n817",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#socket2@0.5.5": "1sgq315f1njky114ip7wcy83qlphv9qclprfjwvxcpfblmcsqpvv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#spin@0.5.2": "0b84m6dbzrwf2kxylnw82d3dr8w06av7rfkr8s85fb5f43rwyqvf",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#spin@0.9.8": "0rvam5r0p3a6qhc18scqpvpgb3ckzyqxpgdfyjnghh8ja7byi039",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#spki@0.7.3": "17fj8k5fmx4w9mp27l970clrh5qa7r5sjdvbsln987xhb34dc7nr",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlformat@0.2.3": "0v0p70wjdshj18zgjjac9xlx8hmpx33xhq7g8x9rg4s4gjyvg0ff",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-core@0.7.3": "1gdz44yb9qwxv4xl4hv6w4vbqx0zzdlzsf9j9gcj1qir6wy0ljyq",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-macros-core@0.7.3": "0h88wahkxa6nam536lhwr1y0yxlr6la8b1x0hs0n88v790clbgfh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-macros@0.7.3": "19gjwisiym07q7ibkp9nkvvbywjh0r5rc572msvzyzadvh01r5l9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-mysql@0.7.3": "190ygz5a3pqcd9vvqjv2i4r1xh8vi53j4272yrld07zpblwrawg3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-postgres@0.7.3": "090wm9s6mm53ggn1xwr183cnn8yxly8rgcksdk4hrlfcnz1hmb6n",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx-sqlite@0.7.3": "143laha7wf8dmi0xwycwqmvxdcnb25dq7jnqrsgvmis8v6vpc291",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#sqlx@0.7.3": "1kv3hyx7izmmsjqh3l47zrfhjlcblpg20cvnk7pr8dm7klkkr86v",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.0": "1lxjr8q2n534b2lhkxd6l6wcddzjvnksi58zv11f9y0jjmr15wd8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#stringprep@0.1.4": "1rkfsf7riynsmqj3hbldfrvmna0i9chx2sz39qdpl40s4d7dfhdv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#strsim@0.10.0": "08s69r4rcrahwnickvi0kq49z524ci50capybln83mg6b473qivk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#subtle@2.5.0": "1g2yjs7gffgmdvkkq0wrrh0pxds3q0dv6dhkw9cdpbib656xdkc1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#syn@1.0.109": "0ds2if4600bd59wsv7jjgfkayfzy3hnazs394kz6zdkmna8l3dkj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#syn@2.0.48": "0gqgfygmrxmp8q32lia9p294kdd501ybn6kn2h4gqza0irik2d8g",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-configuration-sys@0.5.0": "1jckxvdr37bay3i9v52izgy52dg690x5xfg3hd394sv2xf4b2px7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-configuration@0.5.1": "1rz0r30xn7fiyqay2dvzfy56cvaa3km74hnbz2d72p97bkf3lfms",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#system-deps@6.2.0": "0c836abhh3k8yn5ymg8wx383ay7n731gkrbbp3gma352yq7mhb9a",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.12.12": "02lk65ik5ffb8vl9qzq02v0df8kxrp16zih78a33mji49789zhql",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tempdir@0.3.7": "1n5n86zxpgd85y0mswrp5cfdisizq2rv3la906g6ipyc03xvbwhm",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tempfile@3.8.1": "1r88v07zdafzf46y63vs39rmzwl4vqd4g2c5qarz9mqa8nnavwby",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#termcolor@1.4.0": "0jfllflbxxffghlq6gx4csv0bv0qv77943dcx01h9zssy39w66zz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#thiserror-impl@1.0.51": "1ps9ylhlk2vn19fv3cxp40j3wcg1xmb117g2z2fbf4vmg2bj4x01",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#thiserror@1.0.51": "1drvyim21w5sga3izvnvivrdp06l2c24xwbhp0vg1mhn2iz2277i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tiff@0.6.1": "0ds48vs919ccxa3fv1www7788pzkvpg434ilqkq7sjb5dmqg8lws",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tiff@0.9.0": "04b2fd3clxm0pmdlfip8xj594zyrsfwmh641i6x1gfiz9l7jn5vd",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#time-core@0.1.2": "1wx3qizcihw6z151hywfzzyd1y5dl804ydyxci6qm07vbakpr4pg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#time-macros@0.2.16": "0gx4ngf5g7ydqa8lf7kh9sy72rd4dhvpi31y1jvswi0288rpw696",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#time@0.1.45": "0nl0pzv9yf56djy8y5dx25nka5pr2q1ivlandb3d24pksgx7ly8v",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#time@0.3.31": "0gjqcdsdbh0r5vi4c2vrj5a6prdviapx731wwn07cvpqqd1blmzn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.5": "1khf3j95bwwksj2hw76nlvwlwpwi4d1j421lj6x35arqqprjph43",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tinyvec@1.6.0": "0l6bl2h62a5m44jdnpn7lmj14rd44via8180i7121fvm73mmrk47",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tinyvec_macros@0.1.1": "081gag86208sc3y6sdkshgw3vysm5d34p431dzw0bshz66ncng0z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-macros@2.4.0": "0lnpg14h1v3fh2jvnc8cz7cjf0m7z1xgkwfpcyy632g829imjgb9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-native-tls@0.3.1": "1wkfg6zn85zckmv4im7mv20ca6b1vmlib5xwz9p7g19wjfmpdbmv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-stream@0.1.14": "0hi8hcwavh5sdi1ivc9qc4yvyr32f153c212dpd7sb366y6rhz1r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-tungstenite@0.21.0": "0f5wj0crsx74rlll97lhw0wk6y12nhdnqvmnjx002hjn08fmcfy8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio-util@0.7.10": "058y6x4mf0fsqji9rfyb77qbfyc50y4pk2spqgj6xsyr693z66al",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tokio@1.39.3": "1xgzhj7bxqqpjaabjkgsx8hi0f600bzj4iyp9f0a9gr3k6dwkawv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml@0.8.2": "0g9ysjaqvm2mv8q85xpqfn7hi710hj24sd56k49wyddvvyq8lp8q",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_datetime@0.6.3": "0jsy7v8bdvmzsci6imj8fzgd255fmy5fzp6zsri14yrry7i77nkw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.19.15": "08bl7rp5g6jwmfpad9s8jpw8wjrciadpnbaswgywpr9hv9qbfnqv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#toml_edit@0.20.2": "0f7k5svmxw98fhi28jpcyv7ldr2s3c867pjbji65bdxjpd44svir",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tower-service@0.3.2": "0lmfzmmvid2yp2l36mbavhmqgsvzqf7r2wiwz73ml4xmwaf1rg5n",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing-attributes@0.1.27": "1rvb5dn9z6d0xdj14r403z0af0bbaqhg02hq4jc97g5wds6lqw1l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing-core@0.1.32": "0m5aglin3cdwxpvbg6kz0r9r0k31j48n0kcfwsp6l49z26k3svf0",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tracing@0.1.40": "1vv48dac9zgj9650pg2b4d0j3w6f3x9gbggf43scq5hrlysklln3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#traitobject@0.1.0": "0yb0n8822mr59j200fyr2fxgzzgqljyxflx9y8bdy3rlaqngilgg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#try-lock@0.2.5": "0jqijrrvm1pyq34zn1jmy2vihd4jcrjlvsh4alkjahhssjnsn8g4",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#tungstenite@0.21.0": "1qaphb5kgwgid19p64grhv2b9kxy7f1059yy92l9kwrlx90sdwcy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#type-map@0.4.0": "0ilsqq7pcl3k9ggxv2x5fbxxfd6x7ljsndrhc38jmjwnbr63dlxn",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#typeable@0.1.2": "11w8dywgnm32hb291izjvh4zjd037ccnkk77ahk63l913zwzc40l",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#typemap@0.3.3": "1xm1gbvz9qisj1l6d36hrl9pw8imr8ngs6qyanjnsad3h0yfcfv5",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#typenum@1.17.0": "09dqxv69m9lj9zvv6xw5vxaqx15ps0vxyy5myg33i0kbqvq0pzs2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#typeshare-annotation@1.0.2": "1adpfhyz3lqjjbq2ym69mv62ymqyd5651gxlqdy8aa446l70srzw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#typeshare@1.0.1": "1mi7snkx2b4g84x8vx38v1myg5r6g48c865j0nz5zcsc8lpilkgl",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unarray@0.1.4": "154smf048k84prsdgh09nkm2n0w0336v84jd4zikyn6v6jrqbspa",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unic-langid-impl@0.9.4": "1ijvqmsrg6qw3b1h9bh537pvwk2jn2kl6ck3z3qlxspxcch5mmab",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unic-langid@0.9.4": "05pm5p3j29c9jw9a4dr3v64g3x6g3zh37splj47i7vclszk251r3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicase@1.4.2": "0cwazh4qsmm9msckjk86zc1z35xg7hjxjykrgjalzdv367w6aivz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicase@2.7.0": "12gd74j79f94k4clxpf06l99wiv4p30wjr0qm04ihqk9zgdd9lpp",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode-bidi@0.3.14": "05i4ps31vskq1wdp8yf315fxivyh1frijly9d4gb5clygbr2h9bg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12": "0jzf1znfpb2gx8nr8mvmyqs1crnv79l57nxnbiszc7xf7ynbjm1k",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode-normalization@0.1.22": "08d95g7b1irc578b2iyhzv4xhsa4pfvwsqxcl9lbcpabzkq16msw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode-segmentation@1.10.1": "0dky2hm5k51xy11hc3nk85p533rvghd462b6i0c532b7hl4j9mhx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode-width@0.1.13": "0p92vl8n7qc8mxz45xn6qbgi0259z96n32a158l6vj5bywwdadh3",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unicode_categories@0.1.1": "0kp1d7fryxxm7hqywbk88yb9d1avsam9sg76xh36k5qx2arj9v1r",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#unsafe-any@0.4.2": "0zwwphsqkw5qaiqmjwngnfpv9ym85qcsyj7adip9qplzjzbn00zk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#url@1.7.2": "0nim1c90mxpi9wgdw2xh8dqd72vlklwlzam436akcrhjac6pqknx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#url@2.5.0": "0cs65961miawncdg2z20171w0vqrmraswv2ihdpd8lxp7cp31rii",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#urlencoding@2.1.3": "1nj99jp37k47n0hvaz5fvz7z6jd0sb4ppvfy3nphr1zbnyixpy6s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#usb-device@0.2.9": "0205a850jhw9gb96scwfx1k4iwpjvighvz3m80mjkda9r2nw6v0z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#utf-8@0.7.6": "1a9ns3fvgird0snjkd3wbdhwd3zdpc2h5gpyybrfr6ra5pkqxk09",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#utf8parse@0.2.1": "02ip1a0az0qmc2786vxk2nqwsgcwf17d3a38fkf0q7hrmwh9c6vi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#uuid@0.4.0": "0cdj2v6v2yy3zyisij69waksd17cyir1n58kwyk1n622105wbzkw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#uuid@0.8.2": "1dy4ldcp7rnzjy56dxh7d2sgrcvn4q77y0a8r0a48946h66zjp5w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#uuid@1.6.1": "0q45jxahvysldn3iy04m8xmr8hgig80855y9gq9di8x72v7myfay",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#value-bag@1.7.0": "02r8wccrzi3bzlkrslkcfw9pwp8kwif9szif2i9arn9dzqx44vhj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#vcell@0.1.3": "00n0ss2z3rh0ihig6d4w7xp72g58f7g1m6s5v4h3nc6jacdrqhvp",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#vcpkg@0.2.15": "09i4nf5y8lig6xgj3f7fyrvzd3nlaw4znrihw8psidvv5yk4xkdc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#version-compare@0.1.1": "0acg4pmjdbmclg0m7yhijn979mdy66z3k8qrcnvn634f1gy456jp",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#version_check@0.1.5": "1pf91pvj8n6akh7w6j5ypka6aqz08b3qpzgs0ak2kjf4frkiljwi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.4": "0gs8grwdlgh0xq660d7wr80x14vxbizmd8dbp29p2pdncx8lp1s9",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#void@1.0.2": "0zc8f0ksxvmhvgx4fdg0zyn6vdnbxd2xv9hfx4nhzg6kbs4f80ka",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#volatile-register@0.2.2": "1k0rkm81qyhn4r8f03z0sch2kyikkgjjfalpaami9c08c8m7whyy",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wait-timeout@0.2.0": "1xpkk0j5l9pfmjfh1pi0i89invlavfrd9av5xp0zhxgb29dhy84z",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#want@0.3.1": "03hbfrnvqqdchb5kgxyavb9jabwza0dmh2vw5kg0dq8rxl57d9xz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#warp@0.3.7": "07137zd13lchy5hxpspd0hs6sl19b0fv2zc1chf02nwnzw1d4y23",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.10.0+wasi-snapshot-preview1": "07y3l8mzfzzz4cj09c8y90yak4hpsi9g7pllyzpr6xvwrabka50s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasi@0.11.0+wasi-snapshot-preview1": "08z4hxwkpdpalxjps1ai9y7ihin26y9f476i53dv98v45gkqg3cw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.89": "09l8lyylsdssz993h4fzja69zpvpykaw84fivs210fjgwqjzcmhv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-futures@0.4.39": "04lsxpw4jqfwh7c0crzx0smj52nvwp1w3bh4098sq90149da2dmc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.89": "10sj1gr2naxv5q116yjb929hhpvz45dxbkvyk8hyc2lknzy85szh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.89": "1cl2w7k5jn2jbd5kx613c8k8vjvda22hfgcgx7y2mk93fbrxnqh1",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.89": "17s5rppad113c6ggkaq8c3cg7a3zz15i78wxcg6mcl1n15iv7fbs",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.89": "0kh6akdldy13z9xqj0skz6b4npq1d98bjkgzb8ccq59hibvd9l0f",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#web-sys@0.3.66": "03q1z22djv5ncqkyydcvnchmdsl5gvnyzcyixkxnifw6xi24mhjh",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#weezl@0.1.7": "1frdbq6y5jn2j93i20hc80swpkj30p1wffwxj1nr4fp09m6id4wi",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#whoami@1.4.1": "0l6ca9pl92wmngsn1dh9ih716v216nmn2zvcn94k04x9p1b3gz12",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winapi-i686-pc-windows-gnu@0.4.0": "1dmpa6mvcvzz16zg6d5vrfy4bxgg541wxrcip7cnshi06v38ffxc",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winapi-util@0.1.6": "15i5lm39wd44004i9d5qspry2cynkrpvwzghr6s2c3dsk28nz7pj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winapi-x86_64-pc-windows-gnu@0.4.0": "0gqq64czqb64kskjryj8isp62m2sgvx25yyj3kpc2myh85w24bki",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winapi@0.3.9": "06gl025x418lchw1wxj64ycr7gha83m44cjr5sarhynd9xkrm0sw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows-core@0.51.1": "0r1f57hsshsghjyc7ypp2s0i78f7b1vr93w68sdb8baxyf2czy7i",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.48.0": "1aan23v5gs7gya1lc46hqn9mdh8yph3fhxmhxlw36pn6pqc28zb7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows-sys@0.52.0": "0gd3v4ji88490zgb6b5mq5zgbvwv7zx1ibn8v3x83rwcdbryaar8",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.48.5": "034ljxqshifs1lan89xwpcy1hp0lhdh4b5n0d2z4fwjx2piacbws",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows-targets@0.52.0": "1kg7a27ynzw8zz3krdgy6w5gbqcji27j1sz4p7xk2j5j8082064a",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.48.5": "1n05v7qblg1ci3i567inc7xrkmywczxrs1z3lj3rkkxw18py6f1b",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_gnullvm@0.52.0": "1shmn1kbdc0bpphcxz0vlph96bxz0h1jlmh93s9agf2dbpin8xyb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.48.5": "1g5l4ry968p73g6bg6jgyvy9lb8fyhcs54067yzxpcpkf44k2dfw",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_aarch64_msvc@0.52.0": "1vvmy1ypvzdvxn9yf0b8ygfl85gl2gpcyvsvqppsmlpisil07amv",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.48.5": "0gklnglwd9ilqx7ac3cn8hbhkraqisd0n83jxzf9837nvvkiand7",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_i686_gnu@0.52.0": "04zkglz4p3pjsns5gbz85v4s5aw102raz4spj4b0lmm33z5kg1m2",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.48.5": "01m4rik437dl9rdf0ndnm2syh10hizvq0dajdkv2fjqcywrw4mcg",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_i686_msvc@0.52.0": "16kvmbvx0vr0zbgnaz6nsks9ycvfh5xp05bjrhq65kj623iyirgz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.48.5": "13kiqqcvz2vnyxzydjh73hwgigsdr2z1xpzx313kxll34nyhmm2k",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnu@0.52.0": "1zdy4qn178sil5sdm63lm7f0kkcjg6gvdwmcprd2yjmwn8ns6vrx",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.48.5": "1k24810wfbgz8k48c2yknqjmiigmql6kk3knmddkv8k8g1v54yqb",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_gnullvm@0.52.0": "17lllq4l2k1lqgcnw1cccphxp9vs7inq99kjlm2lfl9zklg7wr8s",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.48.5": "0f4mdp895kkjh9zv8dxvn4pc10xr7839lf5pa9l0193i2pkgr57d",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#windows_x86_64_msvc@0.52.0": "012wfq37f18c09ij5m6rniw7xxn5fcvrxbqd0wd8vgnl3hfn9yfz",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winnow@0.5.30": "1ifj9vnqna5qp0d7nb9mrinzf8j7zi1m0gv75870vm91jyw3sp4v",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#winreg@0.50.0": "1cddmp929k882mdh6i9f2as848f13qqna6czwsqzkh1pqnr5fkjj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#yansi-term@0.1.2": "1w8vjlvxba6yvidqdvxddx3crl6z66h39qxj8xi6aqayw2nk0p7y",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.7.31": "06k0zk4x4n9s1blgxmxqb1g81y8q334aayx61gyy6v9y1dajkhdk",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.7.31": "0gcfyrmlrhmsz16qxjp2qzr6vixyaw1p04zl28f08lxkvfz62h0w",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#zeroize@1.7.0": "0bfvby7k9pdp6623p98yz2irqnamcyzpn7zh20nqmdn68b0lwnsj",
|
|
||||||
"registry+https://github.com/rust-lang/crates.io-index#zune-inflate@0.2.54": "00kg24jh3zqa3i6rg6yksnb71bch9yi1casqydl00s7nw8pk7avk"
|
|
||||||
}
|
|
|
@ -1,13 +1,12 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dashboard"
|
name = "dashboard"
|
||||||
version = "0.1.3"
|
version = "0.1.2"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
||||||
async-std = { version = "1.13" }
|
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
fluent-ergonomics = { path = "../fluent-ergonomics/" }
|
fluent-ergonomics = { path = "../fluent-ergonomics/" }
|
||||||
|
@ -18,11 +17,13 @@ gio = { version = "0.18" }
|
||||||
glib = { version = "0.18" }
|
glib = { version = "0.18" }
|
||||||
gdk = { version = "0.7", package = "gdk4" }
|
gdk = { version = "0.7", package = "gdk4" }
|
||||||
gtk = { version = "0.7", package = "gtk4" }
|
gtk = { version = "0.7", package = "gtk4" }
|
||||||
|
ifc = { path = "../ifc/" }
|
||||||
lazy_static = { version = "1.4" }
|
lazy_static = { version = "1.4" }
|
||||||
memorycache = { path = "../memorycache/" }
|
memorycache = { path = "../memorycache/" }
|
||||||
reqwest = { version = "0.11", features = ["json"] }
|
reqwest = { version = "0.11", features = ["json"] }
|
||||||
|
serde_derive = { version = "1" }
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1" }
|
||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
unic-langid = { version = "0.9" }
|
unic-langid = { version = "0.9" }
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl ApplicationWindow {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let date_label = Date::default();
|
let date_label = Date::default();
|
||||||
let header = adw::HeaderBar::builder()
|
layout.append(&date_label);
|
||||||
.title_widget(&date_label)
|
|
||||||
.build();
|
|
||||||
layout.append(&header);
|
|
||||||
|
|
||||||
let events = Events::default();
|
let events = Events::default();
|
||||||
layout.append(&events);
|
layout.append(&events);
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use chrono::Datelike;
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use ifc::IFC;
|
||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
pub struct DatePrivate {
|
pub struct DatePrivate {
|
||||||
date: Rc<RefCell<NaiveDate>>,
|
date: Rc<RefCell<IFC>>,
|
||||||
label: Rc<RefCell<gtk::Label>>,
|
label: Rc<RefCell<gtk::Label>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for DatePrivate {
|
impl Default for DatePrivate {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let date = chrono::Local::now().date_naive();
|
let date = chrono::Local::now().date_naive();
|
||||||
|
let year = date.year();
|
||||||
|
let date = date.with_year(year + 10000).unwrap();
|
||||||
Self {
|
Self {
|
||||||
date: Rc::new(RefCell::new(date)),
|
date: Rc::new(RefCell::new(IFC::from(date))),
|
||||||
label: Rc::new(RefCell::new(gtk::Label::new(None))),
|
label: Rc::new(RefCell::new(gtk::Label::new(None))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,16 +52,19 @@ impl Default for Date {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Date {
|
impl Date {
|
||||||
pub fn update_date(&self, date: NaiveDate) {
|
pub fn update_date(&self, date: IFC) {
|
||||||
*self.imp().date.borrow_mut() = date;
|
*self.imp().date.borrow_mut() = date;
|
||||||
self.redraw();
|
self.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn redraw(&self) {
|
fn redraw(&self) {
|
||||||
let date = self.imp().date.borrow();
|
let date = self.imp().date.borrow().clone();
|
||||||
self.imp()
|
self.imp().label.borrow_mut().set_text(&format!(
|
||||||
.label
|
"{:?}, {:?} {}, {}",
|
||||||
.borrow_mut()
|
date.weekday(),
|
||||||
.set_text(&date.format("%Y %B %d").to_string());
|
date.month(),
|
||||||
|
date.day(),
|
||||||
|
date.year()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use glib::Object;
|
use glib::Object;
|
||||||
use gtk::{prelude::*, subclass::prelude::*};
|
use gtk::{prelude::*, subclass::prelude::*};
|
||||||
|
use ifc::IFC;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -58,19 +59,19 @@ impl Events {
|
||||||
pub fn set_events(&self, events: YearlyEvents, next_event: solstices::Event) {
|
pub fn set_events(&self, events: YearlyEvents, next_event: solstices::Event) {
|
||||||
self.imp()
|
self.imp()
|
||||||
.spring_equinox
|
.spring_equinox
|
||||||
.update_date(events.spring_equinox.date_naive());
|
.update_date(IFC::from(events.spring_equinox.date_naive()));
|
||||||
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.summer_solstice
|
.summer_solstice
|
||||||
.update_date(events.summer_solstice.date_naive());
|
.update_date(IFC::from(events.summer_solstice.date_naive()));
|
||||||
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.autumn_equinox
|
.autumn_equinox
|
||||||
.update_date(events.autumn_equinox.date_naive());
|
.update_date(IFC::from(events.autumn_equinox.date_naive()));
|
||||||
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.winter_solstice
|
.winter_solstice
|
||||||
.update_date(events.winter_solstice.date_naive());
|
.update_date(IFC::from(events.winter_solstice.date_naive()));
|
||||||
|
|
||||||
self.imp().spring_equinox.remove_css_class("highlight");
|
self.imp().spring_equinox.remove_css_class("highlight");
|
||||||
self.imp().summer_solstice.remove_css_class("highlight");
|
self.imp().summer_solstice.remove_css_class("highlight");
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
|
use chrono::{Datelike, Local, Utc};
|
||||||
|
use geo_types::{Latitude, Longitude};
|
||||||
|
use glib::Sender;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use ifc::IFC;
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
env,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
use async_std::channel::Sender;
|
|
||||||
use chrono::{Datelike, Local, Utc};
|
|
||||||
use geo_types::{Latitude, Longitude};
|
|
||||||
use gtk::prelude::*;
|
|
||||||
|
|
||||||
mod app_window;
|
mod app_window;
|
||||||
use app_window::ApplicationWindow;
|
use app_window::ApplicationWindow;
|
||||||
|
|
||||||
|
@ -102,17 +102,14 @@ pub fn main() {
|
||||||
|
|
||||||
let now = Local::now();
|
let now = Local::now();
|
||||||
let state = State {
|
let state = State {
|
||||||
date: now.date_naive(),
|
date: IFC::from(now.date_naive().with_year(now.year() + 10000).unwrap()),
|
||||||
next_event: EVENTS.next_event(now.with_timezone(&Utc)).unwrap(),
|
next_event: EVENTS.next_event(now.with_timezone(&Utc)).unwrap(),
|
||||||
events: EVENTS.yearly_events(now.year()).unwrap(),
|
events: EVENTS.yearly_events(now.year()).unwrap(),
|
||||||
transit: Some(transit),
|
transit: Some(transit),
|
||||||
};
|
};
|
||||||
|
|
||||||
let gtk_tx = core.tx.read().unwrap().clone();
|
if let Some(ref gtk_tx) = *core.tx.read().unwrap() {
|
||||||
|
let _ = gtk_tx.send(Message::Refresh(state.clone()));
|
||||||
if let Some(gtk_tx) = gtk_tx {
|
|
||||||
let state = state.clone();
|
|
||||||
let _ = gtk_tx.send(Message::Refresh(state)).await;
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(60));
|
std::thread::sleep(std::time::Duration::from_secs(60));
|
||||||
} else {
|
} else {
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
@ -122,17 +119,21 @@ pub fn main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
app.connect_activate(move |app| {
|
app.connect_activate(move |app| {
|
||||||
let (gtk_tx, gtk_rx) = async_std::channel::unbounded();
|
let (gtk_tx, gtk_rx) =
|
||||||
|
gtk::glib::MainContext::channel::<Message>(gtk::glib::Priority::DEFAULT);
|
||||||
|
|
||||||
*core.tx.write().unwrap() = Some(gtk_tx);
|
*core.tx.write().unwrap() = Some(gtk_tx);
|
||||||
|
|
||||||
let window = ApplicationWindow::new(app);
|
let window = ApplicationWindow::new(app);
|
||||||
window.window.present();
|
window.window.present();
|
||||||
|
|
||||||
glib::spawn_future_local(async move {
|
gtk_rx.attach(None, {
|
||||||
loop {
|
let window = window.clone();
|
||||||
let Message::Refresh(state) = gtk_rx.recv().await.unwrap();
|
move |msg| {
|
||||||
window.update_state(state);
|
let Message::Refresh(state) = msg;
|
||||||
|
ApplicationWindow::update_state(&window, state);
|
||||||
|
|
||||||
|
glib::ControlFlow::Continue
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
// http://astropixels.com/ephemeris/soleq2001.html
|
// http://astropixels.com/ephemeris/soleq2001.html
|
||||||
const SOLSTICE_TEXT: &str = "
|
const SOLSTICE_TEXT: &str = "
|
||||||
|
|
|
@ -2,11 +2,11 @@ use crate::{
|
||||||
solstices::{Event, YearlyEvents},
|
solstices::{Event, YearlyEvents},
|
||||||
soluna_client::SunMoon,
|
soluna_client::SunMoon,
|
||||||
};
|
};
|
||||||
use chrono::NaiveDate;
|
use ifc::IFC;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub date: NaiveDate,
|
pub date: IFC,
|
||||||
pub next_event: Event,
|
pub next_event: Event,
|
||||||
pub events: YearlyEvents,
|
pub events: YearlyEvents,
|
||||||
pub transit: Option<SunMoon>,
|
pub transit: Option<SunMoon>,
|
||||||
|
|
|
@ -58,6 +58,10 @@ fn setup_app_close_action(app: &adw::Application) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
for (name, value) in std::env::vars() {
|
||||||
|
println!("{}: {}", name, value);
|
||||||
|
}
|
||||||
|
|
||||||
// I still don't fully understand gio resources. resources_register_include! is convenient
|
// I still don't fully understand gio resources. resources_register_include! is convenient
|
||||||
// because I don't have to deal with filesystem locations at runtime. However, I think other
|
// because I don't have to deal with filesystem locations at runtime. However, I think other
|
||||||
// GTK applications do that rather than compiling the resources directly into the app. So, I'm
|
// GTK applications do that rather than compiling the resources directly into the app. So, I'm
|
||||||
|
|
27
flake.nix
27
flake.nix
|
@ -17,6 +17,21 @@
|
||||||
let
|
let
|
||||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||||
pkgs-unstable = import unstable { system = "x86_64-linux"; };
|
pkgs-unstable = import unstable { system = "x86_64-linux"; };
|
||||||
|
|
||||||
|
cargo_nix = pkgs.callPackage ./Cargo.nix {
|
||||||
|
nixpkgs = pkgs;
|
||||||
|
};
|
||||||
|
|
||||||
|
l10n-codegen-rust = pkgs.stdenv.mkDerivation {
|
||||||
|
name = "l10n-codegen-rust";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp ${cargo_nix.workspaceMembers.l10n.build}/bin/codegen-rust $out/bin/l10n-codegen-rust
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
in
|
in
|
||||||
pkgs.mkShell {
|
pkgs.mkShell {
|
||||||
name = "ld-tools-devshell";
|
name = "ld-tools-devshell";
|
||||||
|
@ -45,7 +60,8 @@
|
||||||
pkgs.udev
|
pkgs.udev
|
||||||
pkgs.wasm-pack
|
pkgs.wasm-pack
|
||||||
typeshare.packages."x86_64-linux".default
|
typeshare.packages."x86_64-linux".default
|
||||||
pkgs.nodePackages_latest.typescript-language-server
|
|
||||||
|
l10n-codegen-rust
|
||||||
];
|
];
|
||||||
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
|
||||||
ENV = "dev";
|
ENV = "dev";
|
||||||
|
@ -71,7 +87,8 @@
|
||||||
|
|
||||||
dashboard = attrs: { nativeBuildInputs = gtkNativeInputs; };
|
dashboard = attrs: { nativeBuildInputs = gtkNativeInputs; };
|
||||||
fitnesstrax = import ./fitnesstrax/app/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
fitnesstrax = import ./fitnesstrax/app/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
||||||
otg-gtk = import ./otg/gtk/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
kifu-gtk = import ./kifu/gtk/override.nix { gtkNativeInputs = gtkNativeInputs; };
|
||||||
|
xdg-test = import ./xdg-test/override.nix { wrapGAppsHook4 = pkgs.wrapGAppsHook4; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,7 +102,8 @@
|
||||||
dashboard = cargo_nix.workspaceMembers.dashboard.build;
|
dashboard = cargo_nix.workspaceMembers.dashboard.build;
|
||||||
file-service = cargo_nix.workspaceMembers.file-service.build;
|
file-service = cargo_nix.workspaceMembers.file-service.build;
|
||||||
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
fitnesstrax = cargo_nix.workspaceMembers.fitnesstrax.build;
|
||||||
otg-gtk = cargo_nix.workspaceMembers.otg-gtk.build;
|
kifu-gtk = cargo_nix.workspaceMembers.kifu-gtk.build;
|
||||||
|
xdg-test = cargo_nix.workspaceMembers.xdg-test.build;
|
||||||
|
|
||||||
all = pkgs.symlinkJoin {
|
all = pkgs.symlinkJoin {
|
||||||
name = "all";
|
name = "all";
|
||||||
|
@ -94,7 +112,8 @@
|
||||||
dashboard
|
dashboard
|
||||||
file-service
|
file-service
|
||||||
fitnesstrax
|
fitnesstrax
|
||||||
otg-gtk
|
# kifu-gtk
|
||||||
|
xdg-test
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "gm-dash"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
pipewire = "0.8.0"
|
|
||||||
serde = { version = "1.0.209", features = ["alloc", "derive"] }
|
|
||||||
serde_json = "1.0.127"
|
|
||||||
tokio = { version = "1.39.3", features = ["full"] }
|
|
||||||
warp = "0.3.7"
|
|
|
@ -1,25 +0,0 @@
|
||||||
use pipewire::{context::Context, main_loop::MainLoop};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mainloop = MainLoop::new(None)?;
|
|
||||||
let context = Context::new(&mainloop)?;
|
|
||||||
let core = context.connect(None)?;
|
|
||||||
let registry = core.get_registry()?;
|
|
||||||
|
|
||||||
let _listener = registry
|
|
||||||
.add_listener_local()
|
|
||||||
.global(|global| {
|
|
||||||
if global.props.and_then(|p| p.get("media.class")) == Some("Audio/Sink"){
|
|
||||||
println!(
|
|
||||||
"\t{:?} {:?}",
|
|
||||||
global.props.and_then(|p| p.get("node.description")),
|
|
||||||
global.props.and_then(|p| p.get("media.class"))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register();
|
|
||||||
|
|
||||||
mainloop.run();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
use pipewire::{context::Context, main_loop::MainLoop};
|
|
||||||
use std::{
|
|
||||||
net::{Ipv6Addr, SocketAddrV6},
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
use tokio::task::spawn_blocking;
|
|
||||||
use warp::{serve, Filter};
|
|
||||||
|
|
||||||
struct State_ {
|
|
||||||
device_list: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct State {
|
|
||||||
internal: Arc<RwLock<State_>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
fn new() -> State {
|
|
||||||
let internal = State_ {
|
|
||||||
device_list: vec![],
|
|
||||||
};
|
|
||||||
State {
|
|
||||||
internal: Arc::new(RwLock::new(internal)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_audio(&self, device: String) {
|
|
||||||
let mut st = self.internal.write().unwrap();
|
|
||||||
(*st).device_list.push(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn audio_devices(&self) -> Vec<String> {
|
|
||||||
let st = self.internal.read().unwrap();
|
|
||||||
(*st).device_list.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for State {
|
|
||||||
fn default() -> State {
|
|
||||||
State::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn server_main(state: State) {
|
|
||||||
let localhost: Ipv6Addr = "::1".parse().unwrap();
|
|
||||||
let server_addr = SocketAddrV6::new(localhost, 3001, 0, 0);
|
|
||||||
|
|
||||||
let root = warp::path!().map(|| "ok".to_string());
|
|
||||||
let list_output_devices = warp::path!("output_devices").map({
|
|
||||||
let state = state.clone();
|
|
||||||
move || {
|
|
||||||
let devices = state.audio_devices();
|
|
||||||
serde_json::to_string(&devices).unwrap()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let routes = root.or(list_output_devices);
|
|
||||||
|
|
||||||
serve(routes).run(server_addr).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_add_audio_device(state: State, props: &pipewire::spa::utils::dict::DictRef)
|
|
||||||
{
|
|
||||||
if props.get("media.class") == Some("Audio/Sink") {
|
|
||||||
if let Some(device_name) = props.get("node.description") {
|
|
||||||
state.add_audio(device_name.to_owned());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipewire_loop(state: State) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let mainloop = MainLoop::new(None)?;
|
|
||||||
let context = Context::new(&mainloop)?;
|
|
||||||
let core = context.connect(None)?;
|
|
||||||
let registry = core.get_registry()?;
|
|
||||||
|
|
||||||
let _listener = registry
|
|
||||||
.add_listener_local()
|
|
||||||
.global({
|
|
||||||
let state = state.clone();
|
|
||||||
move |global_data| {
|
|
||||||
if let Some(props) = global_data.props {
|
|
||||||
handle_add_audio_device(state.clone(), props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.register();
|
|
||||||
|
|
||||||
mainloop.run();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pipewire_main(state: State) {
|
|
||||||
pipewire_loop(state).expect("pipewire should not error");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() {
|
|
||||||
let state = State::default();
|
|
||||||
|
|
||||||
spawn_blocking({
|
|
||||||
let state = state.clone();
|
|
||||||
move || pipewire_main(state)
|
|
||||||
});
|
|
||||||
|
|
||||||
server_main(state.clone()).await;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
|
@ -1,46 +0,0 @@
|
||||||
# Getting Started with Create React App
|
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
|
||||||
|
|
||||||
## Available Scripts
|
|
||||||
|
|
||||||
In the project directory, you can run:
|
|
||||||
|
|
||||||
### `npm start`
|
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
|
||||||
You will also see any lint errors in the console.
|
|
||||||
|
|
||||||
### `npm test`
|
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
|
||||||
|
|
||||||
### `npm run build`
|
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
|
||||||
|
|
||||||
### `npm run eject`
|
|
||||||
|
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
|
||||||
|
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
|
||||||
|
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
|
||||||
|
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
|
||||||
|
|
||||||
## Learn More
|
|
||||||
|
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
|
||||||
|
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,45 +0,0 @@
|
||||||
{
|
|
||||||
"name": "ui",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"private": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
|
||||||
"@testing-library/react": "^13.4.0",
|
|
||||||
"@testing-library/user-event": "^13.5.0",
|
|
||||||
"@types/jest": "^27.5.2",
|
|
||||||
"@types/node": "^16.18.105",
|
|
||||||
"@types/react": "^18.3.3",
|
|
||||||
"@types/react-dom": "^18.3.0",
|
|
||||||
"classnames": "^2.5.1",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-dom": "^18.3.1",
|
|
||||||
"react-router-dom": "^6.26.1",
|
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"web-vitals": "^2.1.4"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "react-scripts start",
|
|
||||||
"build": "react-scripts build",
|
|
||||||
"test": "react-scripts test",
|
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
|
||||||
"eslintConfig": {
|
|
||||||
"extends": [
|
|
||||||
"react-app",
|
|
||||||
"react-app/jest"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"browserslist": {
|
|
||||||
"production": [
|
|
||||||
">0.2%",
|
|
||||||
"not dead",
|
|
||||||
"not op_mini all"
|
|
||||||
],
|
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.8 KiB |
|
@ -1,43 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>React App</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 9.4 KiB |
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"short_name": "React App",
|
|
||||||
"name": "Create React App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
|
@ -1,5 +0,0 @@
|
||||||
.layout {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import Dashboard from './Dashboard';
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
render(<Dashboard />);
|
|
||||||
const linkElement = screen.getByText(/learn react/i);
|
|
||||||
expect(linkElement).toBeInTheDocument();
|
|
||||||
});
|
|
|
@ -1,77 +0,0 @@
|
||||||
import './Dashboard.css';
|
|
||||||
import Card from './components/Card/Card';
|
|
||||||
import Launcher from './components/Launcher/Launcher';
|
|
||||||
import Launchpad from './components/Launchpad/Launchpad';
|
|
||||||
|
|
||||||
const LightThemes = () => <Card name="Light Themes">
|
|
||||||
<Launchpad
|
|
||||||
exclusive={true}
|
|
||||||
options={[
|
|
||||||
{ title: "Dark reds" },
|
|
||||||
{ title: "Watery" },
|
|
||||||
{ title: "Sunset" },
|
|
||||||
{ title: "Darkness" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
const LightSetup = () => <div> </div>
|
|
||||||
|
|
||||||
interface LightProps {
|
|
||||||
name: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Light = ({ name }: LightProps) => <div> <p> {name} </p> </div>
|
|
||||||
|
|
||||||
const Tracks = () => <Card name="Tracks">
|
|
||||||
<Launchpad
|
|
||||||
exclusive={false}
|
|
||||||
options={[
|
|
||||||
{ title: "City BGM" },
|
|
||||||
{ title: "Chat on the streets" },
|
|
||||||
{ title: "Abandoned structure" },
|
|
||||||
{ title: "Water dripping" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
interface TrackProps {
|
|
||||||
name: string,
|
|
||||||
}
|
|
||||||
const Track = ({ name }: TrackProps) => <Launcher title={name} />
|
|
||||||
|
|
||||||
const Presets = () => <Card name="Presets">
|
|
||||||
<Launchpad
|
|
||||||
exclusive={true}
|
|
||||||
options={[
|
|
||||||
{ title: "Gilcrest Falls day" },
|
|
||||||
{ title: "Gilcrest Falls night" },
|
|
||||||
{ title: "Empty colony" },
|
|
||||||
{ title: "Surk colony" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
interface PresetProps {
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// const Scene = ({ name }: PresetProps) => <Launcher title={name} activated={false} />
|
|
||||||
|
|
||||||
const SceneEditor = () => <div> </div>
|
|
||||||
|
|
||||||
const Dashboard = () => {
|
|
||||||
return (
|
|
||||||
<div className="app">
|
|
||||||
<div className="layout">
|
|
||||||
<Presets />
|
|
||||||
<div>
|
|
||||||
<LightThemes />
|
|
||||||
<Tracks />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Dashboard;
|
|
|
@ -1,78 +0,0 @@
|
||||||
.palette {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette div {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 1em;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-purple div.item-1 {
|
|
||||||
background-color: var(--purple-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-purple div.item-2 {
|
|
||||||
background-color: var(--purple-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-purple div.item-3 {
|
|
||||||
background-color: var(--purple-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-purple div.item-4 {
|
|
||||||
background-color: var(--purple-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-purple div.item-5 {
|
|
||||||
background-color: var(--purple-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-blue div.item-1 {
|
|
||||||
background-color: var(--blue-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-blue div.item-2 {
|
|
||||||
background-color: var(--blue-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-blue div.item-3 {
|
|
||||||
background-color: var(--blue-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-blue div.item-4 {
|
|
||||||
background-color: var(--blue-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-blue div.item-5 {
|
|
||||||
background-color: var(--blue-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-grey div.item-1 {
|
|
||||||
background-color: var(--grey-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-grey div.item-2 {
|
|
||||||
background-color: var(--grey-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-grey div.item-3 {
|
|
||||||
background-color: var(--grey-3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-grey div.item-4 {
|
|
||||||
background-color: var(--grey-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.palette-grey div.item-5 {
|
|
||||||
background-color: var(--grey-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.design_horizontal {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
import './Design.css'
|
|
||||||
import Launchpad from './components/Launchpad/Launchpad'
|
|
||||||
|
|
||||||
const PaletteGrey = () => <div className="palette palette-grey">
|
|
||||||
<div className="item-1" />
|
|
||||||
<div className="item-2" />
|
|
||||||
<div className="item-3" />
|
|
||||||
<div className="item-4" />
|
|
||||||
<div className="item-5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
const PalettePurple = () => <div className="palette palette-purple">
|
|
||||||
<div className="item-1" />
|
|
||||||
<div className="item-2" />
|
|
||||||
<div className="item-3" />
|
|
||||||
<div className="item-4" />
|
|
||||||
<div className="item-5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
const PaletteBlue = () => <div className="palette palette-blue">
|
|
||||||
<div className="item-1" />
|
|
||||||
<div className="item-2" />
|
|
||||||
<div className="item-3" />
|
|
||||||
<div className="item-4" />
|
|
||||||
<div className="item-5" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
const Launchpads = () => <div className="design_horizontal">
|
|
||||||
<Launchpad
|
|
||||||
exclusive={true}
|
|
||||||
options={[
|
|
||||||
{ title: "Grey" },
|
|
||||||
{ title: "Purple" },
|
|
||||||
{ title: "Blue" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Launchpad
|
|
||||||
exclusive={false}
|
|
||||||
flow={"vertical"}
|
|
||||||
options={[
|
|
||||||
{ title: "Grey" },
|
|
||||||
{ title: "Purple" },
|
|
||||||
{ title: "Blue" },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
const Design = () => <div>
|
|
||||||
<PaletteGrey />
|
|
||||||
<PalettePurple />
|
|
||||||
<PaletteBlue />
|
|
||||||
<Launchpads />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
export default Design;
|
|
|
@ -1,15 +0,0 @@
|
||||||
.card {
|
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: var(--spacer-l);
|
|
||||||
box-shadow: 4px 4px 4px 0px var(--shadow-1),
|
|
||||||
8px 8px 8px 0px var(--shadow-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__title {
|
|
||||||
color: var(--title-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card__body {
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { PropsWithChildren } from 'react';
|
|
||||||
import './Card.css';
|
|
||||||
|
|
||||||
interface CardProps {
|
|
||||||
name: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Card = ({ name, children }: PropsWithChildren<CardProps>) => (
|
|
||||||
<div className="card">
|
|
||||||
<h1 className="card__title"> {name} </h1>
|
|
||||||
<div className="card__body">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Card;
|
|
|
@ -1,11 +0,0 @@
|
||||||
.activator {
|
|
||||||
border: 1px solid black;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: var(--spacer-m);
|
|
||||||
padding: var(--spacer-s);
|
|
||||||
box-shadow: var(--shadow-deep);
|
|
||||||
}
|
|
||||||
|
|
||||||
.activator_enabled {
|
|
||||||
box-shadow: var(--activator-ring);
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
import './Launcher.css';
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export interface LauncherProps {
|
|
||||||
title: string,
|
|
||||||
icon?: string,
|
|
||||||
activated?: boolean,
|
|
||||||
onSelected?: (key: string) => void,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Launcher = ({ title, activated = false, onSelected = (key) => {} }: LauncherProps) => {
|
|
||||||
const classnames = activated ? "activator activator_enabled" : "activator";
|
|
||||||
console.log("classnames ", activated, classnames);
|
|
||||||
return (
|
|
||||||
<div className={classnames} onClick={() => onSelected(title)}>
|
|
||||||
<p> {title} </p>
|
|
||||||
</div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Launcher;
|
|
|
@ -1,18 +0,0 @@
|
||||||
.launchpad {
|
|
||||||
display: flex;
|
|
||||||
border: var(--border);
|
|
||||||
border-radius: var(--border-radius);
|
|
||||||
box-shadow: var(--shadow-depression);
|
|
||||||
margin: var(--spacer-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.launchpad_horizontal-flow {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
.launchpad_vertical-flow {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import './Launchpad.css';
|
|
||||||
import Launcher, { LauncherProps } from '../Launcher/Launcher';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
|
|
||||||
export interface Selectable {
|
|
||||||
onSelected?: (key: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Flow = "horizontal" | "vertical";
|
|
||||||
|
|
||||||
interface LaunchpadProps {
|
|
||||||
exclusive: boolean;
|
|
||||||
flow?: Flow;
|
|
||||||
options: Array<LauncherProps>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const exclusiveSelect = (state: { [key: string]: boolean }, targetId: string) => {
|
|
||||||
console.log("running exclusiveSelect on ", targetId);
|
|
||||||
return { [targetId]: true };
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiSelect = (state: { [key: string]: boolean }, targetId: string) => {
|
|
||||||
if (state[targetId]) {
|
|
||||||
return { ...state, [targetId]: false };
|
|
||||||
} else {
|
|
||||||
return { ...state, [targetId]: true };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Launchpad = ({ flow = "horizontal", options, exclusive }: LaunchpadProps) => {
|
|
||||||
const [selected, dispatch] = React.useReducer(exclusive ? exclusiveSelect : multiSelect, {});
|
|
||||||
|
|
||||||
let classOptions = [ "launchpad" ];
|
|
||||||
|
|
||||||
if (flow === "horizontal") {
|
|
||||||
classOptions.push("launchpad_horizontal-flow");
|
|
||||||
} else {
|
|
||||||
classOptions.push("launchpad_vertical-flow");
|
|
||||||
}
|
|
||||||
|
|
||||||
let tiedOptions = options.map(option =>
|
|
||||||
<Launcher key={option.title} title={option.title} onSelected={(key: string) => dispatch(key)} activated={selected[option.title]} />
|
|
||||||
);
|
|
||||||
|
|
||||||
return (<div className={classnames(classOptions)}> {tiedOptions} </div>)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Launchpad;
|
|
|
@ -1,50 +0,0 @@
|
||||||
:root {
|
|
||||||
--purple-1: hsl(265, 50%, 25%);
|
|
||||||
--purple-2: hsl(265, 60%, 35%);
|
|
||||||
--purple-3: hsl(265, 70%, 45%);
|
|
||||||
--purple-4: hsl(265, 80%, 55%);
|
|
||||||
--purple-5: hsl(265, 90%, 60%);
|
|
||||||
|
|
||||||
--blue-1: hsl(210, 50%, 25%);
|
|
||||||
--blue-2: hsl(210, 60%, 35%);
|
|
||||||
--blue-3: hsl(210, 70%, 45%);
|
|
||||||
--blue-4: hsl(210, 80%, 55%);
|
|
||||||
--blue-5: hsl(210, 90%, 65%);
|
|
||||||
|
|
||||||
--grey-1: hsl(210, 0%, 25%);
|
|
||||||
--grey-2: hsl(210, 0%, 40%);
|
|
||||||
--grey-3: hsl(210, 0%, 55%);
|
|
||||||
--grey-4: hsl(210, 0%, 70%);
|
|
||||||
--grey-5: hsl(210, 0%, 85%);
|
|
||||||
|
|
||||||
--title-color: var(--grey-1);
|
|
||||||
|
|
||||||
--border: 1px solid var(--purple-1);
|
|
||||||
--activator-ring: 0px 0px 8px 4px var(--blue-4);
|
|
||||||
--shadow-depression: inset 1px 1px 2px 0px var(--purple-1);
|
|
||||||
--shadow-shallow: 1px 1px 2px 0px var(--purple-1);
|
|
||||||
--shadow-deep: 2px 2px 4px 0px var(--purple-1),
|
|
||||||
4px 4px 8px 0px var(--purple-2);
|
|
||||||
|
|
||||||
--border-radius: 8px;
|
|
||||||
|
|
||||||
--spacer-xs: 2px;
|
|
||||||
--spacer-s: 4px;
|
|
||||||
--spacer-m: 8px;
|
|
||||||
--spacer-l: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
background-color: var(--grey-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import ReactDOM from 'react-dom/client';
|
|
||||||
import './index.css';
|
|
||||||
import Dashboard from './Dashboard';
|
|
||||||
import Design from './Design';
|
|
||||||
import reportWebVitals from './reportWebVitals';
|
|
||||||
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
|
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
|
||||||
document.getElementById('root') as HTMLElement
|
|
||||||
);
|
|
||||||
const router = createBrowserRouter([
|
|
||||||
{
|
|
||||||
path: "/",
|
|
||||||
element: <Dashboard />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/design",
|
|
||||||
element: <Design />,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
root.render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<RouterProvider router={router} />
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
|
||||||
|
|
||||||
// If you want to start measuring performance in your app, pass a function
|
|
||||||
// to log results (for example: reportWebVitals(console.log))
|
|
||||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
|
||||||
reportWebVitals();
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
|
Before Width: | Height: | Size: 2.6 KiB |
|
@ -1 +0,0 @@
|
||||||
/// <reference types="react-scripts" />
|
|
|
@ -1,15 +0,0 @@
|
||||||
import { ReportHandler } from 'web-vitals';
|
|
||||||
|
|
||||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
|
||||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
|
||||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
|
||||||
getCLS(onPerfEntry);
|
|
||||||
getFID(onPerfEntry);
|
|
||||||
getFCP(onPerfEntry);
|
|
||||||
getLCP(onPerfEntry);
|
|
||||||
getTTFB(onPerfEntry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default reportWebVitals;
|
|
|
@ -1,5 +0,0 @@
|
||||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
||||||
// allows you to do things like:
|
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
|
||||||
import '@testing-library/jest-dom';
|
|
|
@ -1,26 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es5",
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx"
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -0,0 +1,591 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "android_system_properties"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bumpalo"
|
||||||
|
version = "3.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.79"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chrono"
|
||||||
|
version = "0.4.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||||
|
dependencies = [
|
||||||
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
"serde",
|
||||||
|
"time",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "codespan-reporting"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||||
|
dependencies = [
|
||||||
|
"termcolor",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cool_asserts"
|
||||||
|
version = "2.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee9f254e53f61e2688d3677fa2cbe4e9b950afd56f48819c98817417cf6b28ec"
|
||||||
|
dependencies = [
|
||||||
|
"indent_write",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "core-foundation-sys"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxx"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cxxbridge-flags",
|
||||||
|
"cxxbridge-macro",
|
||||||
|
"link-cplusplus",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxx-build"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"codespan-reporting",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"scratch",
|
||||||
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-flags"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cxxbridge-macro"
|
||||||
|
version = "1.0.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grid"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0634107a3a005070dd73e27e74ecb691a94e9e5ba7829f434db7fbf73a6b5c47"
|
||||||
|
dependencies = [
|
||||||
|
"no-std-compat",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone"
|
||||||
|
version = "0.1.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||||
|
dependencies = [
|
||||||
|
"android_system_properties",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"iana-time-zone-haiku",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"windows",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iana-time-zone-haiku"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||||
|
dependencies = [
|
||||||
|
"cxx",
|
||||||
|
"cxx-build",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indent_write"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "js-sys"
|
||||||
|
version = "0.3.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||||
|
dependencies = [
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kifu-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"cool_asserts",
|
||||||
|
"grid",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sgf",
|
||||||
|
"thiserror",
|
||||||
|
"typeshare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "link-cplusplus"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimal-lexical"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "no-std-compat"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "7.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"minimal-lexical",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scratch"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.162"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.162"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.96"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"nom",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
"typeshare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeshare"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f44d1a2f454cb35fbe05b218c410792697e76bd868f48d3a418f2cd1a7d527d6"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"typeshare-annotation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typeshare-annotation"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc670d0e358428857cc3b4bf504c691e572fccaec9542ff09212d3f13d74b7a9"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen"
|
||||||
|
version = "0.2.84"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"wasm-bindgen-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-backend"
|
||||||
|
version = "0.2.84"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||||
|
dependencies = [
|
||||||
|
"bumpalo",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro"
|
||||||
|
version = "0.2.84"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"wasm-bindgen-macro-support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-macro-support"
|
||||||
|
version = "0.2.84"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
"wasm-bindgen-backend",
|
||||||
|
"wasm-bindgen-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-bindgen-shared"
|
||||||
|
version = "0.2.84"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.48.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "otg-core"
|
name = "kifu-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@ -14,9 +14,7 @@ sgf = { path = "../../sgf" }
|
||||||
grid = { version = "0.9" }
|
grid = { version = "0.9" }
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
serde = { version = "1", features = [ "derive" ] }
|
serde = { version = "1", features = [ "derive" ] }
|
||||||
nary_tree = { version = "0.4" }
|
|
||||||
thiserror = { version = "1" }
|
thiserror = { version = "1" }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
cool_asserts = { version = "2" }
|
cool_asserts = { version = "2" }
|
|
@ -1,37 +1,21 @@
|
||||||
/*
|
|
||||||
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
|
||||||
|
|
||||||
This file is part of On the Grid.
|
|
||||||
|
|
||||||
On the Grid is free software: you can redistribute it and/or modify it under the terms of the GNU
|
|
||||||
General Public License as published by the Free Software Foundation, either version 3 of the
|
|
||||||
License, or (at your option) any later version.
|
|
||||||
|
|
||||||
On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
|
||||||
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
database::Database,
|
database::Database,
|
||||||
library, settings,
|
types::{AppState, Config, ConfigOption, DatabasePath, GameState, Player, Rank},
|
||||||
types::{Config, LibraryPath},
|
ui::{configuration, home, playing_field, ConfigurationView, HomeView, PlayingFieldView},
|
||||||
};
|
};
|
||||||
use async_std::channel::{Receiver, Sender};
|
use async_std::channel::{Receiver, Sender};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{Arc, RwLock, RwLockReadGuard};
|
use std::{
|
||||||
|
path::PathBuf,
|
||||||
|
sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
pub trait Observable<T> {
|
pub trait Observable<T> {
|
||||||
fn subscribe(&self) -> Receiver<T>;
|
fn subscribe(&self) -> Receiver<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum CoreRequest {
|
pub enum CoreRequest {
|
||||||
Library(library::LibraryRequest),
|
|
||||||
Settings(settings::SettingsRequest),
|
|
||||||
/*
|
|
||||||
ChangeSetting(ChangeSettingRequest),
|
ChangeSetting(ChangeSettingRequest),
|
||||||
CreateGame(CreateGameRequest),
|
CreateGame(CreateGameRequest),
|
||||||
Home,
|
Home,
|
||||||
|
@ -39,10 +23,8 @@ pub enum CoreRequest {
|
||||||
PlayingField,
|
PlayingField,
|
||||||
PlayStone(PlayStoneRequest),
|
PlayStone(PlayStoneRequest),
|
||||||
StartGame,
|
StartGame,
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum ChangeSettingRequest {
|
pub enum ChangeSettingRequest {
|
||||||
LibraryPath(String),
|
LibraryPath(String),
|
||||||
|
@ -79,104 +61,42 @@ impl From<HotseatPlayerRequest> for Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum CoreResponse {
|
pub enum CoreResponse {
|
||||||
Library(library::LibraryResponse),
|
ConfigurationView(ConfigurationView),
|
||||||
Settings(settings::SettingsResponse),
|
HomeView(HomeView),
|
||||||
|
PlayingFieldView(PlayingFieldView),
|
||||||
|
UpdatedConfigurationView(ConfigurationView),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<library::LibraryResponse> for CoreResponse {
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
fn from(r: library::LibraryResponse) -> Self {
|
|
||||||
Self::Library(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<settings::SettingsResponse> for CoreResponse {
|
|
||||||
fn from(r: settings::SettingsResponse) -> Self {
|
|
||||||
Self::Settings(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum CoreNotification {
|
pub enum CoreNotification {
|
||||||
ConfigurationUpdated(Config),
|
|
||||||
BoardUpdated,
|
BoardUpdated,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Core {
|
pub struct Core {
|
||||||
config: Arc<RwLock<Config>>,
|
// config: Arc<RwLock<Config>>,
|
||||||
// state: Arc<RwLock<AppState>>,
|
// state: Arc<RwLock<AppState>>,
|
||||||
library: Arc<RwLock<Option<Database>>>,
|
database: Arc<RwLock<Option<Database>>>,
|
||||||
subscribers: Arc<RwLock<Vec<Sender<CoreNotification>>>>,
|
subscribers: Arc<RwLock<Vec<Sender<CoreNotification>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new(config: Config) -> Self {
|
pub fn new(_config: Config) -> Self {
|
||||||
let library = match config.get::<LibraryPath>() {
|
// let config = Config::from_path(config_path).expect("configuration to open");
|
||||||
Some(ref path) if path.to_path_buf().exists() => {
|
|
||||||
Some(Database::open_path(path.to_path_buf()).unwrap())
|
// let state = Arc::new(RwLock::new(AppState::new(db_path)));
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
config: Arc::new(RwLock::new(config)),
|
// config: Arc::new(RwLock::new(config)),
|
||||||
// state,
|
// state,
|
||||||
library: Arc::new(RwLock::new(library)),
|
database: Arc::new(RwLock::new(None)),
|
||||||
subscribers: Arc::new(RwLock::new(vec![])),
|
subscribers: Arc::new(RwLock::new(vec![])),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(&self) -> Config {
|
|
||||||
self.config.read().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change the configuration of the Core. This function will update any relevant core
|
|
||||||
/// functions, especially the contents of the library, and it will notify any subscribed objects
|
|
||||||
/// that the configuration has changed.
|
|
||||||
///
|
|
||||||
/// It will not handle persisting the new configuration, as the backing store for the
|
|
||||||
/// configuration is not a decision for the core library.
|
|
||||||
pub async fn set_config(&self, config: Config) {
|
|
||||||
*self.config.write().unwrap() = config.clone();
|
|
||||||
|
|
||||||
// let db = library::read_library(self.config.read().unwrap().get::<LibraryPath>()).await;
|
|
||||||
let library_path = self.config.read().unwrap().get::<LibraryPath>();
|
|
||||||
if let Some(ref path) = library_path {
|
|
||||||
self.load_library(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.notify(CoreNotification::ConfigurationUpdated(config.clone()))
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_library(&self, path: &LibraryPath) {
|
|
||||||
let db = Database::open_path(path.to_path_buf()).unwrap();
|
|
||||||
*self.library.write().unwrap() = Some(db);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn library(&self) -> RwLockReadGuard<'_, Option<Database>> {
|
|
||||||
self.library.read().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
|
||||||
match request {
|
|
||||||
CoreRequest::Library(request) => library::handle(self, request).await.into(),
|
|
||||||
CoreRequest::Settings(request) => settings::handle(self, request).await.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn notify(&self, notification: CoreNotification) {
|
|
||||||
let subscribers = self.subscribers.read().unwrap().clone();
|
|
||||||
for subscriber in subscribers {
|
|
||||||
let subscriber = subscriber.clone();
|
|
||||||
let _ = subscriber.send(notification.clone()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
pub async fn dispatch(&self, request: CoreRequest) -> CoreResponse {
|
||||||
match request {
|
match request {
|
|
@ -1,35 +1,9 @@
|
||||||
/*
|
|
||||||
Copyright 2024, Savanni D'Gerinel <savanni@luminescent-dreams.com>
|
|
||||||
|
|
||||||
This file is part of On the Grid.
|
|
||||||
|
|
||||||
On the Grid is free software: you can redistribute it and/or modify it under the terms of
|
|
||||||
the GNU General Public License as published by the Free Software Foundation, either version 3 of
|
|
||||||
the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
On the Grid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
||||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along with On the Grid. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TBQH, I don't recall what state this object is in, but I do know that I might have some troubles
|
|
||||||
// integrating it with a game record. Some of the time here is going to be me reading (and
|
|
||||||
// documenting) my code from almost a year ago.
|
|
||||||
//
|
|
||||||
use crate::{BoardError, Color, Size};
|
use crate::{BoardError, Color, Size};
|
||||||
use sgf::{GameNode, MoveNode};
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct Goban {
|
pub struct Goban {
|
||||||
/// The size of the board. Usually this is symetrical, but I have actually played a 5x25 game.
|
|
||||||
/// These are fun for novelty, but don't lend much to understanding the game.
|
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
|
|
||||||
/// I found that it was easiest to track groups of stones than to track individual stones on the
|
|
||||||
/// board. So, I just keep track of all of the groups.
|
|
||||||
pub groups: Vec<Group>,
|
pub groups: Vec<Group>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,18 +62,10 @@ impl Goban {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a board state from an iterator of coordinates and the color of any stone present on
|
|
||||||
/// the board. As we walk through the iterator, we play each stone as though it were being
|
|
||||||
/// played in a game.
|
|
||||||
///
|
|
||||||
/// This would not work at all if we wanted to set up an impossible board state, given that
|
|
||||||
/// groups of stones get automatically removed once surrounded.
|
|
||||||
pub fn from_coordinates(
|
pub fn from_coordinates(
|
||||||
coordinates: impl IntoIterator<Item = (Coordinate, Color)>,
|
mut coordinates: impl Iterator<Item = (Coordinate, Color)>,
|
||||||
) -> Result<Self, BoardError> {
|
) -> Result<Self, BoardError> {
|
||||||
coordinates
|
coordinates.try_fold(Self::new(), |board, (coordinate, color)| {
|
||||||
.into_iter()
|
|
||||||
.try_fold(Self::new(), |board, (coordinate, color)| {
|
|
||||||
board.place_stone(coordinate, color)
|
board.place_stone(coordinate, color)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -112,77 +78,37 @@ pub struct Coordinate {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Goban {
|
impl Goban {
|
||||||
/// place_stone is the most fundamental function of this object. This is as though a player put
|
|
||||||
/// a stone on the board and evaluated the consequences.
|
|
||||||
///
|
|
||||||
/// This function does not enforce turn order.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use otg_core::{Color, Size, Coordinate, Goban};
|
|
||||||
/// use cool_asserts::assert_matches;
|
|
||||||
///
|
|
||||||
/// let goban = Goban::new();
|
|
||||||
/// assert_eq!(goban.size, Size{ width: 19, height: 19 });
|
|
||||||
/// let move_result = goban.place_stone(Coordinate{ column: 4, row: 4 }, Color::Black);
|
|
||||||
/// assert_matches!(move_result, Goban);
|
|
||||||
/// ```
|
|
||||||
pub fn place_stone(mut self, coordinate: Coordinate, color: Color) -> Result<Self, BoardError> {
|
pub fn place_stone(mut self, coordinate: Coordinate, color: Color) -> Result<Self, BoardError> {
|
||||||
// Bail out immediately if there is already a stone at this location.
|
|
||||||
if self.stone(&coordinate).is_some() {
|
if self.stone(&coordinate).is_some() {
|
||||||
return Err(BoardError::InvalidPosition);
|
return Err(BoardError::InvalidPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all friendly groups adjacent to this stone. First, calculate the adjacent
|
|
||||||
// coordinates. Then see if there is any group which contains that coordinate. If not, this
|
|
||||||
// stone forms a new group of its own.
|
|
||||||
//
|
|
||||||
// A little subtle here is that this stone will be added to *every* adjoining friendly
|
|
||||||
// group. This normally means only that a group gets bigger, but it could also cause two
|
|
||||||
// groups to share a stone, which means they're now a single group.
|
|
||||||
let mut friendly_group = self
|
let mut friendly_group = self
|
||||||
.adjacencies(&coordinate)
|
.adjacencies(&coordinate)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|c| self.stone(c) == Some(color))
|
.filter(|c| self.stone(c) == Some(color))
|
||||||
.filter_map(|c| self.group(&c).map(|g| g.coordinates.clone()))
|
.filter_map(|c| self.group(&c).map(|g| g.coordinates.clone()))
|
||||||
// In fact, this last step actually connects the coordinates of those friendly groups
|
|
||||||
// into a single large group.
|
|
||||||
.fold(HashSet::new(), |acc, set| {
|
.fold(HashSet::new(), |acc, set| {
|
||||||
acc.union(&set).cloned().collect()
|
acc.union(&set).cloned().collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
// This is a little misnamed. This is a HashSet, not a full Group.
|
|
||||||
friendly_group.insert(coordinate);
|
friendly_group.insert(coordinate);
|
||||||
|
|
||||||
// Remove all groups which contain the stones overlapping with this friendly group.
|
|
||||||
self.groups
|
self.groups
|
||||||
.retain(|g| g.coordinates.is_disjoint(&friendly_group));
|
.retain(|g| g.coordinates.is_disjoint(&friendly_group));
|
||||||
|
|
||||||
// Generate a new friendly group given the coordinates.
|
|
||||||
let friendly_group = Group {
|
let friendly_group = Group {
|
||||||
color,
|
color,
|
||||||
coordinates: friendly_group,
|
coordinates: friendly_group,
|
||||||
};
|
};
|
||||||
// Now add the group back to the board.
|
|
||||||
self.groups.push(friendly_group.clone());
|
self.groups.push(friendly_group.clone());
|
||||||
|
|
||||||
// Now, find all groups adjacent to this one. Those are the only groups that this move is
|
|
||||||
// going to impact. Calculate their liberties.
|
|
||||||
let adjacent_groups = self.adjacent_groups(&friendly_group);
|
let adjacent_groups = self.adjacent_groups(&friendly_group);
|
||||||
for group in adjacent_groups {
|
for group in adjacent_groups {
|
||||||
// Any group that has been reduced to 0 liberties should now be removed from the board.
|
|
||||||
//
|
|
||||||
// TODO: capture rules: we're not counting captured stones yet. Okay with some scoring
|
|
||||||
// methods, but not all.
|
|
||||||
if self.liberties(&group) == 0 {
|
if self.liberties(&group) == 0 {
|
||||||
self.remove_group(&group);
|
self.remove_group(&group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, recalculate the liberties of this friendly group. If this group has been reduced to
|
|
||||||
// zero liberties, after all captures have been accounted for, the move is an illegal
|
|
||||||
// self-capture. Drop all of the work we've done and return an error.
|
|
||||||
if self.liberties(&friendly_group) == 0 {
|
if self.liberties(&friendly_group) == 0 {
|
||||||
return Err(BoardError::SelfCapture);
|
return Err(BoardError::SelfCapture);
|
||||||
}
|
}
|
||||||
|
@ -190,53 +116,6 @@ impl Goban {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Apply a list of moves to the board and return the final board. The moves will be played as
|
|
||||||
/// though they are live moves played normally, but this function is for generating a board
|
|
||||||
/// state from a game record. All of the moves will be played in the order given. This does not
|
|
||||||
/// allow for the branching which is natural in a game review.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use otg_core::{Color, Size, Coordinate, Goban};
|
|
||||||
/// use cool_asserts::assert_matches;
|
|
||||||
/// use sgf::{GameNode, MoveNode, Move};
|
|
||||||
///
|
|
||||||
/// let goban = Goban::new();
|
|
||||||
/// let moves = vec![
|
|
||||||
/// GameNode::MoveNode(MoveNode::new(sgf::Color::Black, Move::Move("dd".to_owned()))),
|
|
||||||
/// GameNode::MoveNode(MoveNode::new(sgf::Color::White, Move::Move("pp".to_owned()))),
|
|
||||||
/// GameNode::MoveNode(MoveNode::new(sgf::Color::Black, Move::Move("dp".to_owned()))),
|
|
||||||
/// ];
|
|
||||||
/// let moves_: Vec<&GameNode> = moves.iter().collect();
|
|
||||||
/// let goban = goban.apply_moves(moves_).expect("the test to have valid moves");
|
|
||||||
///
|
|
||||||
/// assert_eq!(goban.stone(&Coordinate{ row: 3, column: 3 }), Some(Color::Black));
|
|
||||||
/// assert_eq!(goban.stone(&Coordinate{ row: 15, column: 15 }), Some(Color::White));
|
|
||||||
/// assert_eq!(goban.stone(&Coordinate{ row: 15, column: 3 }), Some(Color::Black));
|
|
||||||
/// ```
|
|
||||||
pub fn apply_moves<'a>(
|
|
||||||
self,
|
|
||||||
moves: impl IntoIterator<Item = &'a GameNode>,
|
|
||||||
) -> Result<Goban, BoardError> {
|
|
||||||
let mut s = self;
|
|
||||||
for m in moves.into_iter() {
|
|
||||||
match m {
|
|
||||||
GameNode::MoveNode(node) => s = s.apply_move_node(node)?,
|
|
||||||
GameNode::SetupNode(_n) => unimplemented!("setup nodes aren't processed yet"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_move_node(self, m: &MoveNode) -> Result<Goban, BoardError> {
|
|
||||||
if let Some((row, column)) = m.mv.coordinate() {
|
|
||||||
self.place_stone(Coordinate { row, column }, Color::from(&m.color))
|
|
||||||
} else {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stone(&self, coordinate: &Coordinate) -> Option<Color> {
|
pub fn stone(&self, coordinate: &Coordinate) -> Option<Color> {
|
||||||
self.groups
|
self.groups
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -244,17 +123,17 @@ impl Goban {
|
||||||
.map(|g| g.color)
|
.map(|g| g.color)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group(&self, coordinate: &Coordinate) -> Option<&Group> {
|
pub fn group(&self, coordinate: &Coordinate) -> Option<&Group> {
|
||||||
self.groups
|
self.groups
|
||||||
.iter()
|
.iter()
|
||||||
.find(|g| g.coordinates.contains(coordinate))
|
.find(|g| g.coordinates.contains(coordinate))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_group(&mut self, group: &Group) {
|
pub fn remove_group(&mut self, group: &Group) {
|
||||||
self.groups.retain(|g| g != group);
|
self.groups.retain(|g| g != group);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adjacent_groups(&self, group: &Group) -> Vec<Group> {
|
pub fn adjacent_groups(&self, group: &Group) -> Vec<Group> {
|
||||||
let adjacent_spaces = self.group_halo(group).into_iter();
|
let adjacent_spaces = self.group_halo(group).into_iter();
|
||||||
let mut grps: Vec<Group> = Vec::new();
|
let mut grps: Vec<Group> = Vec::new();
|
||||||
|
|
||||||
|
@ -274,7 +153,7 @@ impl Goban {
|
||||||
grps
|
grps
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group_halo(&self, group: &Group) -> HashSet<Coordinate> {
|
pub fn group_halo(&self, group: &Group) -> HashSet<Coordinate> {
|
||||||
group
|
group
|
||||||
.coordinates
|
.coordinates
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -282,14 +161,14 @@ impl Goban {
|
||||||
.collect::<HashSet<Coordinate>>()
|
.collect::<HashSet<Coordinate>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn liberties(&self, group: &Group) -> usize {
|
pub fn liberties(&self, group: &Group) -> usize {
|
||||||
self.group_halo(group)
|
self.group_halo(group)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|c| self.stone(c).is_none())
|
.filter(|c| self.stone(c).is_none())
|
||||||
.count()
|
.count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adjacencies(&self, coordinate: &Coordinate) -> Vec<Coordinate> {
|
pub fn adjacencies(&self, coordinate: &Coordinate) -> Vec<Coordinate> {
|
||||||
let mut v = Vec::new();
|
let mut v = Vec::new();
|
||||||
if coordinate.column > 0 {
|
if coordinate.column > 0 {
|
||||||
v.push(Coordinate {
|
v.push(Coordinate {
|
||||||
|
@ -314,7 +193,7 @@ impl Goban {
|
||||||
v.into_iter().filter(|c| self.within_board(c)).collect()
|
v.into_iter().filter(|c| self.within_board(c)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn within_board(&self, coordinate: &Coordinate) -> bool {
|
pub fn within_board(&self, coordinate: &Coordinate) -> bool {
|
||||||
coordinate.column < self.size.width && coordinate.row < self.size.height
|
coordinate.column < self.size.width && coordinate.row < self.size.height
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -611,6 +490,7 @@ mod test {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
println!("{}", board);
|
||||||
for (board, coordinate, group, liberties) in test_cases {
|
for (board, coordinate, group, liberties) in test_cases {
|
||||||
assert_eq!(board.group(&coordinate), group.as_ref());
|
assert_eq!(board.group(&coordinate), group.as_ref());
|
||||||
assert_eq!(
|
assert_eq!(
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{io::Read, path::PathBuf};
|
use std::{io::Read, path::PathBuf};
|
||||||
|
|
||||||
use sgf::{parse_sgf, GameRecord};
|
use sgf::{parse_sgf, Game};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -21,12 +21,12 @@ impl From<std::io::Error> for Error {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
games: Vec<GameRecord>,
|
games: Vec<Game>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Database {
|
impl Database {
|
||||||
pub fn open_path(path: PathBuf) -> Result<Database, Error> {
|
pub fn open_path(path: PathBuf) -> Result<Database, Error> {
|
||||||
let mut games: Vec<GameRecord> = Vec::new();
|
let mut games: Vec<Game> = Vec::new();
|
||||||
|
|
||||||
let extension = PathBuf::from("sgf").into_os_string();
|
let extension = PathBuf::from("sgf").into_os_string();
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ impl Database {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
match parse_sgf(&buffer) {
|
match parse_sgf(&buffer) {
|
||||||
Ok(sgfs) => {
|
Ok(sgfs) => {
|
||||||
let mut sgfs =
|
for sgf in sgfs {
|
||||||
sgfs.into_iter().flatten().collect::<Vec<sgf::GameRecord>>();
|
games.push(sgf);
|
||||||
games.append(&mut sgfs);
|
}
|
||||||
}
|
}
|
||||||
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),
|
Err(err) => println!("Error parsing {:?}: {:?}", entry.path(), err),
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ impl Database {
|
||||||
Ok(Database { games })
|
Ok(Database { games })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn all_games(&self) -> impl Iterator<Item = &GameRecord> {
|
pub fn all_games(&self) -> impl Iterator<Item = &Game> {
|
||||||
self.games.iter()
|
self.games.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,11 +82,11 @@ mod test {
|
||||||
Database::open_path(PathBuf::from("fixtures/five_games/")).expect("database to open");
|
Database::open_path(PathBuf::from("fixtures/five_games/")).expect("database to open");
|
||||||
assert_eq!(db.all_games().count(), 5);
|
assert_eq!(db.all_games().count(), 5);
|
||||||
|
|
||||||
assert_matches!(db.all_games().find(|g| g.black_player.name == Some("Steve".to_owned())),
|
assert_matches!(db.all_games().find(|g| g.info.black_player == Some("Steve".to_owned())),
|
||||||
Some(game) => {
|
Some(game) => {
|
||||||
assert_eq!(game.black_player.name, Some("Steve".to_owned()));
|
assert_eq!(game.info.black_player, Some("Steve".to_owned()));
|
||||||
assert_eq!(game.white_player.name, Some("Savanni".to_owned()));
|
assert_eq!(game.info.white_player, Some("Savanni".to_owned()));
|
||||||
assert_eq!(game.dates, vec![Date::Date(chrono::NaiveDate::from_ymd_opt(2023, 4, 19).unwrap())]);
|
assert_eq!(game.info.date, vec![Date::Date(chrono::NaiveDate::from_ymd_opt(2023, 4, 19).unwrap())]);
|
||||||
// assert_eq!(game.info.komi, Some(6.5));
|
// assert_eq!(game.info.komi, Some(6.5));
|
||||||
}
|
}
|
||||||
);
|
);
|
|
@ -0,0 +1,17 @@
|
||||||
|
extern crate config_derive;
|
||||||
|
|
||||||
|
mod api;
|
||||||
|
pub use api::{
|
||||||
|
ChangeSettingRequest, Core, CoreNotification, CoreRequest, CoreResponse, CreateGameRequest,
|
||||||
|
HotseatPlayerRequest, Observable, PlayerInfoRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod board;
|
||||||
|
pub use board::*;
|
||||||
|
|
||||||
|
mod database;
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
pub use types::{BoardError, Color, Config, ConfigOption, DatabasePath, Player, Rank, Size};
|
||||||
|
|
||||||
|
pub mod ui;
|
|
@ -0,0 +1,240 @@
|
||||||
|
use crate::{
|
||||||
|
api::PlayStoneRequest,
|
||||||
|
board::{Coordinate, Goban},
|
||||||
|
database::Database,
|
||||||
|
};
|
||||||
|
use config::define_config;
|
||||||
|
use config_derive::ConfigOption;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{path::PathBuf, time::Duration};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
define_config! {
|
||||||
|
DatabasePath(DatabasePath),
|
||||||
|
Me(Me),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
|
||||||
|
pub struct DatabasePath(pub PathBuf);
|
||||||
|
|
||||||
|
impl std::ops::Deref for DatabasePath {
|
||||||
|
type Target = PathBuf;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for DatabasePath {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
Self(PathBuf::from(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ConfigOption)]
|
||||||
|
pub struct Me(Player);
|
||||||
|
|
||||||
|
impl std::ops::Deref for Me {
|
||||||
|
type Target = Player;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Error)]
|
||||||
|
pub enum BoardError {
|
||||||
|
#[error("Position is invalid")]
|
||||||
|
InvalidPosition,
|
||||||
|
#[error("Self-capture is forbidden")]
|
||||||
|
SelfCapture,
|
||||||
|
#[error("Ko")]
|
||||||
|
Ko,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Hash, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Color {
|
||||||
|
Black,
|
||||||
|
White,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Size {
|
||||||
|
pub width: u8,
|
||||||
|
pub height: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Size {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
width: 19,
|
||||||
|
height: 19,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub game: Option<GameState>,
|
||||||
|
pub database: Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
pub fn new(database_path: DatabasePath) -> Self {
|
||||||
|
Self {
|
||||||
|
game: Some(GameState::default()),
|
||||||
|
database: Database::open_path(database_path.to_path_buf()).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place_stone(&mut self, req: PlayStoneRequest) {
|
||||||
|
if let Some(ref mut game) = self.game {
|
||||||
|
let _ = game.place_stone(Coordinate {
|
||||||
|
column: req.column,
|
||||||
|
row: req.row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Rank {
|
||||||
|
Kyu(u8),
|
||||||
|
Dan(u8),
|
||||||
|
Pro(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for Rank {
|
||||||
|
type Error = String;
|
||||||
|
|
||||||
|
fn try_from(_: &str) -> Result<Rank, Self::Error> {
|
||||||
|
Ok(Rank::Kyu(15))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rank> for String {
|
||||||
|
fn from(r: Rank) -> String {
|
||||||
|
match r {
|
||||||
|
Rank::Kyu(v) => format!("{} kyu", v),
|
||||||
|
Rank::Dan(v) => format!("{} dan", v),
|
||||||
|
Rank::Pro(v) => format!("{} pro", v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Player {
|
||||||
|
pub name: String,
|
||||||
|
pub rank: Option<Rank>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GameState {
|
||||||
|
pub board: Goban,
|
||||||
|
pub past_positions: Vec<Goban>,
|
||||||
|
|
||||||
|
pub conversation: Vec<String>,
|
||||||
|
pub current_player: Color,
|
||||||
|
|
||||||
|
pub white_player: Player,
|
||||||
|
pub black_player: Player,
|
||||||
|
|
||||||
|
pub white_clock: Duration,
|
||||||
|
pub black_clock: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
board: Goban::new(),
|
||||||
|
past_positions: vec![],
|
||||||
|
conversation: vec![],
|
||||||
|
current_player: Color::Black,
|
||||||
|
white_player: Player {
|
||||||
|
name: "".to_owned(),
|
||||||
|
rank: None,
|
||||||
|
},
|
||||||
|
black_player: Player {
|
||||||
|
name: "".to_owned(),
|
||||||
|
rank: None,
|
||||||
|
},
|
||||||
|
white_clock: Duration::from_secs(600),
|
||||||
|
black_clock: Duration::from_secs(600),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameState {
|
||||||
|
fn place_stone(&mut self, coordinate: Coordinate) -> Result<(), BoardError> {
|
||||||
|
let board = self.board.clone();
|
||||||
|
let new_board = board.place_stone(coordinate, self.current_player)?;
|
||||||
|
|
||||||
|
if self.past_positions.contains(&new_board) {
|
||||||
|
return Err(BoardError::Ko);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.past_positions.push(self.board.clone());
|
||||||
|
self.board = new_board;
|
||||||
|
match self.current_player {
|
||||||
|
Color::White => self.current_player = Color::Black,
|
||||||
|
Color::Black => self.current_player = Color::White,
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn current_player_changes_after_move() {
|
||||||
|
let mut state = GameState::default();
|
||||||
|
assert_eq!(state.current_player, Color::Black);
|
||||||
|
state.place_stone(Coordinate { column: 9, row: 9 }).unwrap();
|
||||||
|
assert_eq!(state.current_player, Color::White);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn current_player_remains_the_same_after_self_capture() {
|
||||||
|
let mut state = GameState::default();
|
||||||
|
state.board = Goban::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 17, row: 0 }, Color::White),
|
||||||
|
(Coordinate { column: 17, row: 1 }, Color::White),
|
||||||
|
(Coordinate { column: 18, row: 1 }, Color::White),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
state.current_player = Color::Black;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
state.place_stone(Coordinate { column: 18, row: 0 }),
|
||||||
|
Err(BoardError::SelfCapture)
|
||||||
|
);
|
||||||
|
assert_eq!(state.current_player, Color::Black);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ko_rules_are_enforced() {
|
||||||
|
let mut state = GameState::default();
|
||||||
|
state.board = Goban::from_coordinates(
|
||||||
|
vec![
|
||||||
|
(Coordinate { column: 7, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 8 }, Color::White),
|
||||||
|
(Coordinate { column: 8, row: 10 }, Color::White),
|
||||||
|
(Coordinate { column: 9, row: 9 }, Color::White),
|
||||||
|
(Coordinate { column: 10, row: 9 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 8 }, Color::Black),
|
||||||
|
(Coordinate { column: 9, row: 10 }, Color::Black),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
state.place_stone(Coordinate { column: 8, row: 9 }).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
state.place_stone(Coordinate { column: 9, row: 9 }),
|
||||||
|
Err(BoardError::Ko)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
use crate::{
|
||||||
|
types::{Config, DatabasePath},
|
||||||
|
ui::Field,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ConfigurationView {
|
||||||
|
pub library: Field<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn configuration(config: &Config) -> ConfigurationView {
|
||||||
|
let path: Option<DatabasePath> = config.get();
|
||||||
|
ConfigurationView {
|
||||||
|
library: Field {
|
||||||
|
id: "library-path-field".to_owned(),
|
||||||
|
label: "Library".to_owned(),
|
||||||
|
value: path.map(|path| path.to_string_lossy().into_owned()),
|
||||||
|
action: (),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "otg-gtk"
|
name = "kifu-gtk"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
@ -9,20 +9,23 @@ screenplay = []
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
adw = { version = "0.5", package = "libadwaita", features = [ "v1_4" ] }
|
adw = { version = "0.5", package = "libadwaita", features = [ "v1_2" ] }
|
||||||
async-channel = { version = "2" }
|
async-channel = { version = "2" }
|
||||||
async-std = { version = "1" }
|
async-std = { version = "1" }
|
||||||
cairo-rs = { version = "0.18" }
|
cairo-rs = { version = "0.18" }
|
||||||
|
fluent-ergonomics = { path = "../../fluent-ergonomics" }
|
||||||
|
fluent = { version = "0.16" }
|
||||||
gio = { version = "0.18" }
|
gio = { version = "0.18" }
|
||||||
glib = { version = "0.18" }
|
glib = { version = "0.18" }
|
||||||
gtk = { version = "0.7", package = "gtk4", features = [ "v4_10" ] }
|
gtk = { version = "0.7", package = "gtk4", features = [ "v4_8" ] }
|
||||||
image = { version = "0.24" }
|
image = { version = "0.24" }
|
||||||
otg-core = { path = "../core" }
|
kifu-core = { path = "../core" }
|
||||||
|
l10n = { path = "../../l10n" }
|
||||||
pango = { version = "*" }
|
pango = { version = "*" }
|
||||||
sgf = { path = "../../sgf" }
|
sgf = { path = "../../sgf" }
|
||||||
|
sys-locale = { version = "0.3" }
|
||||||
|
thiserror = { version = "1" }
|
||||||
tokio = { version = "1.26", features = [ "full" ] }
|
tokio = { version = "1.26", features = [ "full" ] }
|
||||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
glib-build-tools = "0.17"
|
glib-build-tools = "0.17"
|
||||||
|
|
|
@ -3,7 +3,7 @@ release:
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
export CONFIG=.
|
export CONFIG=.
|
||||||
cargo watch -x 'run --bin otg-gtk'
|
cargo watch -x 'run --bin kifu-gtk'
|
||||||
|
|
||||||
screenplay:
|
screenplay:
|
||||||
cargo watch -x 'run --bin screenplay --features="screenplay"'
|
cargo watch -x 'run --bin screenplay --features="screenplay"'
|
|
@ -0,0 +1,29 @@
|
||||||
|
use std::{path::PathBuf, env, process, fs::File, io::Write};
|
||||||
|
|
||||||
|
fn generate_message_types(dest: &PathBuf) {
|
||||||
|
println!("message types");
|
||||||
|
let output: Vec<u8> = process::Command::new("l10n-codegen-rust")
|
||||||
|
.arg("messages/en.yaml")
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout;
|
||||||
|
|
||||||
|
let mut file = File::create(dest).unwrap();
|
||||||
|
file.write(&output).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
glib_build_tools::compile_resources(
|
||||||
|
&["resources"],
|
||||||
|
"gresources.xml",
|
||||||
|
"com.luminescent-dreams.kifu-gtk.gresource",
|
||||||
|
);
|
||||||
|
|
||||||
|
// println!("OUT_DIR={}", env::var("OUT_DIR").unwrap());
|
||||||
|
let mut message_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||||
|
message_path.push("messages.rs");
|
||||||
|
|
||||||
|
generate_message_types(&message_path);
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=KIFU_GTK_MESSAGES={}", message_path.to_string_lossy());
|
||||||
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/com/luminescent-dreams/otg-gtk/">
|
<gresource prefix="/com/luminescent-dreams/kifu-gtk/">
|
||||||
<file>wood_texture.jpg</file>
|
<file>wood_texture.jpg</file>
|
||||||
<file>style.css</file>
|
<file>style.css</file>
|
||||||
<file>black_stone.png</file>
|
|
||||||
<file>white_stone.png</file>
|
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
|
@ -0,0 +1,8 @@
|
||||||
|
nothing-here = Nothing Here
|
||||||
|
hello = Hello, ${name}
|
||||||
|
games-in-database = {$count ->
|
||||||
|
[one] There is one game in the database
|
||||||
|
*[other] There are ${count} games in the database
|
||||||
|
}
|
||||||
|
|
||||||
|
welcome = Welcome to Kifu
|
|
@ -0,0 +1,7 @@
|
||||||
|
nothing-here = Nenio estas ĉi tie
|
||||||
|
hello = Saluton, ${name}
|
||||||
|
welcome = Bonvenon
|
||||||
|
games-in-database = {$count ->
|
||||||
|
[one] Estas unu ludon en la datumbazo.
|
||||||
|
*[other] Estas ${count} ludojn en la datumbazo.
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
nothing-here:
|
||||||
|
content: "Nothing Here"
|
||||||
|
|
||||||
|
welcome:
|
||||||
|
content: "Welcome to Kifu"
|
||||||
|
|
||||||
|
hello:
|
||||||
|
parameters:
|
||||||
|
name: string
|
||||||
|
content: "Hello, ${name}"
|
||||||
|
|
||||||
|
games-in-database:
|
||||||
|
parameters:
|
||||||
|
count: count
|
||||||
|
content: |
|
||||||
|
{$count ->
|
||||||
|
[one] There is one game in the database
|
||||||
|
*[other] There are ${count} games in the database
|
||||||
|
}
|
|
@ -5,8 +5,8 @@ let
|
||||||
in {
|
in {
|
||||||
nativeBuildInputs = gtkNativeInputs;
|
nativeBuildInputs = gtkNativeInputs;
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
install -Dt $out/share/applications resources/otg.desktop
|
install -Dt $out/share/applications resources/kifu.desktop
|
||||||
install -Dt $out/gsettings-schemas/${gsettingsDir}/glib-2.0/schemas resources/com.luminescent-dreams.otg.gschema.xml
|
install -Dt $out/gsettings-schemas/${gsettingsDir}/glib-2.0/schemas resources/com.luminescent-dreams.kifu.gschema.xml
|
||||||
glib-compile-schemas $out/gsettings-schemas/${gsettingsDir}/glib-2.0/schemas
|
glib-compile-schemas $out/gsettings-schemas/${gsettingsDir}/glib-2.0/schemas
|
||||||
'';
|
'';
|
||||||
# preFixup = ''
|
# preFixup = ''
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<schemalist>
|
<schemalist>
|
||||||
<schema id="com.luminescent-dreams.otg.dev" path="/com/luminescent-dreams/otg-gtk/dev/">
|
<schema id="com.luminescent-dreams.kifu-gtk.dev" path="/com/luminescent-dreams/kifu-gtk/dev/">
|
||||||
<key name="library-path" type="s">
|
<key name="database-path" type="s">
|
||||||
<default>""</default>
|
<default>""</default>
|
||||||
<summary>Path to the directory of games</summary>
|
<summary>Path to the directory of games</summary>
|
||||||
</key>
|
</key>
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<schemalist>
|
<schemalist>
|
||||||
<schema id="com.luminescent-dreams.otg-gtk" path="/com/luminescent-dreams/otg-gtk/">
|
<schema id="com.luminescent-dreams.kifu-gtk" path="/com/luminescent-dreams/kifu-gtk/">
|
||||||
<key name="library-path" type="s">
|
<key name="database-path" type="s">
|
||||||
<default>""</default>
|
<default>""</default>
|
||||||
<summary>Path to the directory of games</summary>
|
<summary>Path to the directory of games</summary>
|
||||||
</key>
|
</key>
|
|
@ -1,5 +1,5 @@
|
||||||
[Desktop Entry]
|
[Desktop Entry]
|
||||||
Version=0.2
|
Version=0.2
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=On the Grid
|
Name=Kifu
|
||||||
Exec=otg-gtk
|
Exec=kifu
|
|
@ -0,0 +1,3 @@
|
||||||
|
.content {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 350 KiB |
|
@ -0,0 +1,129 @@
|
||||||
|
use async_std::task::yield_now;
|
||||||
|
use kifu_core::{Core, CoreRequest, Observable};
|
||||||
|
use l10n::{NonEmptyList, L10N};
|
||||||
|
use std::{rc::Rc, sync::Arc};
|
||||||
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
mod messages {
|
||||||
|
include!(env!("KIFU_GTK_MESSAGES"));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod ui;
|
||||||
|
|
||||||
|
mod view_models;
|
||||||
|
mod views;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CoreApi {
|
||||||
|
pub rt: Arc<Runtime>,
|
||||||
|
pub core: Core,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CoreApi {
|
||||||
|
pub fn dispatch(&self, request: CoreRequest) {
|
||||||
|
/*
|
||||||
|
spawn({
|
||||||
|
/*
|
||||||
|
let gtk_tx = self.gtk_tx.clone();
|
||||||
|
let core = self.core.clone();
|
||||||
|
async move { gtk_tx.send(core.dispatch(request).await) }
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn perftrace<F, A>(trace_name: &str, f: F) -> A
|
||||||
|
where
|
||||||
|
F: FnOnce() -> A,
|
||||||
|
{
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
let result = f();
|
||||||
|
let end = std::time::Instant::now();
|
||||||
|
println!("[Trace: {}] {:?}", trace_name, end - start);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// AppContext is our global store for things that need to be available everywhere. Developers
|
||||||
|
/// should never pass this around directly, but should instead pass it around by traits. This is to
|
||||||
|
/// provide systems such as:
|
||||||
|
///
|
||||||
|
/// - Feature Flags
|
||||||
|
/// - L10N
|
||||||
|
pub struct AppContext {
|
||||||
|
pub l10n: L10N,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AppContextInitError {}
|
||||||
|
|
||||||
|
impl AppContext {
|
||||||
|
pub fn new() -> Result<Self, AppContextInitError> {
|
||||||
|
let mut locale_list = NonEmptyList::new("en-US");
|
||||||
|
|
||||||
|
let user_locale = sys_locale::get_locale().unwrap();
|
||||||
|
println!("user locale: {}", user_locale);
|
||||||
|
if locale_list.find(|l| *l == user_locale).is_none() {
|
||||||
|
locale_list.push(&user_locale);
|
||||||
|
}
|
||||||
|
let mut l10n = l10n::L10N::new("messages".into());
|
||||||
|
l10n.set_locales(locale_list);
|
||||||
|
|
||||||
|
Ok(Self { l10n })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ProvidesL10N {
|
||||||
|
fn l10n(&self) -> &L10N;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProvidesL10N for AppContext {
|
||||||
|
fn l10n(&self) -> &L10N {
|
||||||
|
&self.l10n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// LocalObserver creates a task on the current thread which watches the specified observer for notifications and calls the handler function with each one.
|
||||||
|
///
|
||||||
|
/// The LocalObserver starts a task which listens for notifications during the constructor. When the observer goes out of scope, it will make a point of aborting the task. This combination means that anything which uses the observer can create it, hold on to a reference of it, and then drop it when done, and not have to do anything else with the observer object.
|
||||||
|
struct LocalObserver<T> {
|
||||||
|
join_handle: glib::JoinHandle<()>,
|
||||||
|
handler: Rc<dyn Fn(T)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static> LocalObserver<T> {
|
||||||
|
/// Construct a new LocalObserver and start it running.
|
||||||
|
///
|
||||||
|
/// observable -- any object which emits events
|
||||||
|
/// handler -- a function which can process events
|
||||||
|
fn new(observable: &dyn Observable<T>, handler: impl Fn(T) + 'static) -> Self {
|
||||||
|
let listener = observable.subscribe();
|
||||||
|
let handler = Rc::new(handler);
|
||||||
|
let join_handle = glib::spawn_future_local({
|
||||||
|
let handler = handler.clone();
|
||||||
|
async move {
|
||||||
|
loop {
|
||||||
|
match listener.recv().await {
|
||||||
|
Ok(msg) => handler(msg),
|
||||||
|
Err(_) => {
|
||||||
|
// recv only fails if the channel has been closed and no other notifications are pending. This will break out of the loop and terminate the observer.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield_now().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self {
|
||||||
|
join_handle,
|
||||||
|
handler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for LocalObserver<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Abort the task when the observer goes out of scope.
|
||||||
|
self.join_handle.abort();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,148 @@
|
||||||
|
use adw::prelude::*;
|
||||||
|
use kifu_core::{Config, ConfigOption, Core, CoreRequest, CoreResponse, DatabasePath};
|
||||||
|
use kifu_gtk::{
|
||||||
|
perftrace,
|
||||||
|
ui::{AppWindow, ConfigurationPage, Home, PlayingField},
|
||||||
|
AppContext, CoreApi,
|
||||||
|
};
|
||||||
|
use l10n::NonEmptyList;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
const APP_ID_DEV: &str = "com.luminescent-dreams.kifu-gtk.dev";
|
||||||
|
const APP_ID_PROD: &str = "com.luminescent-dreams.kifu-gtk";
|
||||||
|
|
||||||
|
const RESOURCE_BASE_PATH: &str = "/com/luminescent-dreams/kifu-gtk/";
|
||||||
|
|
||||||
|
fn handle_response(api: CoreApi, app_window: &AppWindow, message: CoreResponse) {
|
||||||
|
let playing_field = Arc::new(RwLock::new(None));
|
||||||
|
match message {
|
||||||
|
CoreResponse::ConfigurationView(view) => perftrace("ConfigurationView", || {
|
||||||
|
let config_page = ConfigurationPage::new(api, view);
|
||||||
|
|
||||||
|
let window = adw::PreferencesWindow::new();
|
||||||
|
window.add(&config_page);
|
||||||
|
window.set_visible_page(&config_page);
|
||||||
|
window.present();
|
||||||
|
}),
|
||||||
|
CoreResponse::HomeView(view) => perftrace("HomeView", || {
|
||||||
|
let api = api.clone();
|
||||||
|
|
||||||
|
let home = Home::new(api, view);
|
||||||
|
app_window.set_content(&home);
|
||||||
|
}),
|
||||||
|
CoreResponse::PlayingFieldView(view) => perftrace("PlayingFieldView", || {
|
||||||
|
let api = api.clone();
|
||||||
|
|
||||||
|
let mut playing_field = playing_field.write().unwrap();
|
||||||
|
if playing_field.is_none() {
|
||||||
|
perftrace("creating a new playing field", || {
|
||||||
|
let field = PlayingField::new(api, view);
|
||||||
|
app_window.set_content(&field);
|
||||||
|
*playing_field = Some(field);
|
||||||
|
})
|
||||||
|
} else if let Some(field) = playing_field.as_ref() {
|
||||||
|
field.update_view(view)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
CoreResponse::UpdatedConfigurationView(view) => perftrace("UpdatedConfiguration", || {
|
||||||
|
println!("updated configuration: {:?}", view);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
gio::resources_register_include!("com.luminescent-dreams.kifu-gtk.gresource")
|
||||||
|
.expect("Failed to register resources");
|
||||||
|
|
||||||
|
let app_id = if std::env::var_os("ENV") == Some("dev".into()) {
|
||||||
|
APP_ID_DEV
|
||||||
|
} else {
|
||||||
|
APP_ID_PROD
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = gio::Settings::new(app_id);
|
||||||
|
let db_path: String = settings.string("database-path").into();
|
||||||
|
let mut config = Config::new();
|
||||||
|
config.set(ConfigOption::DatabasePath(db_path.into()));
|
||||||
|
|
||||||
|
let runtime = Arc::new(
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
/*
|
||||||
|
let config_path = std::env::var("CONFIG")
|
||||||
|
.map(std::path::PathBuf::from)
|
||||||
|
.or({
|
||||||
|
std::env::var("HOME").map(|base| {
|
||||||
|
let mut config_path = std::path::PathBuf::from(base);
|
||||||
|
config_path.push(".config");
|
||||||
|
config_path.push("kifu");
|
||||||
|
config_path
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.expect("no config path could be found");
|
||||||
|
*/
|
||||||
|
|
||||||
|
let core = Core::new(config);
|
||||||
|
|
||||||
|
/*
|
||||||
|
let core_handle = runtime.spawn({
|
||||||
|
let core = core.clone();
|
||||||
|
async move {
|
||||||
|
core.run().await;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
let app = adw::Application::builder()
|
||||||
|
.application_id("com.luminescent-dreams.kifu-gtk")
|
||||||
|
.resource_base_path("/com/luminescent-dreams/kifu-gtk")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
app.connect_activate({
|
||||||
|
let runtime = runtime.clone();
|
||||||
|
move |app| {
|
||||||
|
|
||||||
|
let ctx = AppContext::new().unwrap();
|
||||||
|
let app_window = AppWindow::new(app, &ctx);
|
||||||
|
|
||||||
|
let api = CoreApi {
|
||||||
|
rt: runtime.clone(),
|
||||||
|
core: core.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let action_config = gio::SimpleAction::new("show-config", None);
|
||||||
|
action_config.connect_activate({
|
||||||
|
let api = api.clone();
|
||||||
|
move |_, _| {
|
||||||
|
api.dispatch(CoreRequest::OpenConfiguration);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.add_action(&action_config);
|
||||||
|
|
||||||
|
app_window.window.present();
|
||||||
|
|
||||||
|
/*
|
||||||
|
gtk_rx.attach(None, {
|
||||||
|
let api = api.clone();
|
||||||
|
move |message| {
|
||||||
|
perftrace("handle_response", || {
|
||||||
|
handle_response(api.clone(), &app_window, message)
|
||||||
|
});
|
||||||
|
glib::ControlFlow::Continue
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
api.dispatch(CoreRequest::Home);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
println!("running the gtk loop");
|
||||||
|
app.run();
|
||||||
|
|
||||||
|
/* let _ = runtime.block_on(core_handle); */
|
||||||
|
}
|
|
@ -7,7 +7,8 @@ use gtk::{
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
};
|
};
|
||||||
use image::io::Reader as ImageReader;
|
use image::io::Reader as ImageReader;
|
||||||
use otg_core::{
|
use kifu_core::{
|
||||||
|
ui::{BoardElement, IntersectionElement},
|
||||||
Color,
|
Color,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
use std::{cell::RefCell, io::Cursor, rc::Rc};
|
||||||
|
@ -26,7 +27,7 @@ pub struct BoardPrivate {
|
||||||
drawing_area: gtk::DrawingArea,
|
drawing_area: gtk::DrawingArea,
|
||||||
|
|
||||||
current_player: Rc<RefCell<Color>>,
|
current_player: Rc<RefCell<Color>>,
|
||||||
// board: Rc<RefCell<BoardElement>>,
|
board: Rc<RefCell<BoardElement>>,
|
||||||
cursor_location: Rc<RefCell<Option<Addr>>>,
|
cursor_location: Rc<RefCell<Option<Addr>>>,
|
||||||
|
|
||||||
api: Rc<RefCell<Option<CoreApi>>>,
|
api: Rc<RefCell<Option<CoreApi>>>,
|
||||||
|
@ -42,7 +43,7 @@ impl ObjectSubclass for BoardPrivate {
|
||||||
BoardPrivate {
|
BoardPrivate {
|
||||||
drawing_area: Default::default(),
|
drawing_area: Default::default(),
|
||||||
current_player: Rc::new(RefCell::new(Color::Black)),
|
current_player: Rc::new(RefCell::new(Color::Black)),
|
||||||
// board: Default::default(),
|
board: Default::default(),
|
||||||
cursor_location: Default::default(),
|
cursor_location: Default::default(),
|
||||||
api: Default::default(),
|
api: Default::default(),
|
||||||
}
|
}
|
||||||
|
@ -54,12 +55,12 @@ impl ObjectImpl for BoardPrivate {
|
||||||
self.drawing_area.set_width_request(WIDTH);
|
self.drawing_area.set_width_request(WIDTH);
|
||||||
self.drawing_area.set_height_request(HEIGHT);
|
self.drawing_area.set_height_request(HEIGHT);
|
||||||
|
|
||||||
// let board = self.board.clone();
|
let board = self.board.clone();
|
||||||
let cursor_location = self.cursor_location.clone();
|
let cursor_location = self.cursor_location.clone();
|
||||||
let current_player = self.current_player.clone();
|
let current_player = self.current_player.clone();
|
||||||
|
|
||||||
let wood_texture = resources_lookup_data(
|
let wood_texture = resources_lookup_data(
|
||||||
"/com/luminescent-dreams/otg-gtk/wood_texture.jpg",
|
"/com/luminescent-dreams/kifu-gtk/wood_texture.jpg",
|
||||||
gio::ResourceLookupFlags::NONE,
|
gio::ResourceLookupFlags::NONE,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -84,7 +85,6 @@ impl ObjectImpl for BoardPrivate {
|
||||||
.set_draw_func(move |_, context, width, height| {
|
.set_draw_func(move |_, context, width, height| {
|
||||||
perftrace("render drawing area", || {
|
perftrace("render drawing area", || {
|
||||||
let render_start = std::time::Instant::now();
|
let render_start = std::time::Instant::now();
|
||||||
/*
|
|
||||||
let board = board.borrow();
|
let board = board.borrow();
|
||||||
|
|
||||||
match background {
|
match background {
|
||||||
|
@ -137,9 +137,7 @@ impl ObjectImpl for BoardPrivate {
|
||||||
pen.star_point(context, col, row);
|
pen.star_point(context, col, row);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
(0..19).for_each(|col| {
|
(0..19).for_each(|col| {
|
||||||
(0..19).for_each(|row| {
|
(0..19).for_each(|row| {
|
||||||
if let IntersectionElement::Filled(stone) = board.stone(row, col) {
|
if let IntersectionElement::Filled(stone) = board.stone(row, col) {
|
||||||
|
@ -164,7 +162,6 @@ impl ObjectImpl for BoardPrivate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
let render_end = std::time::Instant::now();
|
let render_end = std::time::Instant::now();
|
||||||
println!("board rendering time: {:?}", render_end - render_start);
|
println!("board rendering time: {:?}", render_end - render_start);
|
||||||
})
|
})
|
||||||
|
@ -172,11 +169,10 @@ impl ObjectImpl for BoardPrivate {
|
||||||
|
|
||||||
let motion_controller = gtk::EventControllerMotion::new();
|
let motion_controller = gtk::EventControllerMotion::new();
|
||||||
{
|
{
|
||||||
// let board = self.board.clone();
|
let board = self.board.clone();
|
||||||
let cursor = self.cursor_location.clone();
|
let cursor = self.cursor_location.clone();
|
||||||
let drawing_area = self.drawing_area.clone();
|
let drawing_area = self.drawing_area.clone();
|
||||||
motion_controller.connect_motion(move |_, x, y| {
|
motion_controller.connect_motion(move |_, x, y| {
|
||||||
/*
|
|
||||||
let board = board.borrow();
|
let board = board.borrow();
|
||||||
let mut cursor = cursor.borrow_mut();
|
let mut cursor = cursor.borrow_mut();
|
||||||
let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
|
let hspace_between = ((WIDTH - 40) as f64) / ((board.size.width - 1) as f64);
|
||||||
|
@ -199,21 +195,17 @@ impl ObjectImpl for BoardPrivate {
|
||||||
*cursor = addr;
|
*cursor = addr;
|
||||||
drawing_area.queue_draw();
|
drawing_area.queue_draw();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let gesture = gtk::GestureClick::new();
|
let gesture = gtk::GestureClick::new();
|
||||||
{
|
{
|
||||||
// let board = self.board.clone();
|
let board = self.board.clone();
|
||||||
let cursor = self.cursor_location.clone();
|
let cursor = self.cursor_location.clone();
|
||||||
let api = self.api.clone();
|
let api = self.api.clone();
|
||||||
gesture.connect_released(move |_, _, _, _| {
|
gesture.connect_released(move |_, _, _, _| {
|
||||||
/*
|
|
||||||
let board = board.borrow();
|
let board = board.borrow();
|
||||||
let cursor = cursor.borrow();
|
let cursor = cursor.borrow();
|
||||||
*/
|
|
||||||
/*
|
|
||||||
match *cursor {
|
match *cursor {
|
||||||
None => {}
|
None => {}
|
||||||
Some(ref cursor) => {
|
Some(ref cursor) => {
|
||||||
|
@ -228,7 +220,6 @@ impl ObjectImpl for BoardPrivate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,12 +244,10 @@ impl Board {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn set_board(&self, board: BoardElement) {
|
pub fn set_board(&self, board: BoardElement) {
|
||||||
*self.imp().board.borrow_mut() = board;
|
*self.imp().board.borrow_mut() = board;
|
||||||
self.imp().drawing_area.queue_draw();
|
self.imp().drawing_area.queue_draw();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn set_current_player(&self, color: Color) {
|
pub fn set_current_player(&self, color: Color) {
|
||||||
*self.imp().current_player.borrow_mut() = color;
|
*self.imp().current_player.borrow_mut() = color;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue