From 54a0f7f53386edea06cfc1da15581f653d4141b7 Mon Sep 17 00:00:00 2001 From: Dorian Zedler Date: Fri, 17 Feb 2023 14:14:00 +0100 Subject: [PATCH] Feat: fully working gui --- receiver/Cargo.lock | 248 ++++++++++++++++++++++++++++++++++++++- receiver/Cargo.toml | 2 + receiver/src/gui.rs | 181 ++++++++++++++++++++++++++++ receiver/src/keyboard.rs | 16 ++- receiver/src/main.rs | 95 ++------------- 5 files changed, 456 insertions(+), 86 deletions(-) create mode 100644 receiver/src/gui.rs diff --git a/receiver/Cargo.lock b/receiver/Cargo.lock index 2513b54..2df861d 100644 --- a/receiver/Cargo.lock +++ b/receiver/Cargo.lock @@ -247,6 +247,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + [[package]] name = "cipher" version = "0.4.3" @@ -364,6 +379,12 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58baae561b85ca19b3122a9ddd35c8ec40c3bcd14fe89921824eae73f7baffbf" +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "core-foundation" version = "0.6.4" @@ -568,6 +589,50 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "d3d12" version = "0.5.0" @@ -614,6 +679,19 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "digest" version = "0.9.0" @@ -769,6 +847,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float_next_after" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632" +dependencies = [ + "num-traits", +] + [[package]] name = "flume" version = "0.10.14" @@ -980,7 +1067,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1139,6 +1226,30 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys 0.8.3", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[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 = "iced" version = "0.7.0" @@ -1154,6 +1265,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "iced_aw" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a108808b3a5753ca85f460cffef776c33aa2e0dd557aab9261c7d2c60bbdb6d" +dependencies = [ + "chrono", + "iced_graphics", + "iced_native", + "iced_style", + "lazy_static", + "num-traits", +] + [[package]] name = "iced_core" version = "0.7.0" @@ -1189,6 +1314,7 @@ dependencies = [ "iced_native", "iced_style", "log", + "lyon", "raw-window-handle 0.5.0", "thiserror", ] @@ -1347,6 +1473,21 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1372,6 +1513,58 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lyon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7f9cda98b5430809e63ca5197b06c7d191bf7e26dfc467d5a3f0290e2a74f" +dependencies = [ + "lyon_algorithms", + "lyon_tessellation", +] + +[[package]] +name = "lyon_algorithms" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb7a1845c15729d73d25d42cb650b647f73c3494453a5c3cd3aae0df3ac5c6c" +dependencies = [ + "lyon_path", + "num-traits", +] + +[[package]] +name = "lyon_geom" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad" +dependencies = [ + "arrayvec 0.7.2", + "euclid", + "num-traits", +] + +[[package]] +name = "lyon_path" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8358c012e5651e4619cfd0b5b75c0f77866181a01b0909aab4bae14adf660" +dependencies = [ + "lyon_geom", + "num-traits", +] + +[[package]] +name = "lyon_tessellation" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d2124218d5428149f9e09520b9acc024334a607e671f032d06567b61008977c" +dependencies = [ + "float_next_after", + "lyon_path", + "thiserror", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1451,7 +1644,7 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.42.0", ] @@ -1599,6 +1792,16 @@ dependencies = [ "memchr", ] +[[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" @@ -1606,6 +1809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2057,9 +2261,11 @@ version = "0.1.0" dependencies = [ "base64", "crypto_helper", + "derive_more", "enigo", "hex", "iced", + "iced_aw", "rumqttc", "serde", "serde_json", @@ -2121,6 +2327,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustls" version = "0.20.8" @@ -2190,6 +2405,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "sct" version = "0.7.0" @@ -2235,6 +2456,12 @@ dependencies = [ "libc", ] +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + [[package]] name = "serde" version = "1.0.152" @@ -2495,6 +2722,17 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "tiny-skia" version = "0.7.0" @@ -2670,6 +2908,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/receiver/Cargo.toml b/receiver/Cargo.toml index 1edafd3..cb98c07 100644 --- a/receiver/Cargo.toml +++ b/receiver/Cargo.toml @@ -14,6 +14,8 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" iced = "0.7.0" rumqttc = { version = "0.20.0", default-features = false, features = ["use-rustls"] } +iced_aw = "0.3.0" +derive_more = "0.99.17" [target.'cfg(unix)'.dependencies] enigo = "0.0.14" diff --git a/receiver/src/gui.rs b/receiver/src/gui.rs new file mode 100644 index 0000000..35b2b4f --- /dev/null +++ b/receiver/src/gui.rs @@ -0,0 +1,181 @@ +use std::sync::{Mutex, Arc}; +use std::sync::mpsc::{self, TryRecvError}; +use std::thread::{self}; + +use iced::theme; +use iced::widget::{ + column, row, text, + text_input, pick_list +}; +use iced::widget::{Button}; +use iced::{alignment, Element, Length, Sandbox}; +use iced_aw::number_input::NumberInput; + +use crate::{keyboard::{self, ContinueButton}, comm}; + +pub struct Gui { + password: String, + c: Option ()>>, + continue_sequence: Arc> +} + +#[derive(Debug, Clone)] +pub enum Message { + ConnectPressed, + PasswordChanged(String), + DisconnectPressed, + ContinueButtonCountChanged(u32), + ContinueButtonChanged(ContinueButton) +} + +impl Sandbox for Gui { + type Message = Message; + + fn new() -> Self { + Gui { + password: "".to_owned(), + c: None, + continue_sequence: Arc::new(Mutex::new((2, ContinueButton::Tab))) + } + } + + fn update(&mut self, event: Message) { + match event { + Message::PasswordChanged(p) => self.password = p, + Message::ConnectPressed => { + let res = Some(connect(self, &self.password)); + self.c = res; + }, + Message::DisconnectPressed => { + (self.c.as_mut().unwrap())(); + self.c = None; + } + Message::ContinueButtonCountChanged(count) => { + self.continue_sequence.lock().unwrap().0 = count; + } + Message::ContinueButtonChanged(b) => { + self.continue_sequence.lock().unwrap().1 = b + } + } + } + + fn view(&self) -> Element { + let connected = self.c.is_some(); + + + + let content: Element<_> = column![ + text("OK! .. READY! ... GO!").size(30), + if connected { + connected_content(self) + } else { + not_connected_content(self) + } + ] + .width(Length::Fill) + .spacing(20) + .padding(20) + .into(); + + content + } + + fn title(&self) -> String { + "gui".to_owned() + } + + fn theme(&self) -> iced::Theme { + iced::Theme::Dark + } + +} + +fn not_connected_content(gui: &Gui) -> Element { + column![ + "Enter the same password as on your phone to connect.", + text_input("Enter password", &gui.password, Message::PasswordChanged) + .password() + .size(30), + button("Connect") + .width(Length::Fill) + .style(theme::Button::Primary) + .on_press(Message::ConnectPressed), + ].width(Length::Fill).spacing(20).padding(0).into() +} + +fn connected_content(gui: &Gui) -> Element { + let seq = gui.continue_sequence.lock().unwrap(); + column![ + "Connected.", + button("Disconnect") + .width(Length::Fill) + .style(theme::Button::Primary) + .on_press(Message::DisconnectPressed), + row![ + NumberInput::new(seq.0, 10, Message::ContinueButtonCountChanged).step(1).size(30), + pick_list(vec![ContinueButton::Tab, ContinueButton::Enter], Some(seq.1), Message::ContinueButtonChanged).padding(5) + ].spacing(20).width(Length::Fill) + ].width(Length::Fill).spacing(20).padding(0).into() +} + +// ---------- +// components +// ---------- + +fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { + iced::widget::button(text(label).horizontal_alignment(alignment::Horizontal::Center)) + .padding(12) +} + +// ------- +// helpers +// ------- + +fn connect(gui: &Gui, password: &str) -> Box ()> { + let password = password.to_owned(); + let mux = gui.continue_sequence.clone(); + let (tx, rx) = mpsc::channel(); + + let handle = thread::spawn(move || { + let c = comm::Comm::new("broker.emqx.io", &password, handle_time).unwrap(); + loop { + match rx.try_recv() { + Ok(()) | Err(TryRecvError::Disconnected) => { + println!("Comm is terminating."); + break; + }, + Err(TryRecvError::Empty) => {} + } + c.handle_next_event(&mux); + } + c.disconnect(); + }); + + Box::new(move || { + tx.send(()).unwrap(); + while !handle.is_finished() {}; + }) +} + + +fn millis_to_string(millis: u32) -> String { + let seconds = (millis as f64) / 1000.0; + let formatted = format!("{:.2}", seconds); + formatted.replace(".", ",") +} + +fn handle_time(time: u32, mux: &Arc>) -> Result<(), ()> { + let (continue_button_count, continue_button) = *mux.lock().unwrap(); + println!("Got time: {}, count: {}, button: {}", time, continue_button_count, continue_button); + let time_with_comma = millis_to_string(time); + + println!("-> Trying to type {time_with_comma}"); + keyboard::type_text(&time_with_comma)?; + + for _ in 0..continue_button_count { + println!("-> Trying to click {continue_button}"); + keyboard::click_button(continue_button)?; + } + + Ok(()) +} \ No newline at end of file diff --git a/receiver/src/keyboard.rs b/receiver/src/keyboard.rs index 72fcf8b..0c1af10 100644 --- a/receiver/src/keyboard.rs +++ b/receiver/src/keyboard.rs @@ -1,3 +1,11 @@ +use derive_more::Display; + +#[derive(Debug, Clone, Display, PartialEq, Eq, Copy)] +pub enum ContinueButton { + Tab, + Enter +} + #[cfg(target_family = "unix")] use enigo::{KeyboardControllable}; @@ -8,8 +16,12 @@ pub fn type_text(text: &str) -> Result<(),()> { } #[cfg(target_family = "unix")] -pub fn click_tab() -> Result<(),()> { - enigo::Enigo::default().key_click(enigo::Key::Tab); +pub fn click_button(button: ContinueButton) -> Result<(),()> { + let button = match button { + ContinueButton::Tab => enigo::Key::Tab, + ContinueButton::Enter => enigo::Key::Return, + }; + enigo::Enigo::default().key_click(button); Ok(()) } diff --git a/receiver/src/main.rs b/receiver/src/main.rs index bd5400d..9fbb110 100644 --- a/receiver/src/main.rs +++ b/receiver/src/main.rs @@ -1,90 +1,21 @@ -use iced::theme; -use iced::widget::{ - checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, - text_input, toggler, vertical_space, -}; -use iced::widget::{Button, Column, Container, Slider}; -use iced::{alignment, Element, Length, Sandbox, Settings}; +use iced::Sandbox; mod comm; mod keyboard; +mod gui; -struct Gui { - password: String, -} - -#[derive(Debug, Clone)] -enum Message { - Pressed, - PasswordChanged(String), -} - -impl Sandbox for Gui { - type Message = Message; - - fn new() -> Self { - Gui {password: "".to_owned()} - } - - fn update(&mut self, event: Message) { - match event { - Message::PasswordChanged(p) => self.password = p, - Message::Pressed => println!("Pressed") - } - } - - fn view(&self) -> Element { - let content: Element<_> = column![ - text_input("Enter password", &self.password, Message::PasswordChanged) - .password() - .size(30), - button("Connect") - .style(theme::Button::Primary) - .on_press(Message::Pressed), - ] - .max_width(540) - .spacing(20) - .padding(20) - .into(); - - content - } - - fn title(&self) -> String { - "gui".to_owned() - } -} - -fn button<'a, Message: Clone>(label: &str) -> Button<'a, Message> { - iced::widget::button(text(label).horizontal_alignment(alignment::Horizontal::Center)) - .padding(12) -} - -fn millis_to_string(millis: u32) -> String { - let seconds = (millis as f64) / 1000.0; - let formatted = format!("{:.2}", seconds); - formatted.replace(".", ",") -} - -fn handle_time(time: u32) -> Result<(),()> { - println!("Got time: {}", time); - let time_with_comma = millis_to_string(time); - - println!("Trying to type {time_with_comma}"); - keyboard::type_text(&time_with_comma)?; - keyboard::click_tab()?; - keyboard::click_tab()?; - Ok(()) -} fn main() { - let password = "test"; - let broker_domain = "broker.emqx.io"; + let settings = iced::Settings { + window: iced::window::Settings { + size: (300, 500), + resizable: true, + decorations: true, + ..Default::default() + }, + + ..Default::default() + }; - let c = comm::Comm::new(&broker_domain, &password, handle_time).unwrap(); - - //Gui::run(Settings::default()); - - println!("Processing requests..."); - c.listen(); + gui::Gui::run(settings).unwrap(); }