mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-05-08 08:58:04 +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
188
src/evdev.c
188
src/evdev.c
|
|
@ -101,6 +101,17 @@ static const unsigned int well_known_keyboard_keys[] = {
|
|||
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
|
||||
parse_udev_flag(struct evdev_device *device,
|
||||
struct udev_device *udev_device,
|
||||
|
|
@ -249,9 +260,13 @@ evdev_button_scroll_button(struct evdev_device *device,
|
|||
}
|
||||
|
||||
if (is_press) {
|
||||
if (device->scroll.button < BTN_MOUSE + 5) {
|
||||
/* For mouse buttons 1-5 (0x110 to 0x114) we apply a timeout before scrolling
|
||||
* since the button could also be used for regular clicking. */
|
||||
if (!signed_scroll(device) && device->scroll.button < BTN_MOUSE + 5) {
|
||||
/* For mouse buttons 1-5 (0x110 to 0x114) apply a timeout before
|
||||
* 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;
|
||||
|
||||
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,
|
||||
flags);
|
||||
} 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
|
||||
* in order to provide immediate scrolling responsiveness. */
|
||||
device->scroll.button_scroll_state = BUTTONSCROLL_READY;
|
||||
}
|
||||
|
||||
device->scroll.button_down_time = time;
|
||||
evdev_log_debug(device, "btnscroll: down\n");
|
||||
} else {
|
||||
|
|
@ -291,16 +307,21 @@ evdev_button_scroll_button(struct evdev_device *device,
|
|||
case BUTTONSCROLL_READY:
|
||||
evdev_log_debug(device, "btnscroll: cancel\n");
|
||||
|
||||
/* If the button is released quickly enough or
|
||||
* without scroll events, emit the
|
||||
* button press/release events. */
|
||||
evdev_pointer_post_button(device,
|
||||
/* If the button is released quickly enough or without scroll events,
|
||||
* emit the button press/release events.
|
||||
* Don't do this for signed scrolling enabled devices as presumably
|
||||
* 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,
|
||||
LIBINPUT_BUTTON_STATE_PRESSED);
|
||||
evdev_pointer_post_button(device, time,
|
||||
evdev_pointer_post_button(device, time,
|
||||
device->scroll.button,
|
||||
LIBINPUT_BUTTON_STATE_RELEASED);
|
||||
}
|
||||
break;
|
||||
case BUTTONSCROLL_SCROLLING:
|
||||
evdev_log_debug(device, "btnscroll: up\n");
|
||||
|
|
@ -325,6 +346,19 @@ evdev_pointer_notify_button(struct evdev_device *device,
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -1063,10 +1097,10 @@ evdev_process_event(struct evdev_device *device, struct input_event *e)
|
|||
struct evdev_dispatch *dispatch = device->dispatch;
|
||||
uint64_t time = input_event_time(e);
|
||||
|
||||
#if 0
|
||||
#if 0
|
||||
evdev_print_event(device, e);
|
||||
#endif
|
||||
|
||||
|
||||
libinput_timer_flush(evdev_libinput_context(device), time);
|
||||
|
||||
dispatch->interface->process(dispatch, device, e, time);
|
||||
|
|
@ -1585,6 +1619,12 @@ evdev_read_model_flags(struct evdev_device *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;
|
||||
}
|
||||
|
||||
|
|
@ -2816,6 +2856,28 @@ evdev_start_scrolling(struct evdev_device *device,
|
|||
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
|
||||
evdev_post_scroll(struct evdev_device *device,
|
||||
uint64_t time,
|
||||
|
|
@ -2825,42 +2887,94 @@ evdev_post_scroll(struct evdev_device *device,
|
|||
const struct normalized_coords *trigger;
|
||||
struct normalized_coords event;
|
||||
|
||||
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;
|
||||
|
||||
/* For signed scrolling, only accumulate axis selection motion
|
||||
* until one axis is active. One axis active at a time.
|
||||
*/
|
||||
if (signed_scroll(device)) {
|
||||
if (!evdev_is_scrolling(device,
|
||||
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;
|
||||
|
||||
/* If we're not scrolling yet, use a distance trigger: moving
|
||||
past a certain distance starts scrolling */
|
||||
if (!evdev_is_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL) &&
|
||||
!evdev_is_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||
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);
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||
if (signed_scroll(device)) {
|
||||
/* Only one axis at a time with signed scrolling.
|
||||
* Prefer y axis, predominant signed scroll use model, over x axis.
|
||||
* Use a 1/100" mode trigger threshold.
|
||||
*/
|
||||
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
|
||||
trigger speed to start scrolling in the other direction */
|
||||
} else if (!evdev_is_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||
if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
|
||||
evdev_start_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||
} 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);
|
||||
* trigger speed to start scrolling in the other direction.
|
||||
* For the signed scrolling device, disallow dual axis scrolling
|
||||
* on a single button.
|
||||
*/
|
||||
} else if (!signed_scroll(device)) {
|
||||
if (!evdev_is_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) {
|
||||
if (fabs(delta->y) >= device->scroll.direction_lock_threshold)
|
||||
evdev_start_scrolling(device,
|
||||
LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL);
|
||||
} 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;
|
||||
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
|
||||
* the actual scroll movement. Otherwise we get a jump once
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue