From 82e08c8e4ac90250cc13a0bb2774d95dffb9ef80 Mon Sep 17 00:00:00 2001 From: Daniel Prilik Date: Sat, 31 Oct 2020 00:15:34 -0400 Subject: [PATCH] add barebones on-disk mode persistance --- Cargo.lock | 21 ++++++++++++++++ Cargo.toml | 1 + README.md | 3 +-- src/config.rs | 58 +++++++++++++++++++++++++++++++++++++++++++ src/controller/mod.rs | 15 ++++++++--- src/main.rs | 10 ++++++-- 6 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 src/config.rs diff --git a/Cargo.lock b/Cargo.lock index 5bea059..97b4bf9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,6 +111,15 @@ dependencies = [ "libdbus-sys", ] +[[package]] +name = "directories" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs" version = "1.0.5" @@ -122,6 +131,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "dirs-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "evdev-rs" version = "0.4.0" @@ -356,6 +376,7 @@ dependencies = [ name = "surface-dial-daemon" version = "0.1.0" dependencies = [ + "directories", "evdev-rs", "hidapi", "notify-rust", diff --git a/Cargo.toml b/Cargo.toml index 084e703..696c104 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Daniel Prilik "] edition = "2018" [dependencies] +directories = "3.0" # master includes a PR that implements `Send` for `Device` and `UInputDevice` evdev-rs = { git = "https://github.com/ndesh26/evdev-rs.git" } hidapi = { version = "1.2.3", default-features = false, features = ["linux-shared-hidraw"] } diff --git a/README.md b/README.md index f84f84c..5f49dda 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,7 @@ It would be cool to create some sort of GUI overlay (similar to the Windows one) - [x] Dynamically switch between operating modes - [x] Using some-sort of on-device mechanism (e.g: long-press) - [ ] Context-sensitive (based on currently open application) -- [ ] Mode Persistence - - _At the moment, whenever the dial disconnects, the daemon is re-launched, which resets the active mode to the default one. It would be good to have some on-disk persistence to remember the last selected mode._ +- [x] Mode Persistence (keep mode when dial disconnects) - [x] Haptic Feedback - https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation - https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..c415212 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,58 @@ +use std::fs; +use std::io::prelude::*; + +use crate::DynResult; + +// This current implementation is incredibly barebones. +// It literally just reads/writes the last selected mode from the file. +// +// No TOML, No JSON, just raw text. +// +// It shouldn't be too hard to get a proper serde-based implementation up and +// running, it's moreso that it'll bump compile times for no good reason. I'll +// set that all up once I need the complexity. + +pub struct Config { + pub last_mode: usize, +} + +fn get_cfg_file() -> DynResult { + let proj_dirs = directories::ProjectDirs::from("com", "prilik", "surface-dial-daemon") + .ok_or("could not open config directory")?; + let cfg_folder = proj_dirs.config_dir(); + let cfg_file_path = proj_dirs.config_dir().join("config.txt"); + + fs::create_dir_all(cfg_folder).map_err(|_| "could not create config dir")?; + + if !cfg_file_path.exists() { + fs::write(&cfg_file_path, "0")?; + } + + let cfg_file = fs::OpenOptions::new() + .write(true) + .read(true) + .open(cfg_file_path) + .map_err(|e| format!("could not open config file: {}", e))?; + + Ok(cfg_file) +} + +impl Config { + pub fn from_disk() -> DynResult { + let mut cfg_file = get_cfg_file()?; + + let mut content = String::new(); + cfg_file.read_to_string(&mut content)?; + + let last_mode = content.parse()?; + + Ok(Config { last_mode }) + } + + pub fn to_disk(&self) -> DynResult<()> { + let mut cfg_file = get_cfg_file()?; + cfg_file.write_all(format!("{}", self.last_mode).as_bytes())?; + + Ok(()) + } +} diff --git a/src/controller/mod.rs b/src/controller/mod.rs index 14affef..f8ad99c 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -39,7 +39,11 @@ pub struct DialController { } impl DialController { - pub fn new(device: DialDevice, modes: Vec>) -> DialController { + pub fn new( + device: DialDevice, + initial_mode: usize, + modes: Vec>, + ) -> DialController { let metas = modes.iter().map(|m| m.meta()).collect(); let new_mode = Arc::new(Mutex::new(None)); @@ -48,7 +52,7 @@ impl DialController { device, modes, - active_mode: ActiveMode::Normal(0), + active_mode: ActiveMode::Normal(initial_mode), new_mode: new_mode.clone(), meta_mode: Box::new(MetaMode::new(new_mode, 0, metas)), @@ -162,9 +166,14 @@ impl ControlMode for MetaMode { self.first_release = false; } else { *self.new_mode.lock().unwrap() = Some(self.current_mode); - haptics.buzz(1)?; + + crate::config::Config { + last_mode: self.current_mode, + } + .to_disk()?; self.notif.take().unwrap().close(); + haptics.buzz(1)?; } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 882a338..8534cfe 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![allow(clippy::collapsible_if, clippy::new_without_default)] mod common; +mod config; pub mod controller; mod dial_device; mod error; @@ -22,12 +23,16 @@ fn main() { } fn true_main() -> DynResult<()> { - println!("Started."); + println!("Started"); + + let cfg = config::Config::from_disk()?; let dial = DialDevice::new(std::time::Duration::from_millis(750))?; - println!("Found the dial."); + println!("Found the dial"); std::thread::spawn(move || { + // TODO: use this persistent notification for the meta mode. + let active_notification = Notification::new() .hint(Hint::Resident(true)) .hint(Hint::Category("device".into())) @@ -48,6 +53,7 @@ fn true_main() -> DynResult<()> { let mut controller = DialController::new( dial, + cfg.last_mode, vec![ Box::new(controller::controls::ScrollZoom::new()), Box::new(controller::controls::Volume::new()),