From 066c635bd76fd97d4060bc57c98a249414d1c453 Mon Sep 17 00:00:00 2001 From: Daniel Prilik Date: Sat, 7 Nov 2020 11:45:26 -0500 Subject: [PATCH] Separate "vanilla" Volume mode from Media + Volume mode --- README.md | 17 +-- src/controller/controls/media_with_volume.rs | 108 +++++++++++++++++++ src/controller/controls/mod.rs | 2 + src/controller/controls/volume.rs | 67 +----------- src/main.rs | 1 + 5 files changed, 124 insertions(+), 71 deletions(-) create mode 100644 src/controller/controls/media_with_volume.rs diff --git a/README.md b/README.md index 319b7c8..bf338e7 100644 --- a/README.md +++ b/README.md @@ -28,14 +28,15 @@ At the moment, these modes (along with their meta-menu ordering) are hard-coded Modes in **bold** should be considered **EXPERIMENTAL**. They seem to work alright (some of the time), but they would really benefit from some more polish / bug fixes. -| Mode | Click | Rotate | Notes | -| ---------------------------- | ----------------- | -------------------- | ------------------------------------------------------------------------------------- | -| Scroll | - | Scroll | Fakes chunky mouse-wheel scrolling 1 | -| **Scroll (Fake Multitouch)** | Reset Touch Event | Scroll | Fakes smooth two-finger scrolling | -| Zoom | - | Zoom | | -| Volume | Play/Pause | Volume | Double-click to mute | -| Media | Play/Pause | Next/Prev Track | | -| **Paddle Controller** | Space | Left/Right Arrow Key | Like those old [paddle controllers](https://www.google.com/search?q=arkanoid+paddle). | +| Mode | Click | Rotate | Notes | +| ---------------------------- | ----------------- | -------------------- | -------------------------------------------------------------------------------------- | +| Scroll | - | Scroll | Fakes chunky mouse-wheel scrolling 1 | +| **Scroll (Fake Multitouch)** | Reset Touch Event | Scroll | Fakes smooth two-finger scrolling | +| Zoom | - | Zoom | | +| Volume | Mute | Volume | | +| Media | Play/Pause | Next/Prev Track | | +| Media + Volume | Play/Pause | Volume | Double-click = Next Track | +| **Paddle Controller** | Space | Left/Right Arrow Key | Play [arkanoid](https://www.google.com/search?q=arkanoid+paddle) as the devs intended! | 1 At the time of writing, almost all Linux userspace programs don't take advantage of the newer high-resolution scroll wheel events, and only support the older, chunkier scroll wheel events. Check out [this blog post](https://who-t.blogspot.com/2020/04/high-resolution-wheel-scrolling-in.html) for more details. diff --git a/src/controller/controls/media_with_volume.rs b/src/controller/controls/media_with_volume.rs new file mode 100644 index 0000000..2bbaba1 --- /dev/null +++ b/src/controller/controls/media_with_volume.rs @@ -0,0 +1,108 @@ +use std::sync::mpsc; +use std::time::Duration; + +use crate::controller::{ControlMode, ControlModeMeta}; +use crate::dial_device::DialHaptics; +use crate::error::{Error, Result}; +use crate::fake_input; + +use evdev_rs::enums::EV_KEY; + +fn double_click_worker(click: mpsc::Receiver<()>, release: mpsc::Receiver<()>) -> Result<()> { + loop { + // drain any spurious clicks/releases + for _ in click.try_iter() {} + for _ in release.try_iter() {} + + click.recv().unwrap(); + // recv with timeout, in case this is a long-press + match release.recv_timeout(Duration::from_secs(1)) { + Ok(()) => {} + Err(mpsc::RecvTimeoutError::Timeout) => continue, + Err(mpsc::RecvTimeoutError::Disconnected) => panic!(), + } + + match click.recv_timeout(Duration::from_millis(250)) { + Ok(()) => { + // double click + release.recv().unwrap(); // should only fire after button is released + eprintln!("next track"); + fake_input::key_click(&[EV_KEY::KEY_NEXTSONG]).map_err(Error::Evdev)?; + } + Err(mpsc::RecvTimeoutError::Timeout) => { + // single click + eprintln!("play/pause"); + fake_input::key_click(&[EV_KEY::KEY_PLAYPAUSE]).map_err(Error::Evdev)?; + } + Err(mpsc::RecvTimeoutError::Disconnected) => panic!(), + } + } +} + +pub struct MediaWithVolume { + click_tx: mpsc::Sender<()>, + release_tx: mpsc::Sender<()>, + worker_handle: Option>>, +} + +impl MediaWithVolume { + pub fn new() -> MediaWithVolume { + let (click_tx, click_rx) = mpsc::channel(); + let (release_tx, release_rx) = mpsc::channel(); + + let worker_handle = std::thread::spawn(move || double_click_worker(click_rx, release_rx)); + + MediaWithVolume { + click_tx, + release_tx, + worker_handle: Some(worker_handle), + } + } +} + +impl ControlMode for MediaWithVolume { + fn meta(&self) -> ControlModeMeta { + ControlModeMeta { + name: "Media + Volume", + icon: "applications-multimedia", + haptics: true, + steps: 36 * 2, + } + } + + fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> { + if self.click_tx.send(()).is_err() { + self.worker_handle + .take() + .unwrap() + .join() + .expect("panic on thread join")?; + } + Ok(()) + } + + fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> { + if self.release_tx.send(()).is_err() { + self.worker_handle + .take() + .unwrap() + .join() + .expect("panic on thread join")?; + } + Ok(()) + } + + fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { + if delta > 0 { + eprintln!("volume up"); + fake_input::key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP]) + .map_err(Error::Evdev)?; + } else { + eprintln!("volume down"); + fake_input::key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN]) + .map_err(Error::Evdev)?; + } + + Ok(()) + } +} diff --git a/src/controller/controls/mod.rs b/src/controller/controls/mod.rs index c690bf5..7469789 100644 --- a/src/controller/controls/mod.rs +++ b/src/controller/controls/mod.rs @@ -1,4 +1,5 @@ mod media; +mod media_with_volume; mod null; mod paddle; mod scroll; @@ -7,6 +8,7 @@ mod volume; mod zoom; pub use self::media::*; +pub use self::media_with_volume::*; pub use self::null::*; pub use self::paddle::*; pub use self::scroll::*; diff --git a/src/controller/controls/volume.rs b/src/controller/controls/volume.rs index df0be0d..c767721 100644 --- a/src/controller/controls/volume.rs +++ b/src/controller/controls/volume.rs @@ -1,6 +1,3 @@ -use std::sync::mpsc; -use std::time::Duration; - use crate::controller::{ControlMode, ControlModeMeta}; use crate::dial_device::DialHaptics; use crate::error::{Error, Result}; @@ -8,55 +5,11 @@ use crate::fake_input; use evdev_rs::enums::EV_KEY; -fn double_click_worker(click: mpsc::Receiver<()>, release: mpsc::Receiver<()>) -> Result<()> { - loop { - // drain any spurious clicks/releases - for _ in click.try_iter() {} - for _ in release.try_iter() {} - - click.recv().unwrap(); - // recv with timeout, in case this is a long-press - match release.recv_timeout(Duration::from_secs(1)) { - Ok(()) => {} - Err(mpsc::RecvTimeoutError::Timeout) => continue, - Err(mpsc::RecvTimeoutError::Disconnected) => panic!(), - } - - match click.recv_timeout(Duration::from_millis(250)) { - Ok(()) => { - // double click - release.recv().unwrap(); // should only fire after button is released - eprintln!("mute"); - fake_input::key_click(&[EV_KEY::KEY_MUTE]).map_err(Error::Evdev)?; - } - Err(mpsc::RecvTimeoutError::Timeout) => { - // single click - eprintln!("play/pause"); - fake_input::key_click(&[EV_KEY::KEY_PLAYPAUSE]).map_err(Error::Evdev)?; - } - Err(mpsc::RecvTimeoutError::Disconnected) => panic!(), - } - } -} - -pub struct Volume { - click_tx: mpsc::Sender<()>, - release_tx: mpsc::Sender<()>, - worker_handle: Option>>, -} +pub struct Volume {} impl Volume { pub fn new() -> Volume { - let (click_tx, click_rx) = mpsc::channel(); - let (release_tx, release_rx) = mpsc::channel(); - - let worker_handle = std::thread::spawn(move || double_click_worker(click_rx, release_rx)); - - Volume { - click_tx, - release_tx, - worker_handle: Some(worker_handle), - } + Volume {} } } @@ -71,24 +24,12 @@ impl ControlMode for Volume { } fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> { - if self.click_tx.send(()).is_err() { - self.worker_handle - .take() - .unwrap() - .join() - .expect("panic on thread join")?; - } Ok(()) } fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> { - if self.release_tx.send(()).is_err() { - self.worker_handle - .take() - .unwrap() - .join() - .expect("panic on thread join")?; - } + eprintln!("mute"); + fake_input::key_click(&[EV_KEY::KEY_MUTE]).map_err(Error::Evdev)?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 7da541c..ef0ef3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -87,6 +87,7 @@ fn controller_main() -> Result<()> { Box::new(controller::controls::Zoom::new()), Box::new(controller::controls::Volume::new()), Box::new(controller::controls::Media::new()), + Box::new(controller::controls::MediaWithVolume::new()), Box::new(controller::controls::Paddle::new()), ], );