1
0

refactor fakeinput + invert main thread function

- fakeinput is now just a series of free functions that access the global
  static, which makes more sense than having some arbitrary
  zero-sized-struct with associated methods on it.
- instead of having the active notification run in its own thread, it
  now runs on the main thread, and the controller runs in a separate
  thread. This results in midly nicer code, at least IMO.

Also, I've had to disable optimizations on the evdev_rs crate, as it
seems to be segfaulting on release builds. Very spooky, but it's not
something I'm super interested in debugging right now, so I'll just work
around it. It's not like I need the _blazing fast performance_ from a
Rust wrapper around a C library anyways...
This commit is contained in:
Daniel Prilik
2020-11-05 10:26:11 -05:00
parent 905c33ad4a
commit fed2060872
11 changed files with 240 additions and 270 deletions

View File

@@ -13,3 +13,13 @@ lazy_static = "1.4"
notify-rust = "4" notify-rust = "4"
parking_lot = "0.11.0" parking_lot = "0.11.0"
signal-hook = "0.1.16" signal-hook = "0.1.16"
# HACK: Using >1 virtual uinput devices will segfault in release builds.
#
# While spooky, this isn't a show-stopper for us Pragmatic Programmers™, as we
# can simply disable optimizations for `evdev-rs` and have things work Okay™.
#
# That said, I would like to find some time to narrow down why this is happening
# and fix it. Maybe later...
[profile.release.package.evdev-rs]
opt-level = 0

View File

@@ -33,8 +33,10 @@ It would be cool to create some sort of GUI overlay (similar to the Windows one)
- Operating Modes - Operating Modes
- [x] Volume Controls - [x] Volume Controls
- [x] Media Controls - [x] Media Controls
- [x] D-Pad (emulated left, right, and space key) - [x] Scrolling - using a virtual mouse-wheel
- [x] Scrolling / Zooming - [x] Scrolling - using a virtual touchpad (for [smoother scrolling](https://who-t.blogspot.com/2020/04/high-resolution-wheel-scrolling-in.html))
- [x] Zooming
- [x] [Paddle](https://www.google.com/search?q=arkanoid+paddle) (emulated left, right, and space key)
- [ ] \(meta\) custom modes specified via config file(s) - [ ] \(meta\) custom modes specified via config file(s)
- [x] Dynamically switch between operating modes - [x] Dynamically switch between operating modes
- [x] Using some-sort of on-device mechanism (e.g: long-press) - [x] Using some-sort of on-device mechanism (e.g: long-press)

View File

@@ -1,19 +1,15 @@
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};
use crate::fake_input::FakeInput; use crate::fake_input;
use evdev_rs::enums::EV_KEY; use evdev_rs::enums::EV_KEY;
pub struct Media { pub struct Media {}
fake_input: FakeInput,
}
impl Media { impl Media {
pub fn new() -> Media { pub fn new() -> Media {
Media { Media {}
fake_input: FakeInput::new(),
}
} }
} }
@@ -35,23 +31,17 @@ impl ControlMode for Media {
} }
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> { fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
self.fake_input fake_input::key_click(&[EV_KEY::KEY_PLAYPAUSE]).map_err(Error::Evdev)?;
.key_click(&[EV_KEY::KEY_PLAYPAUSE])
.map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 { if delta > 0 {
eprintln!("next song"); eprintln!("next song");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_NEXTSONG]).map_err(Error::Evdev)?;
.key_click(&[EV_KEY::KEY_NEXTSONG])
.map_err(Error::Evdev)?;
} else { } else {
eprintln!("last song"); eprintln!("last song");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_PREVIOUSSONG]).map_err(Error::Evdev)?;
.key_click(&[EV_KEY::KEY_PREVIOUSSONG])
.map_err(Error::Evdev)?;
} }
Ok(()) Ok(())
} }

View File

@@ -6,7 +6,7 @@ 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::Result; use crate::error::Result;
use crate::fake_input::FakeInput; use crate::fake_input;
use evdev_rs::enums::EV_KEY; use evdev_rs::enums::EV_KEY;
@@ -23,7 +23,6 @@ enum Msg {
struct Worker { struct Worker {
msg: mpsc::Receiver<Msg>, msg: mpsc::Receiver<Msg>,
fake_input: FakeInput,
timeout: u64, timeout: u64,
falloff: i32, falloff: i32,
@@ -39,7 +38,6 @@ impl Worker {
pub fn new(msg: mpsc::Receiver<Msg>) -> Worker { pub fn new(msg: mpsc::Receiver<Msg>) -> Worker {
Worker { Worker {
msg, msg,
fake_input: FakeInput::new(),
// tweak these for "feel" // tweak these for "feel"
timeout: 5, timeout: 5,
@@ -70,13 +68,16 @@ impl Worker {
Ok(Msg::Enabled(enabled)) => { Ok(Msg::Enabled(enabled)) => {
self.enabled = enabled; self.enabled = enabled;
if !enabled { if !enabled {
self.fake_input fake_input::key_release(&[
.key_release(&[EV_KEY::KEY_SPACE, EV_KEY::KEY_LEFT, EV_KEY::KEY_RIGHT]) EV_KEY::KEY_SPACE,
.unwrap() EV_KEY::KEY_LEFT,
EV_KEY::KEY_RIGHT,
])
.unwrap()
} }
} }
Ok(Msg::ButtonDown) => self.fake_input.key_press(&[EV_KEY::KEY_SPACE]).unwrap(), Ok(Msg::ButtonDown) => fake_input::key_press(&[EV_KEY::KEY_SPACE]).unwrap(),
Ok(Msg::ButtonUp) => self.fake_input.key_release(&[EV_KEY::KEY_SPACE]).unwrap(), Ok(Msg::ButtonUp) => fake_input::key_release(&[EV_KEY::KEY_SPACE]).unwrap(),
Ok(Msg::Delta(delta)) => { Ok(Msg::Delta(delta)) => {
// abrupt direction change! // abrupt direction change!
if (delta < 0) != (self.last_delta < 0) { if (delta < 0) != (self.last_delta < 0) {
@@ -102,16 +103,14 @@ impl Worker {
} }
if self.velocity.abs() < self.deadzone { if self.velocity.abs() < self.deadzone {
self.fake_input fake_input::key_release(&[EV_KEY::KEY_LEFT, EV_KEY::KEY_RIGHT]).unwrap();
.key_release(&[EV_KEY::KEY_LEFT, EV_KEY::KEY_RIGHT])
.unwrap();
continue; continue;
} }
match self.velocity.cmp(&0) { match self.velocity.cmp(&0) {
Ordering::Equal => {} Ordering::Equal => {}
Ordering::Less => self.fake_input.key_press(&[EV_KEY::KEY_LEFT]).unwrap(), Ordering::Less => fake_input::key_press(&[EV_KEY::KEY_LEFT]).unwrap(),
Ordering::Greater => self.fake_input.key_press(&[EV_KEY::KEY_RIGHT]).unwrap(), Ordering::Greater => fake_input::key_press(&[EV_KEY::KEY_RIGHT]).unwrap(),
} }
// eprintln!("{:?}", self.velocity); // eprintln!("{:?}", self.velocity);

View File

@@ -1,17 +1,13 @@
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};
use crate::fake_input::{FakeInput, ScrollStep}; use crate::fake_input::{self, ScrollStep};
pub struct Scroll { pub struct Scroll {}
fake_input: FakeInput,
}
impl Scroll { impl Scroll {
pub fn new() -> Scroll { pub fn new() -> Scroll {
Scroll { Scroll {}
fake_input: FakeInput::new(),
}
} }
} }
@@ -39,14 +35,10 @@ impl ControlMode for Scroll {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 { if delta > 0 {
eprintln!("scroll down"); eprintln!("scroll down");
self.fake_input fake_input::scroll_step(ScrollStep::Down).map_err(Error::Evdev)?;
.scroll_step(ScrollStep::Down)
.map_err(Error::Evdev)?;
} else { } else {
eprintln!("scroll up"); eprintln!("scroll up");
self.fake_input fake_input::scroll_step(ScrollStep::Up).map_err(Error::Evdev)?;
.scroll_step(ScrollStep::Up)
.map_err(Error::Evdev)?;
} }
Ok(()) Ok(())

View File

@@ -1,20 +1,15 @@
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};
use crate::fake_input::FakeInput; use crate::fake_input;
pub struct ScrollMT { pub struct ScrollMT {
acc_delta: i32, acc_delta: i32,
fake_input: FakeInput,
} }
impl ScrollMT { impl ScrollMT {
pub fn new() -> ScrollMT { pub fn new() -> ScrollMT {
ScrollMT { ScrollMT { acc_delta: 0 }
acc_delta: 0,
fake_input: FakeInput::new(),
}
} }
} }
@@ -30,33 +25,31 @@ impl ControlMode for ScrollMT {
haptics.set_mode(false, Some(3600))?; haptics.set_mode(false, Some(3600))?;
self.acc_delta = 0; self.acc_delta = 0;
self.fake_input.scroll_mt_start().map_err(Error::Evdev)?; fake_input::scroll_mt_start().map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> { fn on_end(&mut self, _haptics: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_end().map_err(Error::Evdev)?; fake_input::scroll_mt_end().map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
// HACK: the button will reset the scroll event, which sometimes helps // HACK: the button will reset the scroll event, which sometimes helps
fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> { fn on_btn_press(&mut self, _: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_end().map_err(Error::Evdev)?; fake_input::scroll_mt_end().map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> { fn on_btn_release(&mut self, _haptics: &DialHaptics) -> Result<()> {
self.fake_input.scroll_mt_start().map_err(Error::Evdev)?; fake_input::scroll_mt_start().map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
self.acc_delta += delta; self.acc_delta += delta;
self.fake_input fake_input::scroll_mt_step(self.acc_delta).map_err(Error::Evdev)?;
.scroll_mt_step(self.acc_delta)
.map_err(Error::Evdev)?;
Ok(()) Ok(())
} }

View File

@@ -1,19 +1,15 @@
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};
use crate::fake_input::FakeInput; use crate::fake_input;
use evdev_rs::enums::EV_KEY; use evdev_rs::enums::EV_KEY;
pub struct Volume { pub struct Volume {}
fake_input: FakeInput,
}
impl Volume { impl Volume {
pub fn new() -> Volume { pub fn new() -> Volume {
Volume { Volume {}
fake_input: FakeInput::new(),
}
} }
} }
@@ -37,23 +33,19 @@ impl ControlMode for Volume {
fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> { fn on_btn_release(&mut self, _: &DialHaptics) -> Result<()> {
eprintln!("play/pause"); eprintln!("play/pause");
// self.fake_input.mute()? // fake_input::mute()?
self.fake_input fake_input::key_click(&[EV_KEY::KEY_PLAYPAUSE]).map_err(Error::Evdev)?;
.key_click(&[EV_KEY::KEY_PLAYPAUSE])
.map_err(Error::Evdev)?;
Ok(()) Ok(())
} }
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 { if delta > 0 {
eprintln!("volume up"); eprintln!("volume up");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])
.map_err(Error::Evdev)?; .map_err(Error::Evdev)?;
} else { } else {
eprintln!("volume down"); eprintln!("volume down");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])
.map_err(Error::Evdev)?; .map_err(Error::Evdev)?;
} }

View File

@@ -1,19 +1,15 @@
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};
use crate::fake_input::FakeInput; use crate::fake_input;
use evdev_rs::enums::EV_KEY; use evdev_rs::enums::EV_KEY;
pub struct Zoom { pub struct Zoom {}
fake_input: FakeInput,
}
impl Zoom { impl Zoom {
pub fn new() -> Zoom { pub fn new() -> Zoom {
Zoom { Zoom {}
fake_input: FakeInput::new(),
}
} }
} }
@@ -41,13 +37,11 @@ impl ControlMode for Zoom {
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> { fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> Result<()> {
if delta > 0 { if delta > 0 {
eprintln!("zoom in"); eprintln!("zoom in");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])
.map_err(Error::Evdev)?; .map_err(Error::Evdev)?;
} else { } else {
eprintln!("zoom out"); eprintln!("zoom out");
self.fake_input fake_input::key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_MINUS])
.map_err(Error::Evdev)?; .map_err(Error::Evdev)?;
} }

View File

@@ -16,6 +16,7 @@ pub enum Error {
UnexpectedEvt(InputEvent), UnexpectedEvt(InputEvent),
Evdev(io::Error), Evdev(io::Error),
Notif(notify_rust::error::Error), Notif(notify_rust::error::Error),
TermSig,
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@@ -30,6 +31,7 @@ impl fmt::Display for Error {
Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt), Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt),
Error::Evdev(e) => write!(f, "Evdev error: {}", e), Error::Evdev(e) => write!(f, "Evdev error: {}", e),
Error::Notif(e) => write!(f, "Notification error: {}", e), Error::Notif(e) => write!(f, "Notification error: {}", e),
Error::TermSig => write!(f, "Received termination signal (either SIGTERM or SIGINT)"),
} }
} }
} }

View File

@@ -5,13 +5,18 @@ use evdev_rs::{Device, InputEvent, TimeVal, UInputDevice};
use parking_lot::ReentrantMutex; use parking_lot::ReentrantMutex;
// this should be a fairly high number, as the axis is from 0..(MT_BASELINE*2) // this should be a fairly high number, as the axis is from 0..(MT_BASELINE*2)
const MT_BASELINE: i32 = std::i32::MAX / 4; const MT_BASELINE: i32 = std::i32::MAX / 8;
// higher = more sensitive // higher = more sensitive
const MT_SENSITIVITY: i32 = 64; const MT_SENSITIVITY: i32 = 64;
pub struct FakeInputs {
keyboard: ReentrantMutex<UInputDevice>,
touchpad: ReentrantMutex<UInputDevice>,
}
lazy_static::lazy_static! { lazy_static::lazy_static! {
static ref FAKE_KEYBOARD: ReentrantMutex<UInputDevice> = { pub static ref FAKE_INPUTS: FakeInputs = {
(|| -> io::Result<_> { let keyboard = (|| -> io::Result<_> {
let device = Device::new().unwrap(); let device = Device::new().unwrap();
device.set_name("Surface Dial Virtual Keyboard/Mouse"); device.set_name("Surface Dial Virtual Keyboard/Mouse");
@@ -48,11 +53,9 @@ lazy_static::lazy_static! {
} }
Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?)) Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?))
})().expect("failed to install virtual mouse/keyboard device") })().expect("failed to install virtual mouse/keyboard device");
};
static ref FAKE_TOUCHPAD: ReentrantMutex<UInputDevice> = { let touchpad = (|| -> io::Result<_> {
(|| -> io::Result<_> {
let device = Device::new().unwrap(); let device = Device::new().unwrap();
device.set_name("Surface Dial Virtual Touchpad"); device.set_name("Surface Dial Virtual Touchpad");
@@ -115,14 +118,15 @@ lazy_static::lazy_static! {
} }
Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?)) Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?))
})().expect("failed to install virtual touchpad device") })().expect("failed to install virtual touchpad device");
FakeInputs {
keyboard,
touchpad
}
}; };
} }
#[non_exhaustive]
pub struct FakeInput {}
macro_rules! input_event { macro_rules! input_event {
($type:ident, $code:ident, $value:expr) => { ($type:ident, $code:ident, $value:expr) => {
InputEvent { InputEvent {
@@ -134,158 +138,146 @@ macro_rules! input_event {
}; };
} }
impl Default for FakeInput { fn kbd_syn_report() -> io::Result<()> {
fn default() -> Self { (FAKE_INPUTS.keyboard.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0))
Self::new()
}
} }
impl FakeInput { pub fn key_click(keys: &[EV_KEY]) -> io::Result<()> {
pub fn new() -> FakeInput { key_press(keys)?;
FakeInput {} key_release(keys)?;
} Ok(())
}
fn kbd_syn_report(&self) -> io::Result<()> { pub fn key_press(keys: &[EV_KEY]) -> io::Result<()> {
(FAKE_KEYBOARD.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0)) let keyboard = FAKE_INPUTS.keyboard.lock();
}
pub fn key_click(&self, keys: &[EV_KEY]) -> io::Result<()> { for key in keys {
self.key_press(keys)?;
self.key_release(keys)?;
Ok(())
}
pub fn key_press(&self, keys: &[EV_KEY]) -> io::Result<()> {
let keyboard = FAKE_KEYBOARD.lock();
for key in keys {
keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0),
event_code: EventCode::EV_KEY(*key),
event_type: EventType::EV_KEY,
value: 1,
})?;
}
self.kbd_syn_report()?;
Ok(())
}
pub fn key_release(&self, keys: &[EV_KEY]) -> io::Result<()> {
let keyboard = FAKE_KEYBOARD.lock();
for key in keys.iter().clone() {
keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0),
event_code: EventCode::EV_KEY(*key),
event_type: EventType::EV_KEY,
value: 0,
})?;
}
self.kbd_syn_report()?;
Ok(())
}
pub fn scroll_step(&self, dir: ScrollStep) -> io::Result<()> {
let keyboard = FAKE_KEYBOARD.lock();
// copied from my razer blackwidow chroma mouse
keyboard.write_event(&InputEvent { keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0), time: TimeVal::new(0, 0),
event_code: EventCode::EV_REL(EV_REL::REL_WHEEL), event_code: EventCode::EV_KEY(*key),
event_type: EventType::EV_REL, event_type: EventType::EV_KEY,
value: match dir { value: 1,
ScrollStep::Down => -1,
ScrollStep::Up => 1,
},
})?; })?;
}
kbd_syn_report()?;
Ok(())
}
pub fn key_release(keys: &[EV_KEY]) -> io::Result<()> {
let keyboard = FAKE_INPUTS.keyboard.lock();
for key in keys.iter().clone() {
keyboard.write_event(&InputEvent { keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0), time: TimeVal::new(0, 0),
event_code: EventCode::EV_REL(EV_REL::REL_WHEEL_HI_RES), event_code: EventCode::EV_KEY(*key),
event_type: EventType::EV_REL, event_type: EventType::EV_KEY,
value: match dir { value: 0,
ScrollStep::Down => -120,
ScrollStep::Up => 120,
},
})?; })?;
self.kbd_syn_report()?;
Ok(())
} }
kbd_syn_report()?;
Ok(())
}
fn touch_syn_report(&self) -> io::Result<()> { pub fn scroll_step(dir: ScrollStep) -> io::Result<()> {
(FAKE_TOUCHPAD.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0)) let keyboard = FAKE_INPUTS.keyboard.lock();
}
pub fn scroll_mt_start(&self) -> io::Result<()> { // copied from my razer blackwidow chroma mouse
let touchpad = FAKE_TOUCHPAD.lock(); keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0),
event_code: EventCode::EV_REL(EV_REL::REL_WHEEL),
event_type: EventType::EV_REL,
value: match dir {
ScrollStep::Down => -1,
ScrollStep::Up => 1,
},
})?;
keyboard.write_event(&InputEvent {
time: TimeVal::new(0, 0),
event_code: EventCode::EV_REL(EV_REL::REL_WHEEL_HI_RES),
event_type: EventType::EV_REL,
value: match dir {
ScrollStep::Down => -120,
ScrollStep::Up => 120,
},
})?;
kbd_syn_report()?;
Ok(())
}
{ fn touch_syn_report() -> io::Result<()> {
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?; (FAKE_INPUTS.touchpad.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0))
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, 1))?; }
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_X, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_Y, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOUCH, 1))?; pub fn scroll_mt_start() -> io::Result<()> {
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_FINGER, 1))?; let touchpad = FAKE_INPUTS.touchpad.lock();
touchpad.write_event(&input_event!(EV_ABS, ABS_X, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_Y, MT_BASELINE))?;
}
self.touch_syn_report()?;
{
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, 2))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_X, MT_BASELINE / 2))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_Y, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_FINGER, 0))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_DOUBLETAP, 1))?;
}
self.touch_syn_report()?;
Ok(())
}
pub fn scroll_mt_step(&self, delta: i32) -> io::Result<()> {
let touchpad = FAKE_TOUCHPAD.lock();
{
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?; touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?;
touchpad.write_event(&input_event!( touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, 1))?;
EV_ABS, touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_X, MT_BASELINE))?;
ABS_MT_POSITION_Y, touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_Y, MT_BASELINE))?;
MT_BASELINE + delta
))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?;
touchpad.write_event(&input_event!(
EV_ABS,
ABS_MT_POSITION_Y,
MT_BASELINE + delta
))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_Y, MT_BASELINE + delta))?; touchpad.write_event(&input_event!(EV_KEY, BTN_TOUCH, 1))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_FINGER, 1))?;
self.touch_syn_report()?; touchpad.write_event(&input_event!(EV_ABS, ABS_X, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_Y, MT_BASELINE))?;
Ok(())
} }
pub fn scroll_mt_end(&self) -> io::Result<()> { touch_syn_report()?;
let touchpad = FAKE_TOUCHPAD.lock();
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?; {
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, -1))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?; touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, -1))?; touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, 2))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_X, MT_BASELINE / 2))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_POSITION_Y, MT_BASELINE))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOUCH, 0))?; touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_FINGER, 0))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_DOUBLETAP, 0))?; touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_DOUBLETAP, 1))?;
self.touch_syn_report()?;
Ok(())
} }
touch_syn_report()?;
Ok(())
}
pub fn scroll_mt_step(delta: i32) -> io::Result<()> {
let touchpad = FAKE_INPUTS.touchpad.lock();
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?;
touchpad.write_event(&input_event!(
EV_ABS,
ABS_MT_POSITION_Y,
MT_BASELINE + delta
))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?;
touchpad.write_event(&input_event!(
EV_ABS,
ABS_MT_POSITION_Y,
MT_BASELINE + delta
))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_Y, MT_BASELINE + delta))?;
touch_syn_report()?;
Ok(())
}
pub fn scroll_mt_end() -> io::Result<()> {
let touchpad = FAKE_INPUTS.touchpad.lock();
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 0))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, -1))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_SLOT, 1))?;
touchpad.write_event(&input_event!(EV_ABS, ABS_MT_TRACKING_ID, -1))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOUCH, 0))?;
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_DOUBLETAP, 0))?;
touch_syn_report()?;
Ok(())
} }
pub enum ScrollStep { pub enum ScrollStep {

View File

@@ -11,38 +11,62 @@ use std::sync::mpsc;
use crate::controller::DialController; use crate::controller::DialController;
use crate::dial_device::DialDevice; use crate::dial_device::DialDevice;
use crate::error::Result; use crate::error::{Error, Result};
use notify_rust::{Hint, Notification, Timeout}; use notify_rust::{Hint, Notification, Timeout};
use signal_hook::{iterator::Signals, SIGINT, SIGTERM}; use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
fn main() { fn main() {
let (kill_notif_tx, kill_notif_rx) = mpsc::channel::<Option<(String, &'static str)>>(); let (terminate_tx, terminate_rx) = mpsc::channel::<Result<()>>();
let handle = std::thread::spawn(move || { std::thread::spawn({
let active_notification = Notification::new() let terminate_tx = terminate_tx.clone();
.hint(Hint::Resident(true)) move || {
.hint(Hint::Category("device".into())) let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
.timeout(Timeout::Never) for sig in signals.forever() {
.summary("Surface Dial") eprintln!("received signal {:?}", sig);
.body("Active!") let _ = terminate_tx.send(Err(Error::TermSig));
.icon("media-optical") // it should be vaguely circular :P
.show()
.expect("failed to send notification");
let kill_notif = kill_notif_rx.recv();
active_notification.close();
let (msg, icon) = match kill_notif {
Ok(None) => {
// shutdown immediately
std::process::exit(1);
} }
Ok(Some((msg, icon))) => (msg, icon), }
Err(_) => ("Unexpected Error".into(), "dialog-error"), });
};
std::thread::spawn({
let terminate_tx = terminate_tx;
move || {
let _ = terminate_tx.send(controller_main());
}
});
let active_notification = Notification::new()
.hint(Hint::Resident(true))
.hint(Hint::Category("device".into()))
.timeout(Timeout::Never)
.summary("Surface Dial")
.body("Active!")
.icon("media-optical") // it should be vaguely circular :P
.show()
.expect("Failed to send notification. NOTE: this daemon (probably) can't run as root!");
let (silent, msg, icon) = match terminate_rx.recv() {
Ok(Ok(())) => (true, "".into(), ""),
Ok(Err(e)) => {
println!("Error: {}", e);
match e {
Error::TermSig => (false, "Terminated!".into(), "dialog-warning"),
// HACK: silently exit if the dial disconnects
Error::Evdev(e) if e.raw_os_error() == Some(19) => (true, "".into(), ""),
other => (false, format!("Error: {}", other), "dialog-error"),
}
}
Err(_) => {
println!("Error: Unexpected Error");
(false, "Unexpected Error".into(), "dialog-error")
}
};
active_notification.close();
if !silent {
Notification::new() Notification::new()
.hint(Hint::Transient(true)) .hint(Hint::Transient(true))
.hint(Hint::Category("device".into())) .hint(Hint::Category("device".into()))
@@ -52,33 +76,13 @@ fn main() {
.icon(icon) .icon(icon)
.show() .show()
.unwrap(); .unwrap();
std::process::exit(1);
});
std::thread::spawn({
let kill_notif_tx = kill_notif_tx.clone();
move || {
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
for sig in signals.forever() {
eprintln!("received signal {:?}", sig);
match kill_notif_tx.send(Some(("Terminated!".into(), "dialog-warning"))) {
Ok(_) => {}
Err(_) => std::process::exit(1),
}
}
}
});
if let Err(e) = true_main() {
println!("{}", e);
} }
let _ = kill_notif_tx.send(None); // silently shut down // cleaning up threads is hard...
let _ = handle.join(); std::process::exit(1);
} }
fn true_main() -> Result<()> { fn controller_main() -> Result<()> {
println!("Started"); println!("Started");
let cfg = config::Config::from_disk()?; let cfg = config::Config::from_disk()?;