Paddle tweaks
disable recv_timeout when the mode isn't active + move all fakeinput stuff into the worker.
This commit is contained in:
180
src/controller/controls/paddle.rs
Normal file
180
src/controller/controls/paddle.rs
Normal file
@@ -0,0 +1,180 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::sync::mpsc;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::controller::{ControlMode, ControlModeMeta};
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
// everything is done in a worker, as we need use `recv_timeout` as a (very)
|
||||
// poor man's `select!`.
|
||||
|
||||
enum Msg {
|
||||
Kill,
|
||||
ButtonDown,
|
||||
ButtonUp,
|
||||
Delta(i32),
|
||||
Enabled(bool),
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
msg: mpsc::Receiver<Msg>,
|
||||
fake_input: FakeInput,
|
||||
|
||||
timeout: u64,
|
||||
falloff: i32,
|
||||
cap: i32,
|
||||
deadzone: i32,
|
||||
|
||||
enabled: bool,
|
||||
last_delta: i32,
|
||||
velocity: i32,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
pub fn new(msg: mpsc::Receiver<Msg>) -> Worker {
|
||||
Worker {
|
||||
msg,
|
||||
fake_input: FakeInput::new(),
|
||||
|
||||
// tweak these for "feel"
|
||||
timeout: 5,
|
||||
falloff: 10,
|
||||
cap: 250,
|
||||
deadzone: 10,
|
||||
|
||||
enabled: false,
|
||||
last_delta: 0,
|
||||
velocity: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
let falloff = self.velocity.abs() / self.falloff + 1;
|
||||
|
||||
let msg = if self.enabled {
|
||||
self.msg.recv_timeout(Duration::from_millis(self.timeout))
|
||||
} else {
|
||||
self.msg
|
||||
.recv()
|
||||
.map_err(|_| mpsc::RecvTimeoutError::Disconnected)
|
||||
};
|
||||
|
||||
match msg {
|
||||
Ok(Msg::Kill) => return,
|
||||
Ok(Msg::Enabled(enabled)) => {
|
||||
self.enabled = enabled;
|
||||
if !enabled {
|
||||
self.fake_input
|
||||
.key_release(&[EV_KEY::KEY_SPACE, EV_KEY::KEY_LEFT, EV_KEY::KEY_RIGHT])
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
Ok(Msg::ButtonDown) => self.fake_input.key_press(&[EV_KEY::KEY_SPACE]).unwrap(),
|
||||
Ok(Msg::ButtonUp) => self.fake_input.key_release(&[EV_KEY::KEY_SPACE]).unwrap(),
|
||||
Ok(Msg::Delta(delta)) => {
|
||||
// abrupt direction change!
|
||||
if (delta < 0) != (self.last_delta < 0) {
|
||||
self.velocity = 0
|
||||
}
|
||||
self.last_delta = delta;
|
||||
|
||||
self.velocity += delta
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => match self.velocity.cmp(&0) {
|
||||
Ordering::Equal => {}
|
||||
Ordering::Less => self.velocity += falloff,
|
||||
Ordering::Greater => self.velocity -= falloff,
|
||||
},
|
||||
Err(other) => panic!("{}", other),
|
||||
}
|
||||
|
||||
// clamp velocity within the cap bounds
|
||||
if self.velocity > self.cap {
|
||||
self.velocity = self.cap;
|
||||
} else if self.velocity < -self.cap {
|
||||
self.velocity = -self.cap;
|
||||
}
|
||||
|
||||
if self.velocity.abs() < self.deadzone {
|
||||
self.fake_input
|
||||
.key_release(&[EV_KEY::KEY_LEFT, EV_KEY::KEY_RIGHT])
|
||||
.unwrap();
|
||||
continue;
|
||||
}
|
||||
|
||||
match self.velocity.cmp(&0) {
|
||||
Ordering::Equal => {}
|
||||
Ordering::Less => self.fake_input.key_press(&[EV_KEY::KEY_LEFT]).unwrap(),
|
||||
Ordering::Greater => self.fake_input.key_press(&[EV_KEY::KEY_RIGHT]).unwrap(),
|
||||
}
|
||||
|
||||
// eprintln!("{:?}", self.velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A bit of a misnomer, since it's only left-right.
|
||||
pub struct Paddle {
|
||||
_worker: JoinHandle<()>,
|
||||
msg: mpsc::Sender<Msg>,
|
||||
}
|
||||
|
||||
impl Drop for Paddle {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.msg.send(Msg::Kill);
|
||||
}
|
||||
}
|
||||
|
||||
impl Paddle {
|
||||
pub fn new() -> Paddle {
|
||||
let (msg_tx, msg_rx) = mpsc::channel();
|
||||
|
||||
let worker = std::thread::spawn(move || Worker::new(msg_rx).run());
|
||||
|
||||
Paddle {
|
||||
_worker: worker,
|
||||
msg: msg_tx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Paddle {
|
||||
fn meta(&self) -> ControlModeMeta {
|
||||
ControlModeMeta {
|
||||
name: "Paddle",
|
||||
icon: "input-gaming",
|
||||
}
|
||||
}
|
||||
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(3600))?;
|
||||
self.msg.send(Msg::Enabled(true))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_end(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
self.msg.send(Msg::Enabled(false))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
self.msg.send(Msg::ButtonDown)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
self.msg.send(Msg::ButtonUp)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
self.msg.send(Msg::Delta(delta))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user