mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-05-08 21:48:05 +02:00
Merge branch 'signedscrolling' into 'main'
evdev: add new high performance button invoked scrolling mode See merge request libinput/libinput!713
This commit is contained in:
commit
af273e8dec
1 changed files with 151 additions and 37 deletions
182
src/evdev.c
182
src/evdev.c
|
|
@ -101,6 +101,17 @@ static const unsigned int well_known_keyboard_keys[] = {
|
||||||
KEY_BRIGHTNESSDOWN,
|
KEY_BRIGHTNESSDOWN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
signed_scroll(struct evdev_device *device)
|
||||||
|
{
|
||||||
|
/* Abut the device signed scroll request bit next to the test bit.
|
||||||
|
* This is done to avoid making changes to other files in the initial
|
||||||
|
* signed scroll implementation and to not interfere with any other
|
||||||
|
* model flags.
|
||||||
|
*/
|
||||||
|
return device->model_flags & (EVDEV_MODEL_TEST_DEVICE >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
parse_udev_flag(struct evdev_device *device,
|
parse_udev_flag(struct evdev_device *device,
|
||||||
struct udev_device *udev_device,
|
struct udev_device *udev_device,
|
||||||
|
|
@ -249,9 +260,13 @@ evdev_button_scroll_button(struct evdev_device *device,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_press) {
|
if (is_press) {
|
||||||
if (device->scroll.button < BTN_MOUSE + 5) {
|
if (!signed_scroll(device) && device->scroll.button < BTN_MOUSE + 5) {
|
||||||
/* For mouse buttons 1-5 (0x110 to 0x114) we apply a timeout before scrolling
|
/* For mouse buttons 1-5 (0x110 to 0x114) apply a timeout before
|
||||||
* since the button could also be used for regular clicking. */
|
* scrolling since the button could also be used for clicking.
|
||||||
|
* If the device has signed scrolling enabled don't do this.
|
||||||
|
* Intermixing clicking with modal scrolling is basically a bad idea,
|
||||||
|
* assume any signed scrolling device won't want this.
|
||||||
|
*/
|
||||||
enum timer_flags flags = TIMER_FLAG_NONE;
|
enum timer_flags flags = TIMER_FLAG_NONE;
|
||||||
|
|
||||||
device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN;
|
device->scroll.button_scroll_state = BUTTONSCROLL_BUTTON_DOWN;
|
||||||
|
|
@ -273,11 +288,12 @@ evdev_button_scroll_button(struct evdev_device *device,
|
||||||
time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
|
time + DEFAULT_BUTTON_SCROLL_TIMEOUT,
|
||||||
flags);
|
flags);
|
||||||
} else {
|
} else {
|
||||||
/* For extra mouse buttons numbered 6 or more (0x115+) we assume it is
|
/* For extra mouse buttons numbered 6+ (0x115+) we assume it is
|
||||||
* dedicated exclusively to scrolling, so we don't apply the timeout
|
* dedicated exclusively to scrolling, so we don't apply the timeout
|
||||||
* in order to provide immediate scrolling responsiveness. */
|
* in order to provide immediate scrolling responsiveness. */
|
||||||
device->scroll.button_scroll_state = BUTTONSCROLL_READY;
|
device->scroll.button_scroll_state = BUTTONSCROLL_READY;
|
||||||
}
|
}
|
||||||
|
|
||||||
device->scroll.button_down_time = time;
|
device->scroll.button_down_time = time;
|
||||||
evdev_log_debug(device, "btnscroll: down\n");
|
evdev_log_debug(device, "btnscroll: down\n");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -291,16 +307,21 @@ evdev_button_scroll_button(struct evdev_device *device,
|
||||||
case BUTTONSCROLL_READY:
|
case BUTTONSCROLL_READY:
|
||||||
evdev_log_debug(device, "btnscroll: cancel\n");
|
evdev_log_debug(device, "btnscroll: cancel\n");
|
||||||
|
|
||||||
/* If the button is released quickly enough or
|
/* If the button is released quickly enough or without scroll events,
|
||||||
* without scroll events, emit the
|
* emit the button press/release events.
|
||||||
* button press/release events. */
|
* Don't do this for signed scrolling enabled devices as presumably
|
||||||
evdev_pointer_post_button(device,
|
* anyone wanting this feature won't want to overload scrolling
|
||||||
|
* modal indication with an orthogonal operation like browsing.
|
||||||
|
*/
|
||||||
|
if (!signed_scroll(device) && device->scroll.button < BTN_MOUSE + 5) {
|
||||||
|
evdev_pointer_post_button(device,
|
||||||
device->scroll.button_down_time,
|
device->scroll.button_down_time,
|
||||||
device->scroll.button,
|
device->scroll.button,
|
||||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||||
evdev_pointer_post_button(device, time,
|
evdev_pointer_post_button(device, time,
|
||||||
device->scroll.button,
|
device->scroll.button,
|
||||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case BUTTONSCROLL_SCROLLING:
|
case BUTTONSCROLL_SCROLLING:
|
||||||
evdev_log_debug(device, "btnscroll: up\n");
|
evdev_log_debug(device, "btnscroll: up\n");
|
||||||
|
|
@ -325,6 +346,19 @@ evdev_pointer_notify_button(struct evdev_device *device,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (signed_scroll(device) &&
|
||||||
|
device->scroll.method == LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN &&
|
||||||
|
button == BTN_EXTRA && device->scroll.button == BTN_SIDE) {
|
||||||
|
/* If signed scrolling is enabled on the device,
|
||||||
|
* swallow BTN_EXTRA if scrolling on BTN_SIDE.
|
||||||
|
* Temporary solution for Logitech mouse equipped with two position
|
||||||
|
* capacitive touch sensor at mouse wheel location.
|
||||||
|
* This mouse can produce unwanted BTN_EXTRA events intermixed with
|
||||||
|
* desired BTN_SIDE events.
|
||||||
|
*/
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
evdev_pointer_post_button(device, time, button, state);
|
evdev_pointer_post_button(device, time, button, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1585,6 +1619,12 @@ evdev_read_model_flags(struct evdev_device *device)
|
||||||
model_flags |= EVDEV_MODEL_TEST_DEVICE;
|
model_flags |= EVDEV_MODEL_TEST_DEVICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse_udev_flag(device, device->udev_device,
|
||||||
|
"LIBINPUT_SIGNED_SCROLL")) {
|
||||||
|
evdev_log_debug(device, "requests signed scrolling\n");
|
||||||
|
model_flags |= EVDEV_MODEL_TEST_DEVICE >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
return model_flags;
|
return model_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2816,6 +2856,28 @@ evdev_start_scrolling(struct evdev_device *device,
|
||||||
device->scroll.direction |= bit(axis);
|
device->scroll.direction |= bit(axis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Nonlinear functions for signed scrolling dynamic range expansion.
|
||||||
|
* The basic idea is to expand the normal mouse range +-2^7 to +-2^14
|
||||||
|
* This allows both slow/controlled scrolling and very fast scrolling,
|
||||||
|
* fast enough to preclude the need for scrollbar use.
|
||||||
|
*
|
||||||
|
* The current scheme has only been tested on legacy, dpi < 1000, mice.
|
||||||
|
* It may need to be revisited for mice that report fast enough so that
|
||||||
|
* the number of mickeys in each report never reaches the expected
|
||||||
|
* +-2^7 range.
|
||||||
|
*/
|
||||||
|
static double sign(double x) { return x > 0 ? 1.0 : (x < 0 ? -1.0 : 0.0); }
|
||||||
|
static double ballistics_gain(double x, double dpi) {
|
||||||
|
/* Adjust nonlinear knee for legacy mice (dpi < 1000), otherwise
|
||||||
|
* acceleration becomes too touchy.
|
||||||
|
*/
|
||||||
|
double knee = dpi >= DEFAULT_MOUSE_DPI ? 1.0 : DEFAULT_MOUSE_DPI/dpi;
|
||||||
|
knee = knee*knee*knee*128.0;
|
||||||
|
|
||||||
|
x = fabs(x);
|
||||||
|
return x + floor(x*x*x/(knee));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
evdev_post_scroll(struct evdev_device *device,
|
evdev_post_scroll(struct evdev_device *device,
|
||||||
uint64_t time,
|
uint64_t time,
|
||||||
|
|
@ -2825,42 +2887,94 @@ evdev_post_scroll(struct evdev_device *device,
|
||||||
const struct normalized_coords *trigger;
|
const struct normalized_coords *trigger;
|
||||||
struct normalized_coords event;
|
struct normalized_coords event;
|
||||||
|
|
||||||
if (!evdev_is_scrolling(device,
|
/* For signed scrolling, only accumulate axis selection motion
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
|
* until one axis is active. One axis active at a time.
|
||||||
device->scroll.buildup.y += delta->y;
|
*/
|
||||||
if (!evdev_is_scrolling(device,
|
if (signed_scroll(device)) {
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
|
if (!evdev_is_scrolling(device,
|
||||||
device->scroll.buildup.x += delta->x;
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL) &&
|
||||||
|
!evdev_is_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
|
||||||
|
device->scroll.buildup.y += delta->y;
|
||||||
|
/* Defer x axis selection to make y axis selection more robust.
|
||||||
|
* In the signed scrolling use model, the x axis is used
|
||||||
|
* infrequently enough so that incurring a timing delay isn't
|
||||||
|
* objectionable and it further reduces spurious x axis
|
||||||
|
* activation when y is intended.
|
||||||
|
*/
|
||||||
|
if (time > device->scroll.button_down_time + ms2us(250))
|
||||||
|
device->scroll.buildup.x += delta->x;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!evdev_is_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL))
|
||||||
|
device->scroll.buildup.y += delta->y;
|
||||||
|
if (!evdev_is_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL))
|
||||||
|
device->scroll.buildup.x += delta->x;
|
||||||
|
}
|
||||||
|
|
||||||
trigger = &device->scroll.buildup;
|
trigger = &device->scroll.buildup;
|
||||||
|
|
||||||
/* If we're not scrolling yet, use a distance trigger: moving
|
/* If we're not scrolling yet, use a distance trigger: moving
|
||||||
past a certain distance starts scrolling */
|
past a certain distance starts scrolling */
|
||||||
if (!evdev_is_scrolling(device,
|
if (!evdev_is_scrolling(device,
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
|
||||||
!evdev_is_scrolling(device,
|
!evdev_is_scrolling(device,
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||||
if (fabs(trigger->y) >= device->scroll.threshold)
|
if (signed_scroll(device)) {
|
||||||
evdev_start_scrolling(device,
|
/* Only one axis at a time with signed scrolling.
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
* Prefer y axis, predominant signed scroll use model, over x axis.
|
||||||
if (fabs(trigger->x) >= device->scroll.threshold)
|
* Use a 1/100" mode trigger threshold.
|
||||||
evdev_start_scrolling(device,
|
*/
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
double t = device->dpi/100.0;
|
||||||
|
if (fabs(trigger->y) - 0.25*fabs(trigger->x) > t)
|
||||||
|
evdev_start_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||||
|
else if (0.25*fabs(trigger->x) - fabs(trigger->y) > t)
|
||||||
|
evdev_start_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
||||||
|
} else {
|
||||||
|
if (fabs(trigger->y) >= device->scroll.threshold)
|
||||||
|
evdev_start_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||||
|
if (fabs(trigger->x) >= device->scroll.threshold)
|
||||||
|
evdev_start_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
||||||
|
}
|
||||||
/* We're already scrolling in one direction. Require some
|
/* We're already scrolling in one direction. Require some
|
||||||
trigger speed to start scrolling in the other direction */
|
* trigger speed to start scrolling in the other direction.
|
||||||
} else if (!evdev_is_scrolling(device,
|
* For the signed scrolling device, disallow dual axis scrolling
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
* on a single button.
|
||||||
if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
|
*/
|
||||||
evdev_start_scrolling(device,
|
} else if (!signed_scroll(device)) {
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
if (!evdev_is_scrolling(device,
|
||||||
} else if (!evdev_is_scrolling(device,
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
|
if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
|
||||||
if (fabs(delta->x) >= device->scroll.direction_lock_threshold)
|
evdev_start_scrolling(device,
|
||||||
evdev_start_scrolling(device,
|
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
} else if (!evdev_is_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) {
|
||||||
|
if (fabs(delta->x) >= device->scroll.direction_lock_threshold)
|
||||||
|
evdev_start_scrolling(device,
|
||||||
|
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
event = *delta;
|
event = *delta;
|
||||||
|
if (signed_scroll(device)) {
|
||||||
|
/* signed/freeform/scribble scrolling accumulator.
|
||||||
|
* Only one active axis per model invocation.
|
||||||
|
* Aaccumulate sign adjusted distance for both axes.
|
||||||
|
* Approximate euclidian distance with manhattan distance.
|
||||||
|
* Apply velocity ballistics to distance accumulated.
|
||||||
|
* Apply sign established at invocation.
|
||||||
|
*/
|
||||||
|
event.x = ballistics_gain(delta->x, device->dpi)*sign(trigger->x) +
|
||||||
|
ballistics_gain(delta->y, device->dpi)*sign(trigger->x);
|
||||||
|
event.y = ballistics_gain(delta->x, device->dpi)*sign(trigger->y) +
|
||||||
|
ballistics_gain(delta->y, device->dpi)*sign(trigger->y);
|
||||||
|
}
|
||||||
|
|
||||||
/* We use the trigger to enable, but the delta from this event for
|
/* We use the trigger to enable, but the delta from this event for
|
||||||
* the actual scroll movement. Otherwise we get a jump once
|
* the actual scroll movement. Otherwise we get a jump once
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue