From 18cdc547819d03415d09e6f385832e06643bdf83 Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Tue, 12 May 2026 21:22:36 +0200 Subject: [PATCH] first sim pass --- .cargo/config.toml | 15 -- Cargo.lock | 370 ++++++++++++++++++++++++++++++++++++++- Cargo.toml | 3 + esp32/.cargo/config.toml | 17 ++ esp32/Cargo.toml | 2 + esp32/src/main.rs | 14 +- simulator/Cargo.toml | 12 ++ simulator/src/main.rs | 192 ++++++++++++++++++++ 8 files changed, 594 insertions(+), 31 deletions(-) create mode 100644 esp32/.cargo/config.toml create mode 100644 simulator/Cargo.toml create mode 100644 simulator/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index 03bf83c..d3cef6f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,17 +1,2 @@ -[build] -target = "xtensa-esp32-none-elf" - -[target.xtensa-esp32-none-elf] -runner = "espflash flash --monitor" -rustflags = [ - "-C", - "link-arg=-Wl,-Tlinkall.x", - "-C", - "link-arg=-nostartfiles", -] - -[unstable] -build-std = ["core", "alloc", "compiler_builtins"] - [env] DEFMT_LOG = "debug" diff --git a/Cargo.lock b/Cargo.lock index 2a8116d..27467c3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,56 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "arrayvec" version = "0.7.6" @@ -215,6 +265,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + [[package]] name = "cc" version = "1.2.62" @@ -242,6 +298,46 @@ dependencies = [ "pure-rust-locales", ] +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -252,6 +348,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "const-default" version = "1.0.0" @@ -324,6 +426,31 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.11.1", + "crossterm_winapi", + "mio", + "parking_lot", + "rustix", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.4" @@ -1047,6 +1174,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "esp-alloc" version = "0.10.0" @@ -1680,6 +1817,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.14.0" @@ -1778,6 +1921,12 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + [[package]] name = "litrs" version = "1.0.0" @@ -1833,6 +1982,18 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.61.2", +] + [[package]] name = "nb" version = "0.1.3" @@ -1860,7 +2021,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -1910,6 +2071,12 @@ version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "owned_str" version = "0.1.2" @@ -2337,6 +2504,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2510,6 +2690,47 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simulator" +version = "0.1.0" +dependencies = [ + "clap", + "crossterm", + "device-state", + "tokio", +] + [[package]] name = "siphasher" version = "1.0.3" @@ -2554,6 +2775,16 @@ dependencies = [ "managed", ] +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "somni-expr" version = "0.2.0" @@ -2674,7 +2905,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] [[package]] @@ -2715,6 +2946,32 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "toml_datetime" version = "1.1.1+spec-1.1.0" @@ -2933,6 +3190,12 @@ dependencies = [ "usbd-hid-descriptors", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.1" @@ -2976,15 +3239,43 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[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.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys", + "windows-sys 0.61.2", ] +[[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-link" version = "0.2.1" @@ -3000,6 +3291,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.61.2" @@ -3009,6 +3309,70 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "1.0.2" diff --git a/Cargo.toml b/Cargo.toml index b44aa51..4a3af7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,11 @@ +cargo-features = ["per-package-target"] + [workspace] members = [ "device-state", "esp32", "pico", + "simulator", ] resolver = "2" diff --git a/esp32/.cargo/config.toml b/esp32/.cargo/config.toml new file mode 100644 index 0000000..03bf83c --- /dev/null +++ b/esp32/.cargo/config.toml @@ -0,0 +1,17 @@ +[build] +target = "xtensa-esp32-none-elf" + +[target.xtensa-esp32-none-elf] +runner = "espflash flash --monitor" +rustflags = [ + "-C", + "link-arg=-Wl,-Tlinkall.x", + "-C", + "link-arg=-nostartfiles", +] + +[unstable] +build-std = ["core", "alloc", "compiler_builtins"] + +[env] +DEFMT_LOG = "debug" diff --git a/esp32/Cargo.toml b/esp32/Cargo.toml index 1906585..e5ec3d3 100644 --- a/esp32/Cargo.toml +++ b/esp32/Cargo.toml @@ -1,7 +1,9 @@ +cargo-features = ["per-package-target"] [package] name = "esp32" version = "0.1.0" edition = "2024" +default-target = "xtensa-esp32-none-elf" [dependencies] [dependencies.device-state] diff --git a/esp32/src/main.rs b/esp32/src/main.rs index 5528b71..a84cbc3 100644 --- a/esp32/src/main.rs +++ b/esp32/src/main.rs @@ -3,6 +3,7 @@ extern crate alloc; +use device_state::DeviceState; use embassy_executor::Spawner; use embassy_futures::select::{Either, select}; use embassy_net::tcp::{TcpReader, TcpWriter}; @@ -17,7 +18,6 @@ use esp_hal::{ timer::timg::TimerGroup, }; use esp_println::println; -use device_state::{DeviceState, ViewState}; mod buffer; mod input; @@ -26,8 +26,6 @@ mod screen; pub use input::ANGLE; -use crate::screen::overwrite_lcd; - const WIFI_NETWORK: &str = "flamme"; const WIFI_PASSWORD: &str = "12345678"; const TARGET_IP: &str = "84.238.32.253"; @@ -135,18 +133,8 @@ pub async fn main_loop() { let mut state = STATE.lock().await; state.tick(); let lines = state.render_lines(); - let view = state.view_state(); drop(state); - match view { - ViewState::Loading => continue, - ViewState::Results => { - overwrite_lcd("Results", "").await; - continue; - } - ViewState::Question => {} - } - let Some((title_line, second_line)) = lines else { continue; }; diff --git a/simulator/Cargo.toml b/simulator/Cargo.toml new file mode 100644 index 0000000..24d99a5 --- /dev/null +++ b/simulator/Cargo.toml @@ -0,0 +1,12 @@ +cargo-features = ["per-package-target"] +[package] +name = "simulator" +version = "0.1.0" +edition = "2024" +default-target = "x86_64-unknown-linux-gnu" + +[dependencies] +clap = { version = "4.5.45", features = ["derive"] } +crossterm = "0.28.1" +device-state = { path = "../device-state" } +tokio = { version = "1.47.0", features = ["macros", "rt-multi-thread", "net", "sync", "time", "io-util"] } diff --git a/simulator/src/main.rs b/simulator/src/main.rs new file mode 100644 index 0000000..bb012dc --- /dev/null +++ b/simulator/src/main.rs @@ -0,0 +1,192 @@ +use std::io::{self, Write as _}; +use std::net::SocketAddr; +use std::sync::{Arc, Mutex}; +use std::thread; + +use clap::Parser; +use crossterm::event::{Event, KeyCode, KeyEventKind}; +use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; +use crossterm::event; +use device_state::{DeviceState, WriteType, apply_wheel_delta}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; +use tokio::sync::mpsc; +use tokio::time::{Duration, interval}; + +const DEVICE_ID: &str = "esp32-1"; +const DEFAULT_HOST: &str = "84.238.32.253"; +const DEFAULT_PORT: u16 = 7070; +const WHEEL_PRECISION: i32 = 32; +const WHEEL_STEP: i32 = 1; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Args { + #[arg(long, default_value = DEFAULT_HOST)] + host: String, + #[arg(long, default_value_t = DEFAULT_PORT)] + port: u16, +} + +enum InputEvent { + Button(u8), + WheelDelta(i32), + Quit, +} + +#[tokio::main] +async fn main() -> io::Result<()> { + let args = Args::parse(); + let addr: SocketAddr = format!("{}:{}", args.host, args.port).parse().unwrap(); + + let state = Arc::new(Mutex::new(DeviceState::new())); + let (input_tx, mut input_rx) = mpsc::channel::(64); + + spawn_input_thread(input_tx.clone()); + tokio::spawn(ui_task(state.clone())); + + let mut buf = [0u8; 1024]; + + loop { + let stream = match TcpStream::connect(addr).await { + Ok(stream) => stream, + Err(err) => { + eprintln!("connect error: {err}"); + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } + }; + let (mut read, mut write) = stream.into_split(); + + let device_id = device_state::serialize_write(&WriteType::DeviceId(DEVICE_ID)).unwrap(); + if write.write_all(device_id.as_bytes()).await.is_err() { + tokio::time::sleep(Duration::from_secs(1)).await; + continue; + } + + loop { + tokio::select! { + maybe_event = input_rx.recv() => { + match maybe_event { + Some(InputEvent::Quit) | None => { + let _ = disable_raw_mode(); + return Ok(()); + } + Some(InputEvent::Button(id)) => { + let value = state.lock().unwrap().response_value(id); + let data = WriteType::QuizResponse(value); + let buffer = device_state::serialize_write(&data).unwrap(); + if write.write_all(buffer.as_bytes()).await.is_err() { + break; + } + } + Some(InputEvent::WheelDelta(diff)) => { + let mut state = state.lock().unwrap(); + let wheel = state.wheel(); + if wheel.max != wheel.min { + let mut value = wheel.value; + let mut accumulated = wheel.accumulated; + apply_wheel_delta( + &mut value, + wheel.min, + wheel.max, + &mut accumulated, + diff, + WHEEL_PRECISION, + ); + let wheel = state.wheel_mut(); + wheel.value = value; + wheel.accumulated = accumulated; + } + } + } + } + read_res = read.read(&mut buf) => { + let len = match read_res { + Ok(0) => break, + Ok(len) => len, + Err(_) => break, + }; + let Ok(text) = core::str::from_utf8(&buf[..len]) else { + continue; + }; + if let Some(last) = text.lines().last() { + let Ok(data) = device_state::parse_proxy_output(last) else { + continue; + }; + let mut state = state.lock().unwrap(); + state.apply_proxy_output(data); + } + } + } + } + + tokio::time::sleep(Duration::from_millis(500)).await; + } +} + +fn spawn_input_thread(tx: mpsc::Sender) { + let _ = thread::Builder::new() + .name("sim-input".to_string()) + .spawn(move || input_thread(tx)); +} + +fn input_thread(tx: mpsc::Sender) { + let _ = enable_raw_mode(); + let mut stdout = io::stdout(); + let _ = crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0)); + + loop { + if let Ok(Event::Key(key)) = event::read() { + if key.kind == KeyEventKind::Release { + continue; + } + let event = match key.code { + KeyCode::Char('1') => Some(InputEvent::Button(1)), + KeyCode::Char('2') => Some(InputEvent::Button(2)), + KeyCode::Char('3') => Some(InputEvent::Button(3)), + KeyCode::Char('4') => Some(InputEvent::Button(4)), + KeyCode::Left => Some(InputEvent::WheelDelta(-WHEEL_STEP)), + KeyCode::Right => Some(InputEvent::WheelDelta(WHEEL_STEP)), + KeyCode::Char('a') => Some(InputEvent::WheelDelta(-WHEEL_STEP)), + KeyCode::Char('d') => Some(InputEvent::WheelDelta(WHEEL_STEP)), + KeyCode::Char('q') => Some(InputEvent::Quit), + _ => None, + }; + + if let Some(event) = event { + let quit = matches!(event, InputEvent::Quit); + if tx.blocking_send(event).is_err() { + break; + } + if quit { + break; + } + } + } + } + + let _ = disable_raw_mode(); +} + +async fn ui_task(state: Arc>) { + let mut ticker = interval(Duration::from_millis(50)); + loop { + ticker.tick().await; + let (line1, line2) = { + let mut state = state.lock().unwrap(); + state.tick(); + match state.render_lines() { + Some((line1, line2)) => (line1, line2), + None => continue, + } + }; + + let mut stdout = io::stdout(); + let _ = crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0)); + let _ = writeln!(stdout, "{:<16}", line1.as_str()); + let _ = writeln!(stdout, "{:<16}", line2.as_str()); + let _ = writeln!(stdout, "[1-4]=buttons <-/->=wheel a/d q=quit"); + let _ = stdout.flush(); + } +}