1
0

Separate "vanilla" Volume mode from Media + Volume mode

This commit is contained in:
Daniel Prilik
2020-11-07 11:45:26 -05:00
parent ae58e16610
commit 066c635bd7
5 changed files with 124 additions and 71 deletions

View File

@@ -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 <sup>1</sup> |
| **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 <sup>1</sup> |
| **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! |
<sup>1</sup> 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.

View File

@@ -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<std::thread::JoinHandle<Result<()>>>,
}
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(())
}
}

View File

@@ -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::*;

View File

@@ -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<std::thread::JoinHandle<Result<()>>>,
}
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(())
}

View File

@@ -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()),
],
);