mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 05:40:04 +01:00
evdev: Handle scroll wheel with a plugin
Transform the code present in evdev-wheel.c into a internal plugin. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1235>
This commit is contained in:
parent
ee35e04207
commit
d1800a76fe
7 changed files with 727 additions and 484 deletions
|
|
@ -376,6 +376,7 @@ src_libinput = src_libfilter + [
|
|||
'src/libinput.c',
|
||||
'src/libinput-plugin.c',
|
||||
'src/libinput-plugin-button-debounce.c',
|
||||
'src/libinput-plugin-mouse-wheel.c',
|
||||
'src/libinput-plugin-tablet-double-tool.c',
|
||||
'src/libinput-plugin-tablet-eraser-button.c',
|
||||
'src/libinput-plugin-tablet-forced-tool.c',
|
||||
|
|
@ -395,7 +396,6 @@ src_libinput = src_libfilter + [
|
|||
'src/evdev-tablet.c',
|
||||
'src/evdev-tablet-pad.c',
|
||||
'src/evdev-tablet-pad-leds.c',
|
||||
'src/evdev-wheel.c',
|
||||
'src/path-seat.c',
|
||||
'src/udev-seat.c',
|
||||
'src/timer.c',
|
||||
|
|
|
|||
|
|
@ -207,6 +207,100 @@ fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
|
|||
pointer_notify_motion(base, time, &accel, &raw);
|
||||
}
|
||||
|
||||
static void
|
||||
fallback_flush_wheels(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
struct normalized_coords wheel_degrees = { 0.0, 0.0 };
|
||||
struct discrete_coords discrete = { 0.0, 0.0 };
|
||||
struct wheel_v120 v120 = { 0.0, 0.0 };
|
||||
|
||||
if (!libinput_device_has_capability(&device->base, LIBINPUT_DEVICE_CAP_POINTER))
|
||||
return;
|
||||
|
||||
/* This mouse has a trackstick instead of a mouse wheel and sends
|
||||
* trackstick data via REL_WHEEL. Normalize it like normal x/y coordinates.
|
||||
*/
|
||||
if (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
|
||||
const struct device_float_coords raw = {
|
||||
.x = dispatch->wheel.lo_res.x,
|
||||
.y = dispatch->wheel.lo_res.y * -1,
|
||||
};
|
||||
const struct normalized_coords normalized =
|
||||
filter_dispatch_scroll(device->pointer.filter,
|
||||
&raw,
|
||||
device,
|
||||
time);
|
||||
evdev_post_scroll(device,
|
||||
time,
|
||||
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
|
||||
&normalized);
|
||||
dispatch->wheel.hi_res.x = 0;
|
||||
dispatch->wheel.hi_res.y = 0;
|
||||
dispatch->wheel.lo_res.x = 0;
|
||||
dispatch->wheel.lo_res.y = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.hi_res.y != 0) {
|
||||
int value = dispatch->wheel.hi_res.y;
|
||||
|
||||
v120.y = -1 * value;
|
||||
wheel_degrees.y = -1 * value/120.0 * device->scroll.wheel_click_angle.y;
|
||||
evdev_notify_axis_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
||||
&wheel_degrees,
|
||||
&v120);
|
||||
dispatch->wheel.hi_res.y = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.lo_res.y != 0) {
|
||||
int value = dispatch->wheel.lo_res.y;
|
||||
|
||||
wheel_degrees.y = -1 * value * device->scroll.wheel_click_angle.y;
|
||||
discrete.y = -1 * value;
|
||||
evdev_notify_axis_legacy_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
||||
&wheel_degrees,
|
||||
&discrete);
|
||||
dispatch->wheel.lo_res.y = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.hi_res.x != 0) {
|
||||
int value = dispatch->wheel.hi_res.x;
|
||||
|
||||
v120.x = value;
|
||||
wheel_degrees.x = value/120.0 * device->scroll.wheel_click_angle.x;
|
||||
evdev_notify_axis_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
||||
&wheel_degrees,
|
||||
&v120);
|
||||
dispatch->wheel.hi_res.x = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.lo_res.x != 0) {
|
||||
int value = dispatch->wheel.lo_res.x;
|
||||
|
||||
wheel_degrees.x = value * device->scroll.wheel_click_angle.x;
|
||||
discrete.x = value;
|
||||
evdev_notify_axis_legacy_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
||||
&wheel_degrees,
|
||||
&discrete);
|
||||
dispatch->wheel.lo_res.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
|
|
@ -754,6 +848,20 @@ fallback_reject_relative(struct evdev_device *device,
|
|||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
fallback_rotate_wheel(struct fallback_dispatch *dispatch,
|
||||
struct evdev_event *e)
|
||||
{
|
||||
/* Special case: if we're upside down (-ish),
|
||||
* swap the direction of the wheels so that user-down
|
||||
* means scroll down. This isn't done for any other angle
|
||||
* since it's not clear what the heuristics should be.*/
|
||||
if (dispatch->rotation.angle >= 160.0 &&
|
||||
dispatch->rotation.angle <= 220.0) {
|
||||
e->value *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
fallback_process_relative(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
|
|
@ -771,11 +879,25 @@ fallback_process_relative(struct fallback_dispatch *dispatch,
|
|||
dispatch->rel.y += e->value;
|
||||
dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
|
||||
break;
|
||||
case EVDEV_REL_WHEEL:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.lo_res.y += e->value;
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.lo_res.x += e->value;
|
||||
break;
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.hi_res.y += e->value;
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.hi_res.x += e->value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fallback_wheel_process_relative(dispatch, e, time);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -941,7 +1063,7 @@ fallback_handle_state(struct fallback_dispatch *dispatch,
|
|||
if (need_touch_frame)
|
||||
touch_notify_frame(&device->base, time);
|
||||
|
||||
fallback_wheel_handle_state(dispatch, device, time);
|
||||
fallback_flush_wheels(dispatch, device, time);
|
||||
|
||||
/* Buttons and keys */
|
||||
if (dispatch->pending_event & EVDEV_KEY) {
|
||||
|
|
@ -1125,7 +1247,6 @@ fallback_interface_remove(struct evdev_dispatch *evdev_dispatch)
|
|||
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
||||
struct evdev_paired_keyboard *kbd;
|
||||
|
||||
libinput_timer_cancel(&dispatch->wheel.scroll_timer);
|
||||
libinput_timer_cancel(&dispatch->debounce.timer);
|
||||
libinput_timer_cancel(&dispatch->debounce.timer_short);
|
||||
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
|
||||
|
|
@ -1245,7 +1366,6 @@ fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch)
|
|||
{
|
||||
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
||||
|
||||
libinput_timer_destroy(&dispatch->wheel.scroll_timer);
|
||||
libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
|
||||
libinput_timer_destroy(&dispatch->debounce.timer);
|
||||
libinput_timer_destroy(&dispatch->debounce.timer_short);
|
||||
|
|
@ -1718,7 +1838,6 @@ fallback_dispatch_create(struct libinput_device *libinput_device)
|
|||
want_config);
|
||||
}
|
||||
|
||||
fallback_init_wheel(dispatch, device);
|
||||
fallback_init_arbitration(dispatch, device);
|
||||
|
||||
return &dispatch->base;
|
||||
|
|
|
|||
|
|
@ -60,20 +60,6 @@ enum palm_state {
|
|||
PALM_WAS_PALM, /* this touch sequence was a palm but isn't now */
|
||||
};
|
||||
|
||||
enum wheel_state {
|
||||
WHEEL_STATE_NONE,
|
||||
WHEEL_STATE_ACCUMULATING_SCROLL,
|
||||
WHEEL_STATE_SCROLLING,
|
||||
};
|
||||
|
||||
enum wheel_direction {
|
||||
WHEEL_DIR_UNKNOW,
|
||||
WHEEL_DIR_VPOS,
|
||||
WHEEL_DIR_VNEG,
|
||||
WHEEL_DIR_HPOS,
|
||||
WHEEL_DIR_HNEG,
|
||||
};
|
||||
|
||||
struct mt_slot {
|
||||
bool dirty;
|
||||
enum mt_slot_state state;
|
||||
|
|
@ -112,13 +98,8 @@ struct fallback_dispatch {
|
|||
struct device_coords rel;
|
||||
|
||||
struct {
|
||||
enum wheel_state state;
|
||||
struct device_coords lo_res;
|
||||
struct device_coords hi_res;
|
||||
bool hi_res_event_received;
|
||||
struct libinput_timer scroll_timer;
|
||||
enum wheel_direction dir;
|
||||
bool ignore_small_hi_res_movements;
|
||||
} wheel;
|
||||
|
||||
struct {
|
||||
|
|
@ -239,17 +220,4 @@ fallback_notify_physical_button(struct fallback_dispatch *dispatch,
|
|||
evdev_usage_t button,
|
||||
enum libinput_button_state state);
|
||||
|
||||
void
|
||||
fallback_init_wheel(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device);
|
||||
|
||||
void
|
||||
fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
|
||||
struct evdev_event *e, uint64_t time);
|
||||
|
||||
void
|
||||
fallback_wheel_handle_state(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,445 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2013 Jonas Ådahl
|
||||
* Copyright © 2013-2017 Red Hat, Inc.
|
||||
* Copyright © 2017 James Ye <jye836@gmail.com>
|
||||
* Copyright © 2021 José Expósito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "evdev-fallback.h"
|
||||
#include "util-input-event.h"
|
||||
|
||||
#define ACC_V120_THRESHOLD 60
|
||||
#define WHEEL_SCROLL_TIMEOUT ms2us(500)
|
||||
|
||||
enum wheel_event {
|
||||
WHEEL_EVENT_SCROLL_ACCUMULATED,
|
||||
WHEEL_EVENT_SCROLL,
|
||||
WHEEL_EVENT_SCROLL_TIMEOUT,
|
||||
WHEEL_EVENT_SCROLL_DIR_CHANGED,
|
||||
};
|
||||
|
||||
static inline const char *
|
||||
wheel_state_to_str(enum wheel_state state)
|
||||
{
|
||||
switch(state) {
|
||||
CASE_RETURN_STRING(WHEEL_STATE_NONE);
|
||||
CASE_RETURN_STRING(WHEEL_STATE_ACCUMULATING_SCROLL);
|
||||
CASE_RETURN_STRING(WHEEL_STATE_SCROLLING);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
wheel_event_to_str(enum wheel_event event)
|
||||
{
|
||||
switch(event) {
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_ACCUMULATED);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_TIMEOUT);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_DIR_CHANGED);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
log_wheel_bug(struct fallback_dispatch *dispatch, enum wheel_event event)
|
||||
{
|
||||
evdev_log_bug_libinput(dispatch->device,
|
||||
"invalid wheel event %s in state %s\n",
|
||||
wheel_event_to_str(event),
|
||||
wheel_state_to_str(dispatch->wheel.state));
|
||||
}
|
||||
|
||||
static inline void
|
||||
wheel_set_scroll_timer(struct fallback_dispatch *dispatch, uint64_t time)
|
||||
{
|
||||
libinput_timer_set(&dispatch->wheel.scroll_timer,
|
||||
time + WHEEL_SCROLL_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wheel_cancel_scroll_timer(struct fallback_dispatch *dispatch)
|
||||
{
|
||||
libinput_timer_cancel(&dispatch->wheel.scroll_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_none(struct fallback_dispatch *dispatch,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
dispatch->wheel.state =
|
||||
dispatch->wheel.ignore_small_hi_res_movements ?
|
||||
WHEEL_STATE_ACCUMULATING_SCROLL :
|
||||
WHEEL_STATE_SCROLLING;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
log_wheel_bug(dispatch, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_accumulating_scroll(struct fallback_dispatch *dispatch,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
dispatch->wheel.state = WHEEL_STATE_SCROLLING;
|
||||
wheel_set_scroll_timer(dispatch, time);
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
/* Ignore scroll while accumulating deltas */
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
dispatch->wheel.state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
log_wheel_bug(dispatch, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_scrolling(struct fallback_dispatch *dispatch,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
if (dispatch->wheel.ignore_small_hi_res_movements) {
|
||||
wheel_cancel_scroll_timer(dispatch);
|
||||
wheel_set_scroll_timer(dispatch, time);
|
||||
}
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
dispatch->wheel.state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
if (dispatch->wheel.ignore_small_hi_res_movements)
|
||||
wheel_cancel_scroll_timer(dispatch);
|
||||
dispatch->wheel.state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
log_wheel_bug(dispatch, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event(struct fallback_dispatch *dispatch,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
enum wheel_state oldstate = dispatch->wheel.state;
|
||||
|
||||
switch (oldstate) {
|
||||
case WHEEL_STATE_NONE:
|
||||
wheel_handle_event_on_state_none(dispatch, event, time);
|
||||
break;
|
||||
case WHEEL_STATE_ACCUMULATING_SCROLL:
|
||||
wheel_handle_event_on_state_accumulating_scroll(dispatch,
|
||||
event,
|
||||
time);
|
||||
break;
|
||||
case WHEEL_STATE_SCROLLING:
|
||||
wheel_handle_event_on_state_scrolling(dispatch, event, time);
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldstate != dispatch->wheel.state) {
|
||||
evdev_log_debug(dispatch->device,
|
||||
"wheel: %s → %s → %s\n",
|
||||
wheel_state_to_str(oldstate),
|
||||
wheel_event_to_str(event),
|
||||
wheel_state_to_str(dispatch->wheel.state));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_flush_scroll(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
struct normalized_coords wheel_degrees = { 0.0, 0.0 };
|
||||
struct discrete_coords discrete = { 0.0, 0.0 };
|
||||
struct wheel_v120 v120 = { 0.0, 0.0 };
|
||||
|
||||
/* This mouse has a trackstick instead of a mouse wheel and sends
|
||||
* trackstick data via REL_WHEEL. Normalize it like normal x/y coordinates.
|
||||
*/
|
||||
if (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
|
||||
const struct device_float_coords raw = {
|
||||
.x = dispatch->wheel.lo_res.x,
|
||||
.y = dispatch->wheel.lo_res.y * -1,
|
||||
};
|
||||
const struct normalized_coords normalized =
|
||||
filter_dispatch_scroll(device->pointer.filter,
|
||||
&raw,
|
||||
device,
|
||||
time);
|
||||
evdev_post_scroll(device,
|
||||
time,
|
||||
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
|
||||
&normalized);
|
||||
dispatch->wheel.hi_res.x = 0;
|
||||
dispatch->wheel.hi_res.y = 0;
|
||||
dispatch->wheel.lo_res.x = 0;
|
||||
dispatch->wheel.lo_res.y = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.hi_res.y != 0) {
|
||||
int value = dispatch->wheel.hi_res.y;
|
||||
|
||||
v120.y = -1 * value;
|
||||
wheel_degrees.y = -1 * value/120.0 * device->scroll.wheel_click_angle.y;
|
||||
evdev_notify_axis_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
||||
&wheel_degrees,
|
||||
&v120);
|
||||
dispatch->wheel.hi_res.y = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.lo_res.y != 0) {
|
||||
int value = dispatch->wheel.lo_res.y;
|
||||
|
||||
wheel_degrees.y = -1 * value * device->scroll.wheel_click_angle.y;
|
||||
discrete.y = -1 * value;
|
||||
evdev_notify_axis_legacy_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
||||
&wheel_degrees,
|
||||
&discrete);
|
||||
dispatch->wheel.lo_res.y = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.hi_res.x != 0) {
|
||||
int value = dispatch->wheel.hi_res.x;
|
||||
|
||||
v120.x = value;
|
||||
wheel_degrees.x = value/120.0 * device->scroll.wheel_click_angle.x;
|
||||
evdev_notify_axis_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
||||
&wheel_degrees,
|
||||
&v120);
|
||||
dispatch->wheel.hi_res.x = 0;
|
||||
}
|
||||
|
||||
if (dispatch->wheel.lo_res.x != 0) {
|
||||
int value = dispatch->wheel.lo_res.x;
|
||||
|
||||
wheel_degrees.x = value * device->scroll.wheel_click_angle.x;
|
||||
discrete.x = value;
|
||||
evdev_notify_axis_legacy_wheel(
|
||||
device,
|
||||
time,
|
||||
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
||||
&wheel_degrees,
|
||||
&discrete);
|
||||
dispatch->wheel.lo_res.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_none(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_accumulating_scroll(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
if (abs(dispatch->wheel.hi_res.x) >= ACC_V120_THRESHOLD ||
|
||||
abs(dispatch->wheel.hi_res.y) >= ACC_V120_THRESHOLD) {
|
||||
wheel_handle_event(dispatch,
|
||||
WHEEL_EVENT_SCROLL_ACCUMULATED,
|
||||
time);
|
||||
wheel_flush_scroll(dispatch, device, time);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_scrolling(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
wheel_flush_scroll(dispatch, device, time);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_direction_change(struct fallback_dispatch *dispatch,
|
||||
struct evdev_event *e,
|
||||
uint64_t time)
|
||||
{
|
||||
enum wheel_direction new_dir = WHEEL_DIR_UNKNOW;
|
||||
|
||||
switch (evdev_usage_enum(e->usage)) {
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
new_dir = (e->value > 0) ? WHEEL_DIR_VPOS : WHEEL_DIR_VNEG;
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
new_dir = (e->value > 0) ? WHEEL_DIR_HPOS : WHEEL_DIR_HNEG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_dir != WHEEL_DIR_UNKNOW && new_dir != dispatch->wheel.dir) {
|
||||
dispatch->wheel.dir = new_dir;
|
||||
wheel_handle_event(dispatch,
|
||||
WHEEL_EVENT_SCROLL_DIR_CHANGED,
|
||||
time);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fallback_rotate_wheel(struct fallback_dispatch *dispatch,
|
||||
struct evdev_event *e)
|
||||
{
|
||||
/* Special case: if we're upside down (-ish),
|
||||
* swap the direction of the wheels so that user-down
|
||||
* means scroll down. This isn't done for any other angle
|
||||
* since it's not clear what the heuristics should be.*/
|
||||
if (dispatch->rotation.angle >= 160.0 &&
|
||||
dispatch->rotation.angle <= 220.0) {
|
||||
e->value *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fallback_wheel_process_relative(struct fallback_dispatch *dispatch,
|
||||
struct evdev_event *e, uint64_t time)
|
||||
{
|
||||
switch (evdev_usage_enum(e->usage)) {
|
||||
case EVDEV_REL_WHEEL:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.lo_res.y += e->value;
|
||||
wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.lo_res.x += e->value;
|
||||
wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.hi_res.y += e->value;
|
||||
dispatch->wheel.hi_res_event_received = true;
|
||||
wheel_handle_direction_change(dispatch, e, time);
|
||||
wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
fallback_rotate_wheel(dispatch, e);
|
||||
dispatch->wheel.hi_res.x += e->value;
|
||||
dispatch->wheel.hi_res_event_received = true;
|
||||
wheel_handle_direction_change(dispatch, e, time);
|
||||
wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fallback_wheel_handle_state(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device,
|
||||
uint64_t time)
|
||||
{
|
||||
if (!libinput_device_has_capability(&device->base, LIBINPUT_DEVICE_CAP_POINTER))
|
||||
return;
|
||||
|
||||
if (!dispatch->wheel.hi_res_event_received &&
|
||||
(dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0)) {
|
||||
evdev_log_bug_kernel(device,
|
||||
"device supports high-resolution scroll but only low-resolution events have been received.\n"
|
||||
"See %s/incorrectly-enabled-hires.html for details\n",
|
||||
HTTP_DOC_LINK);
|
||||
dispatch->wheel.hi_res.x = dispatch->wheel.lo_res.x * 120;
|
||||
dispatch->wheel.hi_res.y = dispatch->wheel.lo_res.y * 120;
|
||||
}
|
||||
|
||||
switch (dispatch->wheel.state) {
|
||||
case WHEEL_STATE_NONE:
|
||||
wheel_handle_state_none(dispatch, device, time);
|
||||
break;
|
||||
case WHEEL_STATE_ACCUMULATING_SCROLL:
|
||||
wheel_handle_state_accumulating_scroll(dispatch, device, time);
|
||||
break;
|
||||
case WHEEL_STATE_SCROLLING:
|
||||
wheel_handle_state_scrolling(dispatch, device, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_on_scroll_timer_timeout(uint64_t now, void *data)
|
||||
{
|
||||
struct evdev_device *device = data;
|
||||
struct fallback_dispatch *dispatch =
|
||||
fallback_dispatch(device->dispatch);
|
||||
|
||||
wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL_TIMEOUT, now);
|
||||
}
|
||||
|
||||
void
|
||||
fallback_init_wheel(struct fallback_dispatch *dispatch,
|
||||
struct evdev_device *device)
|
||||
{
|
||||
char timer_name[64];
|
||||
|
||||
dispatch->wheel.state = WHEEL_STATE_NONE;
|
||||
dispatch->wheel.dir = WHEEL_DIR_UNKNOW;
|
||||
dispatch->wheel.ignore_small_hi_res_movements =
|
||||
!evdev_device_is_virtual(dispatch->device);
|
||||
|
||||
if (dispatch->wheel.ignore_small_hi_res_movements) {
|
||||
snprintf(timer_name,
|
||||
sizeof(timer_name),
|
||||
"%s wheel scroll",
|
||||
evdev_device_get_sysname(device));
|
||||
libinput_timer_init(&dispatch->wheel.scroll_timer,
|
||||
evdev_libinput_context(device),
|
||||
timer_name,
|
||||
wheel_on_scroll_timer_timeout,
|
||||
device);
|
||||
}
|
||||
}
|
||||
569
src/libinput-plugin-mouse-wheel.c
Normal file
569
src/libinput-plugin-mouse-wheel.c
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
* Copyright © 2013 Jonas Ådahl
|
||||
* Copyright © 2013-2017 Red Hat, Inc.
|
||||
* Copyright © 2017 James Ye <jye836@gmail.com>
|
||||
* Copyright © 2021-2025 José Expósito
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <libevdev/libevdev.h>
|
||||
|
||||
#include "evdev.h"
|
||||
#include "evdev-fallback.h"
|
||||
|
||||
#include "libinput-log.h"
|
||||
#include "libinput-util.h"
|
||||
#include "libinput-plugin.h"
|
||||
#include "libinput-plugin-mouse-wheel.h"
|
||||
|
||||
#define ACC_V120_THRESHOLD 60
|
||||
#define WHEEL_SCROLL_TIMEOUT ms2us(500)
|
||||
|
||||
enum wheel_state {
|
||||
WHEEL_STATE_NONE,
|
||||
WHEEL_STATE_ACCUMULATING_SCROLL,
|
||||
WHEEL_STATE_SCROLLING,
|
||||
};
|
||||
|
||||
enum wheel_direction {
|
||||
WHEEL_DIR_UNKNOW,
|
||||
WHEEL_DIR_VPOS,
|
||||
WHEEL_DIR_VNEG,
|
||||
WHEEL_DIR_HPOS,
|
||||
WHEEL_DIR_HNEG,
|
||||
};
|
||||
|
||||
enum wheel_event {
|
||||
WHEEL_EVENT_SCROLL_ACCUMULATED,
|
||||
WHEEL_EVENT_SCROLL,
|
||||
WHEEL_EVENT_SCROLL_TIMEOUT,
|
||||
WHEEL_EVENT_SCROLL_DIR_CHANGED,
|
||||
};
|
||||
|
||||
struct plugin_device {
|
||||
struct list link;
|
||||
struct plugin_data *parent;
|
||||
struct libinput_device *device;
|
||||
|
||||
enum wheel_state state;
|
||||
struct device_coords lo_res;
|
||||
struct device_coords hi_res;
|
||||
bool hi_res_event_received;
|
||||
struct libinput_plugin_timer *scroll_timer;
|
||||
enum wheel_direction dir;
|
||||
bool ignore_small_hi_res_movements;
|
||||
};
|
||||
|
||||
struct plugin_data {
|
||||
struct libinput_plugin *plugin;
|
||||
struct list devices;
|
||||
};
|
||||
|
||||
static inline const char *
|
||||
wheel_state_to_str(enum wheel_state state)
|
||||
{
|
||||
switch(state) {
|
||||
CASE_RETURN_STRING(WHEEL_STATE_NONE);
|
||||
CASE_RETURN_STRING(WHEEL_STATE_ACCUMULATING_SCROLL);
|
||||
CASE_RETURN_STRING(WHEEL_STATE_SCROLLING);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
wheel_event_to_str(enum wheel_event event)
|
||||
{
|
||||
switch(event) {
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_ACCUMULATED);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_TIMEOUT);
|
||||
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_DIR_CHANGED);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void
|
||||
log_wheel_bug(struct plugin_device *pd, enum wheel_event event)
|
||||
{
|
||||
plugin_log_bug_libinput(pd->parent->plugin,
|
||||
"invalid wheel event %s in state %s\n",
|
||||
wheel_event_to_str(event),
|
||||
wheel_state_to_str(pd->state));
|
||||
}
|
||||
|
||||
static inline void
|
||||
wheel_set_scroll_timer(struct plugin_device *pd, uint64_t time)
|
||||
{
|
||||
if (!pd->scroll_timer)
|
||||
return;
|
||||
|
||||
libinput_plugin_timer_set(pd->scroll_timer,
|
||||
time + WHEEL_SCROLL_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void
|
||||
wheel_cancel_scroll_timer(struct plugin_device *pd)
|
||||
{
|
||||
if (!pd->scroll_timer)
|
||||
return;
|
||||
|
||||
libinput_plugin_timer_cancel(pd->scroll_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_none(struct plugin_device *pd,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
pd->state =
|
||||
pd->ignore_small_hi_res_movements ?
|
||||
WHEEL_STATE_ACCUMULATING_SCROLL :
|
||||
WHEEL_STATE_SCROLLING;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
log_wheel_bug(pd, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_accumulating_scroll(struct plugin_device *pd,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
pd->state = WHEEL_STATE_SCROLLING;
|
||||
wheel_set_scroll_timer(pd, time);
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
/* Ignore scroll while accumulating deltas */
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
pd->state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
log_wheel_bug(pd, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event_on_state_scrolling(struct plugin_device *pd,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (event) {
|
||||
case WHEEL_EVENT_SCROLL:
|
||||
wheel_cancel_scroll_timer(pd);
|
||||
wheel_set_scroll_timer(pd, time);
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_TIMEOUT:
|
||||
pd->state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_DIR_CHANGED:
|
||||
wheel_cancel_scroll_timer(pd);
|
||||
pd->state = WHEEL_STATE_NONE;
|
||||
break;
|
||||
case WHEEL_EVENT_SCROLL_ACCUMULATED:
|
||||
log_wheel_bug(pd, event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_event(struct plugin_device *pd,
|
||||
enum wheel_event event,
|
||||
uint64_t time)
|
||||
{
|
||||
enum wheel_state oldstate = pd->state;
|
||||
|
||||
switch (oldstate) {
|
||||
case WHEEL_STATE_NONE:
|
||||
wheel_handle_event_on_state_none(pd, event, time);
|
||||
break;
|
||||
case WHEEL_STATE_ACCUMULATING_SCROLL:
|
||||
wheel_handle_event_on_state_accumulating_scroll(pd,
|
||||
event,
|
||||
time);
|
||||
break;
|
||||
case WHEEL_STATE_SCROLLING:
|
||||
wheel_handle_event_on_state_scrolling(pd, event, time);
|
||||
break;
|
||||
}
|
||||
|
||||
if (oldstate != pd->state) {
|
||||
plugin_log_debug(pd->parent->plugin,
|
||||
"wheel: %s → %s → %s\n",
|
||||
wheel_state_to_str(oldstate),
|
||||
wheel_event_to_str(event),
|
||||
wheel_state_to_str(pd->state));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_remove_scroll_events(struct evdev_frame *frame)
|
||||
{
|
||||
size_t nevents;
|
||||
_unref_(evdev_frame) *copy = evdev_frame_clone(frame);
|
||||
struct evdev_event *events = evdev_frame_get_events(copy, &nevents);
|
||||
|
||||
evdev_frame_reset(frame);
|
||||
|
||||
for (size_t i = 0; i < nevents; i++) {
|
||||
struct evdev_event *e = &events[i];
|
||||
|
||||
switch (evdev_usage_enum(e->usage)) {
|
||||
case EVDEV_REL_WHEEL:
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
case EVDEV_REL_HWHEEL:
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
/* Do not append scroll events */
|
||||
break;
|
||||
default:
|
||||
evdev_frame_append(frame, e, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_queue_scroll_events(struct plugin_device *pd, struct evdev_frame *frame)
|
||||
{
|
||||
if (pd->hi_res.y != 0) {
|
||||
struct evdev_event e = {
|
||||
.usage = evdev_usage_from(EVDEV_REL_WHEEL_HI_RES),
|
||||
.value = pd->hi_res.y,
|
||||
};
|
||||
|
||||
evdev_frame_append(frame, &e, 1);
|
||||
pd->hi_res.y = 0;
|
||||
}
|
||||
|
||||
if (pd->lo_res.y != 0) {
|
||||
struct evdev_event e = {
|
||||
.usage = evdev_usage_from(EVDEV_REL_WHEEL),
|
||||
.value = pd->lo_res.y,
|
||||
};
|
||||
|
||||
evdev_frame_append(frame, &e, 1);
|
||||
pd->lo_res.y = 0;
|
||||
}
|
||||
|
||||
if (pd->hi_res.x != 0) {
|
||||
struct evdev_event e = {
|
||||
.usage = evdev_usage_from(EVDEV_REL_HWHEEL_HI_RES),
|
||||
.value = pd->hi_res.x,
|
||||
};
|
||||
|
||||
evdev_frame_append(frame, &e, 1);
|
||||
pd->hi_res.x = 0;
|
||||
}
|
||||
|
||||
if (pd->lo_res.x != 0) {
|
||||
struct evdev_event e = {
|
||||
.usage = evdev_usage_from(EVDEV_REL_HWHEEL),
|
||||
.value = pd->lo_res.x,
|
||||
};
|
||||
|
||||
evdev_frame_append(frame, &e, 1);
|
||||
pd->lo_res.x = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_none(struct plugin_device *pd,
|
||||
struct evdev_frame *frame,
|
||||
uint64_t time)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_accumulating_scroll(struct plugin_device *pd,
|
||||
struct evdev_frame *frame,
|
||||
uint64_t time)
|
||||
{
|
||||
wheel_remove_scroll_events(frame);
|
||||
|
||||
if (abs(pd->hi_res.x) >= ACC_V120_THRESHOLD ||
|
||||
abs(pd->hi_res.y) >= ACC_V120_THRESHOLD) {
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL_ACCUMULATED, time);
|
||||
wheel_queue_scroll_events(pd, frame);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state_scrolling(struct plugin_device *pd,
|
||||
struct evdev_frame *frame,
|
||||
uint64_t time)
|
||||
{
|
||||
wheel_remove_scroll_events(frame);
|
||||
wheel_queue_scroll_events(pd, frame);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_direction_change(struct plugin_device *pd,
|
||||
struct evdev_event *e,
|
||||
uint64_t time)
|
||||
{
|
||||
enum wheel_direction new_dir = WHEEL_DIR_UNKNOW;
|
||||
|
||||
switch (evdev_usage_enum(e->usage)) {
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
new_dir = (e->value > 0) ? WHEEL_DIR_VPOS : WHEEL_DIR_VNEG;
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
new_dir = (e->value > 0) ? WHEEL_DIR_HPOS : WHEEL_DIR_HNEG;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_dir != WHEEL_DIR_UNKNOW && new_dir != pd->dir) {
|
||||
pd->dir = new_dir;
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL_DIR_CHANGED, time);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_process_relative(struct plugin_device *pd,
|
||||
struct evdev_event *e,
|
||||
uint64_t time)
|
||||
{
|
||||
switch (evdev_usage_enum(e->usage)) {
|
||||
case EVDEV_REL_WHEEL:
|
||||
pd->lo_res.y += e->value;
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL:
|
||||
pd->lo_res.x += e->value;
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_WHEEL_HI_RES:
|
||||
pd->hi_res.y += e->value;
|
||||
pd->hi_res_event_received = true;
|
||||
wheel_handle_direction_change(pd, e, time);
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
case EVDEV_REL_HWHEEL_HI_RES:
|
||||
pd->hi_res.x += e->value;
|
||||
pd->hi_res_event_received = true;
|
||||
wheel_handle_direction_change(pd, e, time);
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_state(struct plugin_device *pd,
|
||||
struct evdev_frame *frame,
|
||||
uint64_t time)
|
||||
{
|
||||
struct evdev_device *evdev = evdev_device(pd->device);
|
||||
|
||||
if (!pd->hi_res_event_received &&
|
||||
(pd->lo_res.x != 0 || pd->lo_res.y != 0)) {
|
||||
evdev_log_bug_kernel(evdev,
|
||||
"device supports high-resolution scroll but only low-resolution events have been received.\n"
|
||||
"See %s/incorrectly-enabled-hires.html for details\n",
|
||||
HTTP_DOC_LINK);
|
||||
pd->hi_res.x = pd->lo_res.x * 120;
|
||||
pd->hi_res.y = pd->lo_res.y * 120;
|
||||
}
|
||||
|
||||
switch (pd->state) {
|
||||
case WHEEL_STATE_NONE:
|
||||
wheel_handle_state_none(pd, frame, time);
|
||||
break;
|
||||
case WHEEL_STATE_ACCUMULATING_SCROLL:
|
||||
wheel_handle_state_accumulating_scroll(pd, frame, time);
|
||||
break;
|
||||
case WHEEL_STATE_SCROLLING:
|
||||
wheel_handle_state_scrolling(pd, frame, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_on_scroll_timer_timeout(struct libinput_plugin *plugin,
|
||||
uint64_t now,
|
||||
void *data)
|
||||
{
|
||||
struct plugin_device *pd = data;
|
||||
|
||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL_TIMEOUT, now);
|
||||
}
|
||||
|
||||
static struct plugin_device *
|
||||
wheel_plugin_device_create(struct libinput_plugin *libinput_plugin,
|
||||
struct plugin_data *plugin,
|
||||
struct libinput_device *device)
|
||||
{
|
||||
struct evdev_device *evdev = evdev_device(device);
|
||||
struct plugin_device *pd = zalloc(sizeof(*pd));
|
||||
|
||||
pd->parent = plugin;
|
||||
pd->device = libinput_device_ref(device);
|
||||
pd->state = WHEEL_STATE_NONE;
|
||||
pd->dir = WHEEL_DIR_UNKNOW;
|
||||
pd->ignore_small_hi_res_movements = !evdev_device_is_virtual(evdev);
|
||||
|
||||
if (pd->ignore_small_hi_res_movements) {
|
||||
pd->scroll_timer = libinput_plugin_timer_new(libinput_plugin,
|
||||
libinput_device_get_sysname(device),
|
||||
wheel_on_scroll_timer_timeout,
|
||||
pd);
|
||||
}
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_plugin_device_destroy(struct plugin_device *pd)
|
||||
{
|
||||
list_remove(&pd->link);
|
||||
|
||||
if (pd->scroll_timer) {
|
||||
wheel_cancel_scroll_timer(pd);
|
||||
libinput_plugin_timer_unref(pd->scroll_timer);
|
||||
}
|
||||
|
||||
libinput_device_unref(pd->device);
|
||||
|
||||
free(pd);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_plugin_destroy(struct libinput_plugin *libinput_plugin)
|
||||
{
|
||||
struct plugin_data *data = libinput_plugin_get_user_data(libinput_plugin);
|
||||
|
||||
struct plugin_device *pd;
|
||||
list_for_each_safe(pd, &data->devices, link) {
|
||||
wheel_plugin_device_destroy(pd);
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_plugin_device_added(struct libinput_plugin *libinput_plugin,
|
||||
struct libinput_device *device)
|
||||
{
|
||||
if (!libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
|
||||
return;
|
||||
|
||||
libinput_plugin_enable_device_event_frame(libinput_plugin, device, true);
|
||||
|
||||
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||
struct plugin_device *pd = wheel_plugin_device_create(libinput_plugin,
|
||||
plugin, device);
|
||||
list_take_append(&plugin->devices, pd, link);
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_plugin_device_removed(struct libinput_plugin *libinput_plugin,
|
||||
struct libinput_device *device)
|
||||
{
|
||||
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||
struct plugin_device *pd;
|
||||
|
||||
list_for_each_safe(pd, &plugin->devices, link) {
|
||||
if (pd->device == device) {
|
||||
wheel_plugin_device_destroy(pd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_handle_frame(struct plugin_device *pd,
|
||||
struct evdev_frame *frame,
|
||||
uint64_t time)
|
||||
{
|
||||
size_t nevents;
|
||||
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
|
||||
|
||||
for (size_t i = 0; i < nevents; i++) {
|
||||
struct evdev_event *e = &events[i];
|
||||
uint16_t type = evdev_event_type(e);
|
||||
|
||||
switch (type) {
|
||||
case EV_REL:
|
||||
wheel_process_relative(pd, e, time);
|
||||
break;
|
||||
case EV_SYN:
|
||||
wheel_handle_state(pd, frame, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wheel_plugin_evdev_frame(struct libinput_plugin *libinput_plugin,
|
||||
struct libinput_device *device,
|
||||
struct evdev_frame *frame)
|
||||
{
|
||||
struct plugin_data *plugin = libinput_plugin_get_user_data(libinput_plugin);
|
||||
struct plugin_device *pd;
|
||||
uint64_t time = evdev_frame_get_time(frame);
|
||||
|
||||
list_for_each(pd, &plugin->devices, link) {
|
||||
if (pd->device == device) {
|
||||
wheel_handle_frame(pd, frame, time);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct libinput_plugin_interface interface = {
|
||||
.run = NULL,
|
||||
.destroy = wheel_plugin_destroy,
|
||||
.device_new = NULL,
|
||||
.device_ignored = NULL,
|
||||
.device_added = wheel_plugin_device_added,
|
||||
.device_removed = wheel_plugin_device_removed,
|
||||
.evdev_frame = wheel_plugin_evdev_frame,
|
||||
};
|
||||
|
||||
void
|
||||
libinput_mouse_plugin_wheel(struct libinput *libinput)
|
||||
{
|
||||
struct plugin_data *plugin = zalloc(sizeof(*plugin));
|
||||
list_init(&plugin->devices);
|
||||
|
||||
_unref_(libinput_plugin) *p = libinput_plugin_new(libinput,
|
||||
"mouse-wheel",
|
||||
&interface,
|
||||
plugin);
|
||||
plugin->plugin = p;
|
||||
}
|
||||
30
src/libinput-plugin-mouse-wheel.h
Normal file
30
src/libinput-plugin-mouse-wheel.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright © 2025 Red Hat, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "libinput.h"
|
||||
#include "libinput-plugin.h"
|
||||
|
||||
void
|
||||
libinput_mouse_plugin_wheel(struct libinput *libinput);
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
#include "libinput-util.h"
|
||||
#include "libinput-private.h"
|
||||
#include "libinput-plugin-button-debounce.h"
|
||||
#include "libinput-plugin-mouse-wheel.h"
|
||||
#include "libinput-plugin-tablet-double-tool.h"
|
||||
#include "libinput-plugin-tablet-eraser-button.h"
|
||||
#include "libinput-plugin-tablet-forced-tool.h"
|
||||
|
|
@ -394,6 +395,7 @@ libinput_plugin_system_load_internal_plugins(struct libinput *libinput,
|
|||
libinput_tablet_plugin_proximity_timer(libinput);
|
||||
libinput_tablet_plugin_eraser_button(libinput);
|
||||
libinput_debounce_plugin(libinput);
|
||||
libinput_mouse_plugin_wheel(libinput);
|
||||
|
||||
/* Our own event dispatch is implemented as mini-plugin,
|
||||
* guarantee this one to always be last (and after any
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue