Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: visualization of current input speed #25

Merged
merged 5 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ install: default
@sudo chown -v root:root $(MODULEDIR)/*.ko;
sudo groupadd -f maccel;
sudo depmod;
sudo chown -v :maccel /sys/module/maccel/parameters/*;
sudo chown -v :maccel /sys/module/maccel/parameters/* /dev/maccel;
sudo chmod g+r /dev/maccel;
ls -l /sys/module/maccel/parameters/*

uninstall: clean
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,6 @@ This debugging experience might be lacking still. Feel free to report any issues
- https://docs.kernel.org/input/input-programming.html
- https://www.kernel.org/doc/Documentation/input/input.txt
- https://docs.kernel.org/driver-api/input.html
- https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html
- https://www.youtube.com/watch?v=oX9ZwMQL2f4
- https://gist.github.com/fstiehle/17fca11d7d1b4c2b8dfd982e1cf39caf
41 changes: 41 additions & 0 deletions cli/src/inputspeed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//! Provides functions to allow us to get
//! the last immediate input speed of the user's mouse movement.
//!
//! This is mainly to allow us to visually represent the user's current
//! speed and applied sensitivity.

use std::{fs, io::Read, thread};

static mut INPUT_SPEED: f32 = 0.0;

use crate::libmaccel::{fixedptc::Fixedpt, sensitivity, Params};

pub fn read_input_speed_and_resolved_sens() -> (f64, f64) {
let input_speed = read_input_speed();
return (input_speed as f64, sensitivity(input_speed, Params::new()));
}

pub fn read_input_speed() -> f32 {
// Safety don't care about race conditions
return unsafe { INPUT_SPEED };
}

pub fn setup_input_speed_reader() {
thread::spawn(|| {
let mut file = fs::File::open("/dev/maccel").expect("failed to open /dev/maccel");
let mut buffer = [0u8; 4];

loop {
file.read_exact(&mut buffer)
.expect("failed to read 4 bytes from /dev/maccel");

// The buffer, we expect, is just 4 bytes for a number (fixedpt)
let num: f32 = Fixedpt(i32::from_be_bytes(buffer)).into();

// Safety don't care about race conditions
unsafe { INPUT_SPEED = num };

thread::sleep(std::time::Duration::from_nanos(500));
}
});
}
1 change: 1 addition & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod inputspeed;
mod libmaccel;
mod params;
mod tui;
Expand Down
9 changes: 7 additions & 2 deletions cli/src/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,13 @@ impl Param {
return Ok(Fixedpt(value));
}

pub fn display_name(&self) -> String {
return format!("{:?}", self);
pub fn display_name(&self) -> &'static str {
return match self {
Param::SensMult => "Sens-Multiplier",
Param::Accel => "Accel",
Param::Offset => "Offset",
Param::OutputCap => "Output-Cap",
};
}

/// The canonical name of parameter, exactly what can be read from /sys/module/maccel/parameters
Expand Down
30 changes: 20 additions & 10 deletions cli/src/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::io::stdout;
use tui_input::{backend::crossterm::EventHandler, Input};

use crate::{
inputspeed,
libmaccel::{sensitivity, Params},
params::Param,
};
Expand Down Expand Up @@ -117,6 +118,8 @@ pub fn run_tui() -> anyhow::Result<()> {

let mut app = AppState::new();

inputspeed::setup_input_speed_reader();

loop {
terminal.draw(|f| ui(f, &mut app))?;

Expand Down Expand Up @@ -360,16 +363,23 @@ fn ui(frame: &mut Frame, app: &mut AppState) {
.map(|x| (x as f64, sensitivity(x, Params::new())))
.collect();

let chart = Chart::new(vec![Dataset::default()
.name(format!(
"f(x) = (1 + {}⋅x) ⋅ {}",
Param::Accel.display_name(),
Param::SensMult.display_name()
))
.marker(symbols::Marker::Braille)
.graph_type(GraphType::Line)
.style(Style::default().green())
.data(&data)])
let highlight_point = &[inputspeed::read_input_speed_and_resolved_sens()];
let chart = Chart::new(vec![
Dataset::default()
.name(format!(
"f(x) = (1 + {}⋅x) ⋅ {}",
Param::Accel.display_name(),
Param::SensMult.display_name()
))
.marker(symbols::Marker::Braille)
.graph_type(GraphType::Line)
.style(Style::default().green())
.data(&data),
Dataset::default() // current instance of acceleration
.marker(symbols::Marker::Braille)
.style(Style::default().red())
.data(highlight_point),
])
.x_axis(x_axis)
.y_axis(y_axis);

Expand Down
28 changes: 17 additions & 11 deletions driver/accel.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,22 @@ extern inline fixedpt sensitivity(fixedpt input_speed, fixedpt param_sens_mult,
return sens;
}

static inline fixedpt input_speed(fixedpt dx, fixedpt dy,
u32 polling_interval) {
fixedpt distance =
fixedpt_sqrt(fixedpt_add(fixedpt_mul(dx, dx), fixedpt_mul(dy, dy)));

dbg("distance (in) %s", fixedpt_cstr(distance, 6));

fixedpt speed_in = fixedpt_div(distance, fixedpt_fromint(polling_interval));

dbg("polling interval %s",
fixedpt_cstr(fixedpt_fromint(polling_interval), 6));
dbg("speed (in) %s", fixedpt_cstr(speed_in, 6));

return speed_in;
}

static inline AccelResult f_accelerate(int x, int y, u32 polling_interval,
fixedpt param_sens_mult,
fixedpt param_accel,
Expand All @@ -63,17 +79,7 @@ static inline AccelResult f_accelerate(int x, int y, u32 polling_interval,
dbg("in: x (fixedpt conversion) %s", fixedpt_cstr(dx, 6));
dbg("in: y (fixedpt conversion) %s", fixedpt_cstr(dy, 6));

fixedpt distance =
fixedpt_sqrt(fixedpt_add(fixedpt_mul(dx, dx), fixedpt_mul(dy, dy)));

dbg("distance (in) %s", fixedpt_cstr(distance, 6));

fixedpt speed_in = fixedpt_div(distance, fixedpt_fromint(polling_interval));

dbg("polling interval %s",
fixedpt_cstr(fixedpt_fromint(polling_interval), 6));
dbg("speed (in) %s", fixedpt_cstr(speed_in, 6));

fixedpt speed_in = input_speed(dx, dy, polling_interval);
fixedpt sens = sensitivity(speed_in, param_sens_mult, param_accel,
param_offset, param_output_cap);

Expand Down
80 changes: 80 additions & 0 deletions driver/input_echo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#ifndef _INPUT_ECHO_
#define _INPUT_ECHO_

#include "./accel.h"
#include "linux/cdev.h"

/**
* Cache of the last [REL_X, REL_Y] values to report to userspace
* on read.
*/
static int input_cache[2] = {0};

static struct cdev device;
static struct class *device_class;
static dev_t device_number;

/*
* Convert an int into an array of four bytes, in big endian (MSB first)
*/
static void int_to_bytes(int num, char bytes[4]) {
bytes[0] = (num >> 24) & 0xFF; // Most significant byte
bytes[1] = (num >> 16) & 0xFF;
bytes[2] = (num >> 8) & 0xFF;
bytes[3] = num & 0xFF; // Least significant byte
}

static ssize_t read(struct file *f, char __user *user_buffer, size_t size,
loff_t *offset) {
int x = input_cache[0];
int y = input_cache[1];
fixedpt speed = input_speed(fixedpt_fromint(x), fixedpt_fromint(y), 1);

signed char be_bytes_for_int[4] = {0};
int_to_bytes(speed, be_bytes_for_int);

int err =
copy_to_user(user_buffer, be_bytes_for_int, sizeof(be_bytes_for_int));
if (err)
return -EFAULT;

return sizeof(be_bytes_for_int);
}

struct file_operations fops = {.owner = THIS_MODULE, .read = read};

static int create_char_device(void) {
int err;
err = alloc_chrdev_region(&device_number, 0, 1, "maccel");
if (err)
return -EIO;

cdev_init(&device, &fops);
device.owner = THIS_MODULE;

cdev_add(&device, device_number, 1);

device_class = class_create("maccel");

if (IS_ERR(device_class)) {
goto err_free_cdev;
}

device_create(device_class, NULL, device_number, NULL, "maccel");

return 0;

err_free_cdev:
cdev_del(&device);
unregister_chrdev_region(device_number, 1);
return -EIO;
}

static void destroy_char_device(void) {
device_destroy(device_class, device_number);
class_destroy(device_class);
cdev_del(&device);
unregister_chrdev_region(device_number, 1);
}

#endif // !_INPUT_ECHO_
11 changes: 8 additions & 3 deletions driver/input_handler.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#include "./accelk.h"
#include "accel.h"
#include "linux/input.h"
#include "input_echo.h"
#include <linux/hid.h>

struct input_dev *virtual_input_dev;
static struct input_dev *virtual_input_dev;

#define NONE_EVENT_VALUE 0
static int relative_axis_events[REL_CNT] = { // [x, y, ..., wheel, ...]
Expand All @@ -18,6 +17,12 @@ static bool maccel_filter(struct input_handle *handle, u32 type,
case EV_REL: {
dbg("EV_REL => code %d, value %d", code, value);
relative_axis_events[code] = value;

// So we can relay the original speed of the mouse movement to userspace
// for visualization.
input_cache[0] = relative_axis_events[REL_X];
input_cache[1] = relative_axis_events[REL_Y];

return true; // so input system skips (filters out) this unaccelerated
// mouse input.
}
Expand Down
35 changes: 25 additions & 10 deletions driver/maccel.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
#include "linux/init.h"
#include "linux/module.h"
#include "linux/usb.h"

#include "./input_handler.h"
#include "./usbmouse.h"
#include "input_echo.h"

// The virtual_input_dev is declared for/in the maccel_input_handler module.
// This initializes it.
Expand Down Expand Up @@ -56,18 +53,36 @@ static int __init my_init(void) {
return error;
}
error = usb_register(&maccel_usb_driver);
if (error) {
input_unregister_device(virtual_input_dev);
input_free_device(virtual_input_dev);
return error;
}
if (error)
goto err_free_vdev;

error = create_char_device();
if (error)
goto err_unregister_usb;

return input_register_handler(&maccel_handler);
error = input_register_handler(&maccel_handler);
if (error)
goto err_free_chrdev;

return 0;

err_free_chrdev:
destroy_char_device();

err_unregister_usb:
usb_deregister(&maccel_usb_driver);

err_free_vdev:
input_unregister_device(virtual_input_dev);
input_free_device(virtual_input_dev);
return error;
}

static void __exit my_exit(void) {
input_unregister_handler(&maccel_handler);

destroy_char_device();

usb_deregister(&maccel_usb_driver);

input_unregister_device(virtual_input_dev);
Expand Down
2 changes: 2 additions & 0 deletions udev_rules/maccel_param_ownership_and_resets
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ LOG_DIR=/var/opt/maccel/logs
mkdir -p $LOG_DIR

chown -v :maccel /sys/module/maccel/parameters/* &> $LOG_DIR/chown;
chown -v :maccel /dev/maccel &> $LOG_DIR/chown;
chmod g+r /dev/maccel &> $LOG_DIR/chmod;

# For persisting parameters values across reboots
RESET_SCRIPTS_DIR=/var/opt/maccel/resets
Expand Down
Loading