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. 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 | | Mode | Click | Rotate | Notes |
| ---------------------------- | ----------------- | -------------------- | ------------------------------------------------------------------------------------- | | ---------------------------- | ----------------- | -------------------- | -------------------------------------------------------------------------------------- |
| Scroll | - | Scroll | Fakes chunky mouse-wheel scrolling <sup>1</sup> | | Scroll | - | Scroll | Fakes chunky mouse-wheel scrolling <sup>1</sup> |
| **Scroll (Fake Multitouch)** | Reset Touch Event | Scroll | Fakes smooth two-finger scrolling | | **Scroll (Fake Multitouch)** | Reset Touch Event | Scroll | Fakes smooth two-finger scrolling |
| Zoom | - | Zoom | | | Zoom | - | Zoom | |
| Volume | Play/Pause | Volume | Double-click to mute | | Volume | Mute | Volume | |
| Media | Play/Pause | Next/Prev Track | | | 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). | | 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. <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;
mod media_with_volume;
mod null; mod null;
mod paddle; mod paddle;
mod scroll; mod scroll;
@@ -7,6 +8,7 @@ mod volume;
mod zoom; mod zoom;
pub use self::media::*; pub use self::media::*;
pub use self::media_with_volume::*;
pub use self::null::*; pub use self::null::*;
pub use self::paddle::*; pub use self::paddle::*;
pub use self::scroll::*; 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::controller::{ControlMode, ControlModeMeta};
use crate::dial_device::DialHaptics; use crate::dial_device::DialHaptics;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
@@ -8,55 +5,11 @@ use crate::fake_input;
use evdev_rs::enums::EV_KEY; use evdev_rs::enums::EV_KEY;
fn double_click_worker(click: mpsc::Receiver<()>, release: mpsc::Receiver<()>) -> Result<()> { pub struct Volume {}
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<()>>>,
}
impl Volume { impl Volume {
pub fn new() -> Volume { pub fn new() -> Volume {
let (click_tx, click_rx) = mpsc::channel(); Volume {}
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),
}
} }
} }
@@ -71,24 +24,12 @@ impl ControlMode for Volume {
} }
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> { 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(()) Ok(())
} }
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> { fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
if self.release_tx.send(()).is_err() { eprintln!("mute");
self.worker_handle fake_input::key_click(&[EV_KEY::KEY_MUTE]).map_err(Error::Evdev)?;
.take()
.unwrap()
.join()
.expect("panic on thread join")?;
}
Ok(()) Ok(())
} }

View File

@@ -87,6 +87,7 @@ fn controller_main() -> Result<()> {
Box::new(controller::controls::Zoom::new()), Box::new(controller::controls::Zoom::new()),
Box::new(controller::controls::Volume::new()), Box::new(controller::controls::Volume::new()),
Box::new(controller::controls::Media::new()), Box::new(controller::controls::Media::new()),
Box::new(controller::controls::MediaWithVolume::new()),
Box::new(controller::controls::Paddle::new()), Box::new(controller::controls::Paddle::new()),
], ],
); );