experimental multitouch based smooth scrolling
high-resolution mouse wheels aren't supported in userland yet, so why
don't we fake a high resolution touchpad instead 😄
it's somewhat working, though it seems to crash on startup when
installed (among other weird bugs).
This commit is contained in:
@@ -2,6 +2,7 @@ mod media;
|
||||
mod null;
|
||||
mod paddle;
|
||||
mod scroll;
|
||||
mod scroll_mt;
|
||||
mod volume;
|
||||
mod zoom;
|
||||
|
||||
@@ -9,5 +10,6 @@ pub use self::media::*;
|
||||
pub use self::null::*;
|
||||
pub use self::paddle::*;
|
||||
pub use self::scroll::*;
|
||||
pub use self::scroll_mt::*;
|
||||
pub use self::volume::*;
|
||||
pub use self::zoom::*;
|
||||
|
||||
61
src/controller/controls/scroll_mt.rs
Normal file
61
src/controller/controls/scroll_mt.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use crate::controller::{ControlMode, ControlModeMeta};
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
pub struct ScrollMT {
|
||||
acc_delta: i32,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl ScrollMT {
|
||||
pub fn new() -> ScrollMT {
|
||||
ScrollMT {
|
||||
acc_delta: 0,
|
||||
fake_input: FakeInput::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for ScrollMT {
|
||||
fn meta(&self) -> ControlModeMeta {
|
||||
ControlModeMeta {
|
||||
name: "Scroll",
|
||||
icon: "input-mouse",
|
||||
}
|
||||
}
|
||||
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(3600))?;
|
||||
self.acc_delta = 0;
|
||||
|
||||
self.fake_input.scroll_mt_start()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_end(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
self.fake_input.scroll_mt_end()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// HACK: the button will reset the scroll event, which sometimes helps
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
self.fake_input.scroll_mt_end()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
self.fake_input.scroll_mt_start()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
self.acc_delta += delta;
|
||||
self.fake_input.scroll_mt_step(self.acc_delta)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -2,54 +2,122 @@ use std::io;
|
||||
|
||||
use evdev_rs::enums::*;
|
||||
use evdev_rs::{Device, InputEvent, TimeVal, UInputDevice};
|
||||
use parking_lot::ReentrantMutex;
|
||||
|
||||
static mut FAKE_INPUT: Option<UInputDevice> = None;
|
||||
fn get_fake_input() -> io::Result<&'static UInputDevice> {
|
||||
if unsafe { FAKE_INPUT.is_none() } {
|
||||
let device = Device::new().unwrap();
|
||||
device.set_name("Surface Dial Virtual Input");
|
||||
lazy_static::lazy_static! {
|
||||
static ref FAKE_KEYBOARD: ReentrantMutex<UInputDevice> = {
|
||||
(|| -> io::Result<_> {
|
||||
let device = Device::new().unwrap();
|
||||
device.set_name("Surface Dial Virtual Keyboard/Mouse");
|
||||
|
||||
device.enable(&EventType::EV_SYN)?;
|
||||
device.enable(&EventCode::EV_SYN(EV_SYN::SYN_REPORT))?;
|
||||
device.enable(&EventType::EV_SYN)?;
|
||||
device.enable(&EventCode::EV_SYN(EV_SYN::SYN_REPORT))?;
|
||||
|
||||
device.enable(&EventType::EV_MSC)?;
|
||||
device.enable(&EventCode::EV_MSC(EV_MSC::MSC_SCAN))?;
|
||||
device.enable(&EventType::EV_MSC)?;
|
||||
device.enable(&EventCode::EV_MSC(EV_MSC::MSC_SCAN))?;
|
||||
|
||||
device.enable(&EventType::EV_KEY)?;
|
||||
{
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFTSHIFT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFTCTRL))?;
|
||||
device.enable(&EventType::EV_KEY)?;
|
||||
{
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFTSHIFT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFTCTRL))?;
|
||||
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_MUTE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_VOLUMEDOWN))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_VOLUMEUP))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_NEXTSONG))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_PLAYPAUSE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_PREVIOUSSONG))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_MUTE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_VOLUMEDOWN))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_VOLUMEUP))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_NEXTSONG))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_PLAYPAUSE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_PREVIOUSSONG))?;
|
||||
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_RIGHT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_LEFT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_RIGHT))?;
|
||||
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_SPACE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_EQUAL))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_MINUS))?;
|
||||
}
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_SPACE))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_EQUAL))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::KEY_MINUS))?;
|
||||
}
|
||||
|
||||
device.enable(&EventType::EV_REL)?;
|
||||
{
|
||||
device.enable(&EventCode::EV_REL(EV_REL::REL_WHEEL))?;
|
||||
device.enable(&EventCode::EV_REL(EV_REL::REL_WHEEL_HI_RES))?;
|
||||
}
|
||||
device.enable(&EventType::EV_REL)?;
|
||||
{
|
||||
device.enable(&EventCode::EV_REL(EV_REL::REL_WHEEL))?;
|
||||
device.enable(&EventCode::EV_REL(EV_REL::REL_WHEEL_HI_RES))?;
|
||||
}
|
||||
|
||||
Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?))
|
||||
})().expect("failed to install virtual mouse/keyboard device")
|
||||
};
|
||||
|
||||
static ref FAKE_TOUCHPAD: ReentrantMutex<UInputDevice> = {
|
||||
(|| -> io::Result<_> {
|
||||
let device = Device::new().unwrap();
|
||||
device.set_name("Surface Dial Virtual Touchpad");
|
||||
|
||||
device.enable(&InputProp::INPUT_PROP_BUTTONPAD)?;
|
||||
device.enable(&InputProp::INPUT_PROP_POINTER)?;
|
||||
|
||||
device.enable(&EventType::EV_SYN)?;
|
||||
device.enable(&EventCode::EV_SYN(EV_SYN::SYN_REPORT))?;
|
||||
|
||||
device.enable(&EventType::EV_KEY)?;
|
||||
{
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_LEFT))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_TOOL_FINGER))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_TOUCH))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_TOOL_DOUBLETAP))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_TOOL_TRIPLETAP))?;
|
||||
device.enable(&EventCode::EV_KEY(EV_KEY::BTN_TOOL_QUADTAP))?;
|
||||
}
|
||||
|
||||
// roughly copied from my laptop's trackpad (Aero 15x)
|
||||
device.enable(&EventType::EV_ABS)?;
|
||||
{
|
||||
let mut abs_info = evdev_rs::AbsInfo {
|
||||
value: 0,
|
||||
minimum: 0,
|
||||
maximum: 0,
|
||||
fuzz: 0,
|
||||
flat: 0,
|
||||
resolution: 0,
|
||||
};
|
||||
|
||||
abs_info.minimum = 0;
|
||||
abs_info.maximum = 4;
|
||||
device.enable_event_code(&EventCode::EV_ABS(EV_ABS::ABS_MT_SLOT), Some(&abs_info))?;
|
||||
|
||||
abs_info.minimum = 0;
|
||||
abs_info.maximum = 65535;
|
||||
device.enable_event_code(
|
||||
&EventCode::EV_ABS(EV_ABS::ABS_MT_TRACKING_ID),
|
||||
Some(&abs_info),
|
||||
)?;
|
||||
|
||||
// higher = more sensitive
|
||||
const SENSITIVITY: i32 = 64;
|
||||
|
||||
abs_info.minimum = 0;
|
||||
abs_info.maximum = std::i32::MAX;
|
||||
abs_info.resolution = SENSITIVITY;
|
||||
device.enable_event_code(
|
||||
&EventCode::EV_ABS(EV_ABS::ABS_MT_POSITION_X),
|
||||
Some(&abs_info),
|
||||
)?;
|
||||
|
||||
abs_info.minimum = 0;
|
||||
abs_info.maximum = std::i32::MAX;
|
||||
abs_info.resolution = SENSITIVITY;
|
||||
device.enable_event_code(
|
||||
&EventCode::EV_ABS(EV_ABS::ABS_MT_POSITION_Y),
|
||||
Some(&abs_info),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(ReentrantMutex::new(UInputDevice::create_from_device(&device)?))
|
||||
})().expect("failed to install virtual touchpad device")
|
||||
};
|
||||
|
||||
unsafe { FAKE_INPUT = Some(UInputDevice::create_from_device(&device)?) }
|
||||
}
|
||||
unsafe { Ok(FAKE_INPUT.as_ref().unwrap()) }
|
||||
}
|
||||
|
||||
#[non_exhaustive]
|
||||
pub struct FakeInput {
|
||||
uinput: &'static UInputDevice,
|
||||
}
|
||||
pub struct FakeInput {}
|
||||
|
||||
macro_rules! input_event {
|
||||
($type:ident, $code:ident, $value:expr) => {
|
||||
@@ -70,14 +138,11 @@ impl Default for FakeInput {
|
||||
|
||||
impl FakeInput {
|
||||
pub fn new() -> FakeInput {
|
||||
FakeInput {
|
||||
uinput: get_fake_input().expect("could not install fake input device"),
|
||||
}
|
||||
FakeInput {}
|
||||
}
|
||||
|
||||
fn syn_report(&self) -> io::Result<()> {
|
||||
self.uinput
|
||||
.write_event(&input_event!(EV_SYN, SYN_REPORT, 0))
|
||||
fn kbd_syn_report(&self) -> io::Result<()> {
|
||||
(FAKE_KEYBOARD.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0))
|
||||
}
|
||||
|
||||
pub fn key_click(&self, keys: &[EV_KEY]) -> io::Result<()> {
|
||||
@@ -87,34 +152,40 @@ impl FakeInput {
|
||||
}
|
||||
|
||||
pub fn key_press(&self, keys: &[EV_KEY]) -> io::Result<()> {
|
||||
let keyboard = FAKE_KEYBOARD.lock();
|
||||
|
||||
for key in keys {
|
||||
self.uinput.write_event(&InputEvent {
|
||||
keyboard.write_event(&InputEvent {
|
||||
time: TimeVal::new(0, 0),
|
||||
event_code: EventCode::EV_KEY(*key),
|
||||
event_type: EventType::EV_KEY,
|
||||
value: 1,
|
||||
})?;
|
||||
}
|
||||
self.syn_report()?;
|
||||
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() {
|
||||
self.uinput.write_event(&InputEvent {
|
||||
keyboard.write_event(&InputEvent {
|
||||
time: TimeVal::new(0, 0),
|
||||
event_code: EventCode::EV_KEY(*key),
|
||||
event_type: EventType::EV_KEY,
|
||||
value: 0,
|
||||
})?;
|
||||
}
|
||||
self.syn_report()?;
|
||||
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
|
||||
self.uinput.write_event(&InputEvent {
|
||||
keyboard.write_event(&InputEvent {
|
||||
time: TimeVal::new(0, 0),
|
||||
event_code: EventCode::EV_REL(EV_REL::REL_WHEEL),
|
||||
event_type: EventType::EV_REL,
|
||||
@@ -123,7 +194,7 @@ impl FakeInput {
|
||||
ScrollStep::Up => 1,
|
||||
},
|
||||
})?;
|
||||
self.uinput.write_event(&InputEvent {
|
||||
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,
|
||||
@@ -132,11 +203,89 @@ impl FakeInput {
|
||||
ScrollStep::Up => 120,
|
||||
},
|
||||
})?;
|
||||
self.syn_report()?;
|
||||
self.kbd_syn_report()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn touch_syn_report(&self) -> io::Result<()> {
|
||||
(FAKE_TOUCHPAD.lock()).write_event(&input_event!(EV_SYN, SYN_REPORT, 0))
|
||||
}
|
||||
|
||||
pub fn scroll_mt_start(&self) -> 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_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))?;
|
||||
touchpad.write_event(&input_event!(EV_KEY, BTN_TOOL_FINGER, 1))?;
|
||||
|
||||
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, std::i32::MAX / 3))?;
|
||||
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_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))?;
|
||||
|
||||
self.touch_syn_report()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn scroll_mt_end(&self) -> 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_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))?;
|
||||
|
||||
self.touch_syn_report()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
const MT_BASELINE: i32 = std::i32::MAX / 2;
|
||||
|
||||
pub enum ScrollStep {
|
||||
Up,
|
||||
Down,
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -62,8 +62,8 @@ fn main() {
|
||||
println!("{}", e);
|
||||
}
|
||||
|
||||
kill_notif_tx.send(None).unwrap(); // silently shut down
|
||||
handle.join().unwrap();
|
||||
let _ = kill_notif_tx.send(None); // silently shut down
|
||||
let _ = handle.join();
|
||||
}
|
||||
|
||||
fn true_main(kill_notif_tx: mpsc::Sender<Option<(String, &'static str)>>) -> DynResult<()> {
|
||||
@@ -78,9 +78,10 @@ fn true_main(kill_notif_tx: mpsc::Sender<Option<(String, &'static str)>>) -> Dyn
|
||||
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
|
||||
for sig in signals.forever() {
|
||||
eprintln!("received signal {:?}", sig);
|
||||
kill_notif_tx
|
||||
.send(Some(("Terminated!".into(), "dialog-warning")))
|
||||
.unwrap();
|
||||
match kill_notif_tx.send(Some(("Terminated!".into(), "dialog-warning"))) {
|
||||
Ok(_) => {}
|
||||
Err(_) => std::process::exit(1),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,6 +89,7 @@ fn true_main(kill_notif_tx: mpsc::Sender<Option<(String, &'static str)>>) -> Dyn
|
||||
dial,
|
||||
cfg.last_mode,
|
||||
vec![
|
||||
// Box::new(controller::controls::ScrollMT::new()),
|
||||
Box::new(controller::controls::Scroll::new()),
|
||||
Box::new(controller::controls::Zoom::new()),
|
||||
Box::new(controller::controls::Volume::new()),
|
||||
|
||||
Reference in New Issue
Block a user