1
0
Files
surface-dial-linux/src/controller/mod.rs
2020-11-06 00:48:38 -05:00

229 lines
6.3 KiB
Rust

use std::sync::{Arc, Mutex};
use crate::dial_device::{DialDevice, DialEventKind, DialHaptics};
use crate::error::{Error, Result};
pub mod controls;
pub struct ControlModeMeta {
/// Mode Name (as displayed in the Meta selection menu)
name: &'static str,
/// Mode Icon (as displayed in the Meta selection menu)
///
/// This can be a file:// url, or a standard FreeDesktop icon name.
icon: &'static str,
/// Enable automatic haptic feedback when rotating the dial.
haptics: bool,
/// How many sections the dial should be divided into (from 0 to 3600).
steps: u16,
}
pub trait ControlMode {
fn meta(&self) -> ControlModeMeta;
fn on_start(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
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 {
Normal(usize),
Meta,
}
pub struct DialController {
device: DialDevice,
modes: Vec<Box<dyn ControlMode>>,
active_mode: ActiveMode,
new_mode: Arc<Mutex<Option<usize>>>,
meta_mode: Box<dyn ControlMode>, // concrete type is always `MetaMode`
}
impl 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 new_mode = Arc::new(Mutex::new(None));
DialController {
device,
modes,
active_mode: ActiveMode::Normal(initial_mode),
new_mode: new_mode.clone(),
meta_mode: Box::new(MetaMode::new(new_mode, 0, metas)),
}
}
pub fn run(&mut self) -> Result<()> {
loop {
let evt = self.device.next_event()?;
let haptics = self.device.haptics();
if let Some(new_mode) = self.new_mode.lock().unwrap().take() {
self.active_mode = ActiveMode::Normal(new_mode);
let mode = &mut self.modes[new_mode];
haptics.set_mode(mode.meta().haptics, mode.meta().steps)?;
mode.on_start(haptics)?;
}
let mode = match self.active_mode {
ActiveMode::Normal(idx) => &mut self.modes[idx],
ActiveMode::Meta => &mut self.meta_mode,
};
match evt.kind {
DialEventKind::Ignored => {}
DialEventKind::Connect => {
eprintln!("Dial Connected");
haptics.set_mode(mode.meta().haptics, mode.meta().steps)?;
mode.on_start(haptics)?
}
DialEventKind::Disconnect => {
eprintln!("Dial Disconnected");
mode.on_end(haptics)?
}
DialEventKind::ButtonPress => mode.on_btn_press(haptics)?,
DialEventKind::ButtonRelease => mode.on_btn_release(haptics)?,
DialEventKind::Dial(delta) => mode.on_dial(haptics, delta)?,
DialEventKind::ButtonLongPress => {
eprintln!("long press!");
if !matches!(self.active_mode, ActiveMode::Meta) {
mode.on_end(haptics)?;
self.active_mode = ActiveMode::Meta;
// meta_mode sets haptic feedback manually
self.meta_mode.on_start(haptics)?;
}
}
}
}
}
}
/// A mode for switching between modes.
struct MetaMode {
// constant
metas: Vec<ControlModeMeta>,
// stateful (across invocations)
current_mode: usize,
new_mode: Arc<Mutex<Option<usize>>>,
// reset in on_start
first_release: bool,
notif: Option<notify_rust::NotificationHandle>,
}
impl MetaMode {
fn new(
new_mode: Arc<Mutex<Option<usize>>>,
current_mode: usize,
metas: Vec<ControlModeMeta>,
) -> MetaMode {
MetaMode {
metas,
current_mode,
new_mode,
first_release: true,
notif: None,
}
}
}
impl ControlMode for MetaMode {
fn meta(&self) -> ControlModeMeta {
unreachable!() // meta mode never queries itself
}
fn on_start(&mut self, haptics: &DialHaptics) -> Result<()> {
use notify_rust::*;
self.notif = Some(
Notification::new()
.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()))
.timeout(Timeout::Never)
.summary("Surface Dial")
.body(&format!(
"Entered Meta Mode (From Mode: {})",
self.metas[self.current_mode].name
))
.icon("emblem-system")
.show()
.map_err(Error::Notif)?,
);
haptics.set_mode(true, 36)?;
haptics.buzz(1)?;
self.first_release = true;
Ok(())
}
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> Result<()> {
Ok(())
}
fn on_btn_release(&mut self, haptics: &DialHaptics) -> Result<()> {
if self.first_release {
self.first_release = false;
} else {
*self.new_mode.lock().unwrap() = Some(self.current_mode);
crate::config::Config {
last_mode: self.current_mode,
}
.to_disk()?;
self.notif.take().unwrap().close();
haptics.buzz(1)?;
}
Ok(())
}
fn on_dial(&mut self, _haptics: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 {
self.current_mode += 1;
} else {
if self.current_mode == 0 {
self.current_mode = self.metas.len() - 1;
} else {
self.current_mode -= 1;
}
};
self.current_mode %= self.metas.len();
let mode_meta = &self.metas[self.current_mode];
if let Some(ref mut notification) = self.notif {
notification
.body(&format!("New Mode: {}", mode_meta.name))
.icon(mode_meta.icon);
notification.update();
}
Ok(())
}
}