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:
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user