1
0

stop using DynResult for error handling

I plan to refactor the code to have a single "fatal_error" channel that
all threads get a handle to, and `Box<dyn Error>` can't be sent between
threads safely.
This commit is contained in:
Daniel Prilik
2020-11-04 11:50:13 -05:00
parent 31d86e165a
commit 905c33ad4a
12 changed files with 147 additions and 112 deletions

View File

@@ -1,57 +1,68 @@
use std::fs;
use std::io::prelude::*;
use crate::DynResult;
use crate::error::{Error, Result};
// This current implementation is incredibly barebones.
// It literally just reads/writes the last selected mode from the file.
// This current implementation is incredibly barebones and brittle.
// It literally just reads/writes the last selected mode from the file as a raw
// index into the modes array. That's it.
//
// 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.
// While it wouldn't be too hard to get a proper serde-based implementation up
// and running, it'll just bump compile times up for no good reason, so I'll
// only set it up once I need the added complexity.
// TODO: stop using strings for errors lol
pub struct Config {
pub last_mode: usize,
}
fn get_cfg_file() -> DynResult<fs::File> {
fn get_cfg_file() -> Result<fs::File> {
let proj_dirs = directories::ProjectDirs::from("com", "prilik", "surface-dial-daemon")
.ok_or("could not open config directory")?;
.ok_or_else(|| Error::ConfigFile("could not open config directory".into()))?;
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")?;
fs::create_dir_all(cfg_folder)
.map_err(|e| Error::ConfigFile(format!("could not create config dir: {}", e)))?;
if !cfg_file_path.exists() {
fs::write(&cfg_file_path, "0")?;
fs::write(&cfg_file_path, "0")
.map_err(|e| Error::ConfigFile(format!("could not write to config file: {}", e)))?;
}
let cfg_file = fs::OpenOptions::new()
.write(true)
.read(true)
.open(cfg_file_path)
.map_err(|e| format!("could not open config file: {}", e))?;
.map_err(|e| Error::ConfigFile(format!("could not open config file: {}", e)))?;
Ok(cfg_file)
}
impl Config {
pub fn from_disk() -> DynResult<Config> {
pub fn from_disk() -> Result<Config> {
let mut cfg_file = get_cfg_file()?;
let mut content = String::new();
cfg_file.read_to_string(&mut content)?;
cfg_file
.read_to_string(&mut content)
.map_err(|e| Error::ConfigFile(format!("could not read the config file: {}", e)))?;
let last_mode = content.parse()?;
let last_mode = content
.parse()
.map_err(|e| Error::ConfigFile(format!("could not parse the config file: {}", e)))?;
Ok(Config { last_mode })
}
pub fn to_disk(&self) -> DynResult<()> {
pub fn to_disk(&self) -> Result<()> {
let mut cfg_file = get_cfg_file()?;
cfg_file.write_all(format!("{}", self.last_mode).as_bytes())?;
cfg_file
.write_all(format!("{}", self.last_mode).as_bytes())
.map_err(|e| Error::ConfigFile(format!("could not write to the config file: {}", e)))?;
Ok(())
}

View File

@@ -1,7 +1,7 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::{Error, Result};
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
@@ -25,27 +25,33 @@ impl ControlMode for Media {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(true, Some(36))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
self.fake_input
.key_click(&[EV_KEY::KEY_PLAYPAUSE])
.map_err(Error::Evdev)?;
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
eprintln!("next song");
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
self.fake_input
.key_click(&[EV_KEY::KEY_NEXTSONG])
.map_err(Error::Evdev)?;
} else {
eprintln!("last song");
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
self.fake_input
.key_click(&[EV_KEY::KEY_PREVIOUSSONG])
.map_err(Error::Evdev)?;
}
Ok(())
}

View File

@@ -1,6 +1,6 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::DynResult;
use crate::error::Result;
impl ControlMode for () {
fn meta(&self) -> ControlModeMeta {
@@ -10,20 +10,20 @@ impl ControlMode for () {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(false, Some(0))?;
Ok(())
}
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_dial(&mut self, _haptics: &DialHaptics, _delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _haptics: &DialHaptics, _delta: i32) -> Result<()> {
Ok(())
}
}

View File

@@ -5,8 +5,8 @@ use std::time::Duration;
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::Result;
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
@@ -152,29 +152,29 @@ impl ControlMode for Paddle {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(false, Some(3600))?;
self.msg.send(Msg::Enabled(true))?;
let _ = self.msg.send(Msg::Enabled(true));
Ok(())
}
fn on_end(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
self.msg.send(Msg::Enabled(false))?;
fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> {
let _ = self.msg.send(Msg::Enabled(false));
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
self.msg.send(Msg::ButtonDown)?;
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
let _ = self.msg.send(Msg::ButtonDown);
Ok(())
}
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
self.msg.send(Msg::ButtonUp)?;
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
let _ = self.msg.send(Msg::ButtonUp);
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
self.msg.send(Msg::Delta(delta))?;
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
let _ = self.msg.send(Msg::Delta(delta));
Ok(())
}
}

View File

@@ -1,7 +1,7 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::{Error, Result};
use crate::fake_input::{FakeInput, ScrollStep};
use crate::DynResult;
pub struct Scroll {
fake_input: FakeInput,
@@ -23,26 +23,30 @@ impl ControlMode for Scroll {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(false, Some(90))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
eprintln!("scroll down");
self.fake_input.scroll_step(ScrollStep::Down)?;
self.fake_input
.scroll_step(ScrollStep::Down)
.map_err(Error::Evdev)?;
} else {
eprintln!("scroll up");
self.fake_input.scroll_step(ScrollStep::Up)?;
self.fake_input
.scroll_step(ScrollStep::Up)
.map_err(Error::Evdev)?;
}
Ok(())

View File

@@ -1,7 +1,7 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::{Error, Result};
use crate::fake_input::FakeInput;
use crate::DynResult;
pub struct ScrollMT {
acc_delta: i32,
@@ -26,35 +26,37 @@ impl ControlMode for ScrollMT {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(false, Some(3600))?;
self.acc_delta = 0;
self.fake_input.scroll_mt_start()?;
self.fake_input.scroll_mt_start().map_err(Error::Evdev)?;
Ok(())
}
fn on_end(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
self.fake_input.scroll_mt_end()?;
fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_end().map_err(Error::Evdev)?;
Ok(())
}
// HACK: the button will reset the scroll event, which sometimes helps
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
self.fake_input.scroll_mt_end()?;
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_end().map_err(Error::Evdev)?;
Ok(())
}
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
self.fake_input.scroll_mt_start()?;
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_start().map_err(Error::Evdev)?;
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
self.acc_delta += delta;
self.fake_input.scroll_mt_step(self.acc_delta)?;
self.fake_input
.scroll_mt_step(self.acc_delta)
.map_err(Error::Evdev)?;
Ok(())
}

View File

@@ -1,7 +1,7 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::{Error, Result};
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
@@ -25,32 +25,36 @@ impl ControlMode for Volume {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(true, Some(36 * 2))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
// TODO: support double-click to play/pause
Ok(())
}
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
eprintln!("play/pause");
// self.fake_input.mute()?
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
self.fake_input
.key_click(&[EV_KEY::KEY_PLAYPAUSE])
.map_err(Error::Evdev)?;
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
eprintln!("volume up");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?;
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])
.map_err(Error::Evdev)?;
} else {
eprintln!("volume down");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?;
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])
.map_err(Error::Evdev)?;
}
Ok(())

View File

@@ -1,7 +1,7 @@
use crate::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics;
use crate::error::{Error, Result};
use crate::fake_input::FakeInput;
use crate::DynResult;
use evdev_rs::enums::EV_KEY;
@@ -25,28 +25,30 @@ impl ControlMode for Zoom {
}
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
haptics.set_mode(true, Some(36))?;
Ok(())
}
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
eprintln!("zoom in");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])
.map_err(Error::Evdev)?;
} else {
eprintln!("zoom out");
self.fake_input
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])?;
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])
.map_err(Error::Evdev)?;
}
Ok(())

View File

@@ -1,7 +1,7 @@
use std::sync::{Arc, Mutex};
use crate::dial_device::{DialDevice, DialEventKind, DialHaptics};
use crate::DynResult;
use crate::error::{Error, Result};
pub mod controls;
@@ -13,14 +13,14 @@ pub struct ControlModeMeta {
pub trait ControlMode {
fn meta(&self) -> ControlModeMeta;
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_end(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()>;
fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_press(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()>;
fn on_dial(&mut self, haptics: &DialHaptics, delta: i32) -> DynResult<()>;
fn on_btn_press(&mut self, haptics: &DialHaptics) -> Result<()>;
fn on_btn_release(&mut self, haptics: &DialHaptics) -> Result<()>;
fn on_dial(&mut self, haptics: &DialHaptics, delta: i32) -> Result<()>;
}
enum ActiveMode {
@@ -59,7 +59,7 @@ impl DialController {
}
}
pub fn run(&mut self) -> DynResult<()> {
pub fn run(&mut self) -> Result<()> {
let initial_mode = match self.active_mode {
ActiveMode::Normal(i) => i,
ActiveMode::Meta => 0,
@@ -137,7 +137,7 @@ impl ControlMode for MetaMode {
unreachable!() // meta mode never queries itself
}
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
use notify_rust::*;
self.notif = Some(
Notification::new()
@@ -150,7 +150,8 @@ impl ControlMode for MetaMode {
self.metas[self.current_mode].name
))
.icon("emblem-system")
.show()?,
.show()
.map_err(Error::Notif)?,
);
haptics.buzz(1)?;
@@ -161,11 +162,11 @@ impl ControlMode for MetaMode {
Ok(())
}
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()> {
fn on_btn_release(&mut self, haptics: &DialHaptics) -> Result<()> {
if self.first_release {
self.first_release = false;
} else {
@@ -182,7 +183,7 @@ impl ControlMode for MetaMode {
Ok(())
}
fn on_dial(&mut self, _haptics: &DialHaptics, delta: i32) -> DynResult<()> {
fn on_dial(&mut self, _haptics: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
self.current_mode += 1;
} else {

View File

@@ -5,7 +5,7 @@ use std::time::Duration;
use evdev_rs::{Device, InputEvent, ReadStatus};
use hidapi::{HidApi, HidDevice};
use crate::error::Error;
use crate::error::{Error, Result};
pub struct DialDevice {
long_press_timeout: Duration,
@@ -31,7 +31,7 @@ pub enum DialEventKind {
}
impl DialDevice {
pub fn new(long_press_timeout: Duration) -> Result<DialDevice, crate::Error> {
pub fn new(long_press_timeout: Duration) -> Result<DialDevice> {
let mut control = None;
let mut axis = None;
@@ -72,13 +72,13 @@ impl DialDevice {
let (events_tx, events_rx) = mpsc::channel();
// TODO: interleave control events with regular events
// (once we figure out what control events actually do...)
// inb4 "y not async/await"
std::thread::spawn({
let events = events_tx;
move || loop {
events
.send(axis.next_event(evdev_rs::ReadFlag::NORMAL))
.expect("failed to send axis event");
let _ = events.send(axis.next_event(evdev_rs::ReadFlag::NORMAL));
}
});
@@ -91,7 +91,7 @@ impl DialDevice {
})
}
pub fn next_event(&mut self) -> Result<DialEvent, Error> {
pub fn next_event(&mut self) -> Result<DialEvent> {
let evt = if self.possible_long_press {
self.events.recv_timeout(self.long_press_timeout)
} else {
@@ -166,7 +166,7 @@ pub struct DialHaptics {
}
impl DialHaptics {
fn new() -> Result<DialHaptics, Error> {
fn new() -> Result<DialHaptics> {
let api = HidApi::new().map_err(Error::HidError)?;
let hid_device = api.open(0x045e, 0x091b).map_err(|_| Error::MissingDial)?;
@@ -190,7 +190,7 @@ impl DialHaptics {
/// `steps` should be a value between 0 and 3600, which corresponds to the
/// number of subdivisions the dial should use. If left unspecified, this
/// defaults to 36 (an arbitrary choice that "feels good" most of the time)
pub fn set_mode(&self, haptics: bool, steps: Option<u16>) -> Result<(), Error> {
pub fn set_mode(&self, haptics: bool, steps: Option<u16>) -> Result<()> {
let steps = steps.unwrap_or(36);
assert!(steps <= 3600);
@@ -213,7 +213,7 @@ impl DialHaptics {
Ok(())
}
pub fn buzz(&self, repeat: u8) -> Result<(), Error> {
pub fn buzz(&self, repeat: u8) -> Result<()> {
let mut buf = [0; 5];
buf[0] = 0x01; // Report ID
buf[1] = repeat; // RepeatCount

View File

@@ -3,8 +3,11 @@ use std::io;
use evdev_rs::InputEvent;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
ConfigFile(String),
OpenDevInputDir(io::Error),
OpenEventFile(std::path::PathBuf, io::Error),
HidError(hidapi::HidError),
@@ -12,12 +15,13 @@ pub enum Error {
MultipleDials,
UnexpectedEvt(InputEvent),
Evdev(io::Error),
Io(io::Error),
Notif(notify_rust::error::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::ConfigFile(e) => write!(f, "Could not open config file: {}", e),
Error::OpenDevInputDir(e) => write!(f, "Could not open /dev/input directory: {}", e),
Error::OpenEventFile(path, e) => write!(f, "Could not open {:?}: {}", path, e),
Error::HidError(e) => write!(f, "HID API Error: {}", e),
@@ -25,7 +29,7 @@ impl fmt::Display for Error {
Error::MultipleDials => write!(f, "Found multiple dials"),
Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt),
Error::Evdev(e) => write!(f, "Evdev error: {}", e),
Error::Io(e) => write!(f, "Io error: {}", e),
Error::Notif(e) => write!(f, "Notification error: {}", e),
}
}
}

View File

@@ -7,13 +7,11 @@ mod dial_device;
mod error;
mod fake_input;
pub type DynResult<T> = Result<T, Box<dyn std::error::Error>>;
use std::sync::mpsc;
use crate::controller::DialController;
use crate::dial_device::DialDevice;
use crate::error::Error;
use crate::error::Result;
use notify_rust::{Hint, Notification, Timeout};
use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
@@ -58,23 +56,9 @@ fn main() {
std::process::exit(1);
});
if let Err(e) = true_main(kill_notif_tx.clone()) {
println!("{}", e);
}
let _ = kill_notif_tx.send(None); // silently shut down
let _ = handle.join();
}
fn true_main(kill_notif_tx: mpsc::Sender<Option<(String, &'static str)>>) -> DynResult<()> {
println!("Started");
let cfg = config::Config::from_disk()?;
let dial = DialDevice::new(std::time::Duration::from_millis(750))?;
println!("Found the dial");
std::thread::spawn(move || {
std::thread::spawn({
let kill_notif_tx = kill_notif_tx.clone();
move || {
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
for sig in signals.forever() {
eprintln!("received signal {:?}", sig);
@@ -83,8 +67,25 @@ fn true_main(kill_notif_tx: mpsc::Sender<Option<(String, &'static str)>>) -> Dyn
Err(_) => std::process::exit(1),
}
}
}
});
if let Err(e) = true_main() {
println!("{}", e);
}
let _ = kill_notif_tx.send(None); // silently shut down
let _ = handle.join();
}
fn true_main() -> Result<()> {
println!("Started");
let cfg = config::Config::from_disk()?;
let dial = DialDevice::new(std::time::Duration::from_millis(750))?;
println!("Found the dial");
let mut controller = DialController::new(
dial,
cfg.last_mode,