haptics!
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation With a little bit of trial and error (and a crash-course in how the heck HID even works), I figured out how to get the dial to provide haptic feedback! Along the way, I also learned that you can take advantage of the (incorrectly named) Resolution Multiplier field to customize how many "steps" the dial should have, offloading the work to the device itself! Very cool!!
This commit is contained in:
@@ -1,38 +1,3 @@
|
||||
pub enum DialDir {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
pub struct ThresholdHelper {
|
||||
sensitivity: i32,
|
||||
pos: i32,
|
||||
}
|
||||
|
||||
impl ThresholdHelper {
|
||||
pub fn new(sensitivity: i32) -> ThresholdHelper {
|
||||
ThresholdHelper {
|
||||
sensitivity,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, delta: i32) -> Option<DialDir> {
|
||||
self.pos += delta;
|
||||
|
||||
if self.pos > self.sensitivity {
|
||||
self.pos -= self.sensitivity;
|
||||
return Some(DialDir::Right);
|
||||
}
|
||||
|
||||
if self.pos < -self.sensitivity {
|
||||
self.pos += self.sensitivity;
|
||||
return Some(DialDir::Left);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
use notify_rust::error::Result as NotifyResult;
|
||||
use notify_rust::{Hint, Notification, NotificationHandle, Timeout};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
@@ -106,12 +107,6 @@ impl Drop for DPad {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DPad {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DPad {
|
||||
pub fn new() -> DPad {
|
||||
let (msg_tx, msg_rx) = mpsc::channel();
|
||||
@@ -128,17 +123,22 @@ impl DPad {
|
||||
}
|
||||
|
||||
impl ControlMode for DPad {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(3600))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
eprintln!("space");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_SPACE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
self.msg.send(Msg::Delta(delta))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
use crate::common::{DialDir, ThresholdHelper};
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct Media {
|
||||
thresh: ThresholdHelper,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl Media {
|
||||
pub fn new(sensitivity: i32) -> Media {
|
||||
pub fn new() -> Media {
|
||||
Media {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Media {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(36))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
Some(DialDir::Left) => {
|
||||
eprintln!("next song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
eprintln!("last song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
|
||||
}
|
||||
None => {}
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
eprintln!("last song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
|
||||
} else {
|
||||
eprintln!("next song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod dpad;
|
||||
mod media;
|
||||
mod null;
|
||||
mod scroll_zoom;
|
||||
mod volume;
|
||||
|
||||
pub use self::dpad::*;
|
||||
pub use self::media::*;
|
||||
pub use self::null::*;
|
||||
pub use self::scroll_zoom::*;
|
||||
pub use self::volume::*;
|
||||
|
||||
30
src/controller/controls/null.rs
Normal file
30
src/controller/controls/null.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::DynResult;
|
||||
|
||||
pub struct Null {}
|
||||
|
||||
impl Null {
|
||||
pub fn new() -> Null {
|
||||
Null {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Null {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(0))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, _haptics: &DialHaptics, _delta: i32) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
use crate::common::{action_notification, DialDir, ThresholdHelper};
|
||||
use crate::common::action_notification;
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::{FakeInput, ScrollStep};
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct ScrollZoom {
|
||||
thresh: ThresholdHelper,
|
||||
zoom: bool,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl ScrollZoom {
|
||||
pub fn new(sensitivity: i32) -> ScrollZoom {
|
||||
pub fn new() -> ScrollZoom {
|
||||
ScrollZoom {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
zoom: false,
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
@@ -23,46 +22,55 @@ impl ScrollZoom {
|
||||
}
|
||||
}
|
||||
|
||||
const ZOOM_SENSITIVITY: u16 = 36;
|
||||
const SCROLL_SENSITIVITY: u16 = 90;
|
||||
|
||||
impl ControlMode for ScrollZoom {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
self.zoom = !self.zoom;
|
||||
haptics.buzz(1)?;
|
||||
|
||||
if self.zoom {
|
||||
action_notification("Zoom Mode", "zoom-in")?;
|
||||
haptics.set_mode(false, Some(ZOOM_SENSITIVITY))?;
|
||||
} else {
|
||||
action_notification("ScrollZoom Mode", "input-mouse")?;
|
||||
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
None => {}
|
||||
Some(DialDir::Left) => {
|
||||
if self.zoom {
|
||||
eprintln!("zoom out");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])?;
|
||||
} else {
|
||||
eprintln!("scroll up");
|
||||
self.fake_input.scroll_step(ScrollStep::Up)?;
|
||||
}
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
if self.zoom {
|
||||
eprintln!("zoom in");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
|
||||
} else {
|
||||
eprintln!("scroll down");
|
||||
self.fake_input.scroll_step(ScrollStep::Down)?;
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
if self.zoom {
|
||||
eprintln!("zoom in");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
|
||||
} else {
|
||||
eprintln!("scroll down");
|
||||
self.fake_input.scroll_step(ScrollStep::Down)?;
|
||||
}
|
||||
} else {
|
||||
if self.zoom {
|
||||
eprintln!("zoom out");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])?;
|
||||
} else {
|
||||
eprintln!("scroll up");
|
||||
self.fake_input.scroll_step(ScrollStep::Up)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
use crate::common::{DialDir, ThresholdHelper};
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct Volume {
|
||||
thresh: ThresholdHelper,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl Volume {
|
||||
pub fn new(sensitivity: i32) -> Volume {
|
||||
pub fn new() -> Volume {
|
||||
Volume {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Volume {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(36 * 2))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
// TODO: support double-click to mute
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
eprintln!("play/pause");
|
||||
// self.fake_input.mute()?
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
Some(DialDir::Left) => {
|
||||
eprintln!("volume down");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
eprintln!("volume up");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?
|
||||
}
|
||||
None => {}
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
eprintln!("volume up");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?;
|
||||
} else {
|
||||
eprintln!("volume down");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use crate::DynResult;
|
||||
|
||||
use crate::dial_device::{DialDevice, DialEventKind};
|
||||
use crate::dial_device::{DialDevice, DialEventKind, DialHaptics};
|
||||
|
||||
pub mod controls;
|
||||
|
||||
pub trait ControlMode {
|
||||
fn on_btn_press(&mut self) -> DynResult<()>;
|
||||
fn on_btn_release(&mut self) -> DynResult<()>;
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()>;
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()>;
|
||||
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<()>;
|
||||
}
|
||||
|
||||
pub struct DialController {
|
||||
@@ -26,16 +27,20 @@ impl DialController {
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> DynResult<()> {
|
||||
let haptics = self.device.haptics();
|
||||
|
||||
self.mode.on_start(haptics)?;
|
||||
|
||||
loop {
|
||||
let evt = self.device.next_event()?;
|
||||
|
||||
// TODO: press and hold + rotate to switch between modes
|
||||
// TODO: press and hold (+ rotate?) to switch between modes
|
||||
|
||||
match evt.kind {
|
||||
DialEventKind::Ignored => {}
|
||||
DialEventKind::ButtonPress => self.mode.on_btn_press()?,
|
||||
DialEventKind::ButtonRelease => self.mode.on_btn_release()?,
|
||||
DialEventKind::Dial(delta) => self.mode.on_dial(delta)?,
|
||||
DialEventKind::ButtonPress => self.mode.on_btn_press(haptics)?,
|
||||
DialEventKind::ButtonRelease => self.mode.on_btn_release(haptics)?,
|
||||
DialEventKind::Dial(delta) => self.mode.on_dial(haptics, delta)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::fs;
|
||||
use std::time::Duration;
|
||||
|
||||
use evdev_rs::{Device, InputEvent};
|
||||
use hidapi::{HidApi, HidDevice};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
@@ -9,6 +10,7 @@ pub struct DialDevice {
|
||||
// TODO: explore what the control channel can be used for...
|
||||
_control: Device,
|
||||
axis: Device,
|
||||
haptics: DialHaptics,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -30,6 +32,7 @@ impl DialDevice {
|
||||
let mut control = None;
|
||||
let mut axis = None;
|
||||
|
||||
// discover the evdev devices
|
||||
for e in fs::read_dir("/dev/input/").map_err(Error::OpenDevInputDir)? {
|
||||
let e = e.map_err(Error::OpenDevInputDir)?;
|
||||
if !e.file_name().to_str().unwrap().starts_with("event") {
|
||||
@@ -62,6 +65,7 @@ impl DialDevice {
|
||||
Ok(DialDevice {
|
||||
_control: control.ok_or(Error::MissingDial)?,
|
||||
axis: axis.ok_or(Error::MissingDial)?,
|
||||
haptics: DialHaptics::new()?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,6 +83,10 @@ impl DialDevice {
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub fn haptics(&self) -> &DialHaptics {
|
||||
&self.haptics
|
||||
}
|
||||
}
|
||||
|
||||
impl DialEvent {
|
||||
@@ -110,3 +118,67 @@ impl DialEvent {
|
||||
Some(evt)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DialHaptics {
|
||||
hid_device: HidDevice,
|
||||
}
|
||||
|
||||
impl DialHaptics {
|
||||
fn new() -> Result<DialHaptics, Error> {
|
||||
let api = HidApi::new().map_err(Error::HidError)?;
|
||||
let hid_device = api.open(0x045e, 0x091b).map_err(|_| Error::MissingDial)?;
|
||||
|
||||
// let mut buf = [0; 256];
|
||||
|
||||
// buf[0] = 1;
|
||||
// let len = device
|
||||
// .get_feature_report(&mut buf)
|
||||
// .map_err(Error::HidError)?;
|
||||
// eprintln!("1: {:02x?}", &buf[..len]);
|
||||
|
||||
// buf[0] = 2;
|
||||
// let len = device
|
||||
// .get_feature_report(&mut buf)
|
||||
// .map_err(Error::HidError)?;
|
||||
// eprintln!("2: {:02x?}", &buf[..len]);
|
||||
|
||||
Ok(DialHaptics { hid_device })
|
||||
}
|
||||
|
||||
/// `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> {
|
||||
let steps = steps.unwrap_or(36);
|
||||
assert!(steps <= 3600);
|
||||
|
||||
let steps_lo = steps & 0xff;
|
||||
let steps_hi = (steps >> 8) & 0xff;
|
||||
|
||||
let mut buf = [0; 8];
|
||||
buf[0] = 1;
|
||||
buf[1] = steps_lo as u8; // steps
|
||||
buf[2] = steps_hi as u8; // steps
|
||||
buf[3] = 0x00; // Repeat Count
|
||||
buf[4] = if haptics { 0x03 } else { 0x02 }; // auto trigger
|
||||
buf[5] = 0x00; // Waveform Cutoff Time
|
||||
buf[6] = 0x00; // retrigger period
|
||||
buf[7] = 0x00; // retrigger period
|
||||
self.hid_device
|
||||
.send_feature_report(&buf[..8])
|
||||
.map_err(Error::HidError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn buzz(&self, repeat: u8) -> Result<(), Error> {
|
||||
let mut buf = [0; 5];
|
||||
buf[0] = 0x01; // Report ID
|
||||
buf[1] = repeat; // RepeatCount
|
||||
buf[2] = 0x03; // ManualTrigger
|
||||
buf[3] = 0x00; // RetriggerPeriod (lo)
|
||||
buf[4] = 0x00; // RetriggerPeriod (hi)
|
||||
self.hid_device.write(&buf).map_err(Error::HidError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use evdev_rs::InputEvent;
|
||||
pub enum Error {
|
||||
OpenDevInputDir(io::Error),
|
||||
OpenEventFile(std::path::PathBuf, io::Error),
|
||||
HidError(hidapi::HidError),
|
||||
MissingDial,
|
||||
MultipleDials,
|
||||
UnexpectedEvt(InputEvent),
|
||||
@@ -19,6 +20,7 @@ impl fmt::Display for Error {
|
||||
match self {
|
||||
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),
|
||||
Error::MissingDial => write!(f, "Could not find the Surface Dial"),
|
||||
Error::MultipleDials => write!(f, "Found multiple dials"),
|
||||
Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt),
|
||||
|
||||
31
src/main.rs
31
src/main.rs
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::collapsible_if, clippy::new_without_default)]
|
||||
|
||||
mod common;
|
||||
pub mod controller;
|
||||
mod dial_device;
|
||||
@@ -10,6 +12,9 @@ use crate::controller::DialController;
|
||||
use crate::dial_device::DialDevice;
|
||||
use crate::error::Error;
|
||||
|
||||
use notify_rust::{Hint, Notification, Timeout};
|
||||
use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = true_main() {
|
||||
println!("{}", e);
|
||||
@@ -22,11 +27,29 @@ fn true_main() -> DynResult<()> {
|
||||
let dial = DialDevice::new()?;
|
||||
println!("Found the dial.");
|
||||
|
||||
common::action_notification("Active!", "input-mouse")?;
|
||||
std::thread::spawn(move || {
|
||||
let active_notification = Notification::new()
|
||||
.hint(Hint::Resident(true))
|
||||
.hint(Hint::Category("device".into()))
|
||||
.timeout(Timeout::Never)
|
||||
.summary("Surface Dial")
|
||||
.body("Active!")
|
||||
.icon("input-mouse")
|
||||
.show()
|
||||
.expect("failed to send notification");
|
||||
|
||||
let default_mode = Box::new(controller::controls::ScrollZoom::new(30));
|
||||
// let default_mode = Box::new(controller::controls::Volume::new(30));
|
||||
// let default_mode = Box::new(controller::controls::Media::new(50));
|
||||
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
|
||||
for sig in signals.forever() {
|
||||
eprintln!("received signal {:?}", sig);
|
||||
active_notification.close();
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// let default_mode = Box::new(controller::controls::Null::new());
|
||||
let default_mode = Box::new(controller::controls::ScrollZoom::new());
|
||||
// let default_mode = Box::new(controller::controls::Volume::new());
|
||||
// let default_mode = Box::new(controller::controls::Media::new());
|
||||
// let default_mode = Box::new(controller::controls::DPad::new());
|
||||
|
||||
let mut controller = DialController::new(dial, default_mode);
|
||||
|
||||
Reference in New Issue
Block a user