add barebones on-disk mode persistance
This commit is contained in:
21
Cargo.lock
generated
21
Cargo.lock
generated
@@ -111,6 +111,15 @@ dependencies = [
|
|||||||
"libdbus-sys",
|
"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]]
|
[[package]]
|
||||||
name = "dirs"
|
name = "dirs"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -122,6 +131,17 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "evdev-rs"
|
name = "evdev-rs"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
@@ -356,6 +376,7 @@ dependencies = [
|
|||||||
name = "surface-dial-daemon"
|
name = "surface-dial-daemon"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"directories",
|
||||||
"evdev-rs",
|
"evdev-rs",
|
||||||
"hidapi",
|
"hidapi",
|
||||||
"notify-rust",
|
"notify-rust",
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ authors = ["Daniel Prilik <danielprilik@gmail.com>"]
|
|||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
directories = "3.0"
|
||||||
# master includes a PR that implements `Send` for `Device` and `UInputDevice`
|
# master includes a PR that implements `Send` for `Device` and `UInputDevice`
|
||||||
evdev-rs = { git = "https://github.com/ndesh26/evdev-rs.git" }
|
evdev-rs = { git = "https://github.com/ndesh26/evdev-rs.git" }
|
||||||
hidapi = { version = "1.2.3", default-features = false, features = ["linux-shared-hidraw"] }
|
hidapi = { version = "1.2.3", default-features = false, features = ["linux-shared-hidraw"] }
|
||||||
|
|||||||
@@ -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] Dynamically switch between operating modes
|
||||||
- [x] Using some-sort of on-device mechanism (e.g: long-press)
|
- [x] Using some-sort of on-device mechanism (e.g: long-press)
|
||||||
- [ ] Context-sensitive (based on currently open application)
|
- [ ] Context-sensitive (based on currently open application)
|
||||||
- [ ] Mode Persistence
|
- [x] Mode Persistence (keep mode when dial disconnects)
|
||||||
- _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] Haptic Feedback
|
- [x] Haptic Feedback
|
||||||
- https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation
|
- 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
|
- https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf
|
||||||
|
|||||||
58
src/config.rs
Normal file
58
src/config.rs
Normal file
@@ -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<fs::File> {
|
||||||
|
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<Config> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,11 @@ pub struct DialController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DialController {
|
impl DialController {
|
||||||
pub fn new(device: DialDevice, modes: Vec<Box<dyn ControlMode>>) -> DialController {
|
pub fn new(
|
||||||
|
device: DialDevice,
|
||||||
|
initial_mode: usize,
|
||||||
|
modes: Vec<Box<dyn ControlMode>>,
|
||||||
|
) -> DialController {
|
||||||
let metas = modes.iter().map(|m| m.meta()).collect();
|
let metas = modes.iter().map(|m| m.meta()).collect();
|
||||||
|
|
||||||
let new_mode = Arc::new(Mutex::new(None));
|
let new_mode = Arc::new(Mutex::new(None));
|
||||||
@@ -48,7 +52,7 @@ impl DialController {
|
|||||||
device,
|
device,
|
||||||
|
|
||||||
modes,
|
modes,
|
||||||
active_mode: ActiveMode::Normal(0),
|
active_mode: ActiveMode::Normal(initial_mode),
|
||||||
|
|
||||||
new_mode: new_mode.clone(),
|
new_mode: new_mode.clone(),
|
||||||
meta_mode: Box::new(MetaMode::new(new_mode, 0, metas)),
|
meta_mode: Box::new(MetaMode::new(new_mode, 0, metas)),
|
||||||
@@ -162,9 +166,14 @@ impl ControlMode for MetaMode {
|
|||||||
self.first_release = false;
|
self.first_release = false;
|
||||||
} else {
|
} else {
|
||||||
*self.new_mode.lock().unwrap() = Some(self.current_mode);
|
*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();
|
self.notif.take().unwrap().close();
|
||||||
|
haptics.buzz(1)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/main.rs
10
src/main.rs
@@ -1,6 +1,7 @@
|
|||||||
#![allow(clippy::collapsible_if, clippy::new_without_default)]
|
#![allow(clippy::collapsible_if, clippy::new_without_default)]
|
||||||
|
|
||||||
mod common;
|
mod common;
|
||||||
|
mod config;
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
mod dial_device;
|
mod dial_device;
|
||||||
mod error;
|
mod error;
|
||||||
@@ -22,12 +23,16 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn true_main() -> DynResult<()> {
|
fn true_main() -> DynResult<()> {
|
||||||
println!("Started.");
|
println!("Started");
|
||||||
|
|
||||||
|
let cfg = config::Config::from_disk()?;
|
||||||
|
|
||||||
let dial = DialDevice::new(std::time::Duration::from_millis(750))?;
|
let dial = DialDevice::new(std::time::Duration::from_millis(750))?;
|
||||||
println!("Found the dial.");
|
println!("Found the dial");
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
// TODO: use this persistent notification for the meta mode.
|
||||||
|
|
||||||
let active_notification = Notification::new()
|
let active_notification = Notification::new()
|
||||||
.hint(Hint::Resident(true))
|
.hint(Hint::Resident(true))
|
||||||
.hint(Hint::Category("device".into()))
|
.hint(Hint::Category("device".into()))
|
||||||
@@ -48,6 +53,7 @@ fn true_main() -> DynResult<()> {
|
|||||||
|
|
||||||
let mut controller = DialController::new(
|
let mut controller = DialController::new(
|
||||||
dial,
|
dial,
|
||||||
|
cfg.last_mode,
|
||||||
vec![
|
vec![
|
||||||
Box::new(controller::controls::ScrollZoom::new()),
|
Box::new(controller::controls::ScrollZoom::new()),
|
||||||
Box::new(controller::controls::Volume::new()),
|
Box::new(controller::controls::Volume::new()),
|
||||||
|
|||||||
Reference in New Issue
Block a user