Files
gotta-scroll-fast/driver/input_handler.h
T
thomas 5f1254d11a
Tests / test_core_function (push) Failing after 12s
import upstream maccel baseline
2026-03-25 09:32:22 +00:00

232 lines
6.7 KiB
C

#include "./accel_k.h"
#include "linux/input.h"
#include "mouse_move.h"
#include <linux/hid.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0))
#define __cleanup_events 0
#else
#define __cleanup_events 1
#endif
static inline bool rotation_is_enabled(void) {
/* Fast check: the default is "0", so skip atofp unless the string differs */
return PARAM_ANGLE_ROTATION[0] != '0' || PARAM_ANGLE_ROTATION[1] != '\0';
}
/*
* Collect the events EV_REL REL_X and EV_REL REL_Y, once we have both then
* we accelerate the (x, y) vector and set the EV_REL event's value
* through the `value_ptr` of each collected event.
*/
static void event(struct input_handle *handle, struct input_value *value_ptr) {
/* printk(KERN_INFO "type %d, code %d, value %d", type, code, value); */
switch (value_ptr->type) {
case EV_REL: {
dbg("EV_REL => code %d, value %d", value_ptr->code, value_ptr->value);
update_mouse_move(value_ptr);
return;
}
case EV_SYN: {
int x = get_x(MOVEMENT);
int y = get_y(MOVEMENT);
if (x || y) {
dbg("EV_SYN => code %d", value_ptr->code);
/*
* When rotation is active and one axis is missing from this frame,
* point the missing axis to synthetic storage so f_accelerate can
* write the rotated cross-axis component into it. The synthetic
* value is later injected into the event stream by maccel_events().
*
* Only on >= 6.11.0: injection into the event buffer is not
* possible on older kernels, so there is no point in computing
* the cross-axis component.
*/
#if __cleanup_events
if (rotation_is_enabled()) {
ensure_axes_for_rotation();
}
#endif
accelerate(&x, &y);
dbg("accelerated -> (%d, %d)", x, y);
set_x_move(x);
set_y_move(y);
clear_mouse_move();
}
return;
}
default:
return;
}
}
#if __cleanup_events
static unsigned int maccel_events(struct input_handle *handle,
struct input_value *vals,
unsigned int count) {
#else
static void maccel_events(struct input_handle *handle,
const struct input_value *vals, unsigned int count) {
#endif
struct input_value *v;
for (v = vals; v != vals + count; v++) {
event(handle, v);
}
struct input_value *end = vals;
for (v = vals; v != vals + count; v++) {
if (v->type == EV_REL && v->value == NONE_EVENT_VALUE)
continue;
if (end != v) {
*end = *v;
}
end++;
}
int _count = end - vals;
#if __cleanup_events
/*
* Inject synthetic EV_REL events for axes that rotation produced
* but weren't present in the original frame. Insert before SYN_REPORT
* so downstream handlers see a valid event sequence.
*
* Gated to >= 6.11.0 only: on older kernels the handler returns void
* and cannot communicate a new event count to the input subsystem.
* Writing extra events into the buffer on those kernels is undefined
* behavior — the kernel won't deliver them and may behave erratically.
*/
{
struct input_value *syn_pos = NULL;
unsigned int max = handle->dev->max_vals;
/* Find the last SYN_REPORT so we can insert before it */
for (v = vals; v != end; v++) {
if (v->type == EV_SYN && v->code == SYN_REPORT)
syn_pos = v;
}
if (injected_x && synthetic_x_val != NONE_EVENT_VALUE && _count < max) {
if (syn_pos) {
/* Shift SYN_REPORT and everything after it forward by one */
memmove(syn_pos + 1, syn_pos, (end - syn_pos) * sizeof(*syn_pos));
syn_pos->type = EV_REL;
syn_pos->code = REL_X;
syn_pos->value = synthetic_x_val;
syn_pos++;
end++;
_count++;
}
dbg("rotation: injected synthetic REL_X = %d", synthetic_x_val);
}
if (injected_y && synthetic_y_val != NONE_EVENT_VALUE && _count < max) {
if (syn_pos) {
memmove(syn_pos + 1, syn_pos, (end - syn_pos) * sizeof(*syn_pos));
syn_pos->type = EV_REL;
syn_pos->code = REL_Y;
syn_pos->value = synthetic_y_val;
end++;
_count++;
}
dbg("rotation: injected synthetic REL_Y = %d", synthetic_y_val);
}
}
#endif
handle->dev->num_vals = _count;
#if __cleanup_events
return _count;
#endif
}
/* Same as Linux's input_register_handle but we always add the handle to the
* head of handlers */
static int input_register_handle_head(struct input_handle *handle) {
struct input_handler *handler = handle->handler;
struct input_dev *dev = handle->dev;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 7))
/* In 6.11.7 an additional handler pointer was added:
* https://github.com/torvalds/linux/commit/071b24b54d2d05fbf39ddbb27dee08abd1d713f3
*/
if (handler->events)
handle->handle_events = handler->events;
#endif
int error = mutex_lock_interruptible(&dev->mutex);
if (error)
return error;
list_add_rcu(&handle->d_node, &dev->h_list);
mutex_unlock(&dev->mutex);
list_add_tail_rcu(&handle->h_node, &handler->h_list);
if (handler->start)
handler->start(handle);
return 0;
}
static int maccel_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id) {
struct input_handle *handle;
int error;
handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
if (!handle)
return -ENOMEM;
handle->dev = input_get_device(dev);
handle->handler = handler;
handle->name = "maccel";
error = input_register_handle_head(handle);
if (error)
goto err_free_mem;
error = input_open_device(handle);
if (error)
goto err_unregister_handle;
printk(KERN_INFO pr_fmt("maccel flags: DEBUG=%s; FIXEDPT_BITS=%d"),
DEBUG_TEST ? "true" : "false", FIXEDPT_BITS);
printk(KERN_INFO pr_fmt("maccel connecting to device: %s (%s at %s)"),
dev_name(&dev->dev), dev->name ?: "unknown", dev->phys ?: "unknown");
return 0;
err_unregister_handle:
input_unregister_handle(handle);
err_free_mem:
kfree(handle);
return error;
}
static void maccel_disconnect(struct input_handle *handle) {
input_close_device(handle);
input_unregister_handle(handle);
kfree(handle);
}
static const struct input_device_id my_ids[] = {
{.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = {BIT_MASK(EV_REL)}}, // Match all relative pointer values
{},
};
MODULE_DEVICE_TABLE(input, my_ids);
struct input_handler maccel_handler = {.events = maccel_events,
.connect = maccel_connect,
.disconnect = maccel_disconnect,
.name = "maccel",
.id_table = my_ids};