1
0

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:
Daniel Prilik
2020-11-02 18:50:45 -05:00
parent 3285021903
commit 4f25a73eb3
7 changed files with 4905 additions and 57 deletions

View File

@@ -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::*;

View 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(())
}
}

View File

@@ -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,

View File

@@ -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()),