libinput/src/evdev-fallback.c

1851 lines
48 KiB
C
Raw Normal View History

/*
* Copyright © 2010 Intel Corporation
* Copyright © 2013 Jonas Ådahl
* Copyright © 2013-2017 Red Hat, Inc.
* Copyright © 2017 James Ye <jye836@gmail.com>
*
* 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 <mtdev-plumbing.h>
#include "evdev-fallback.h"
#include "util-input-event.h"
static void
fallback_keyboard_notify_key(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
int key,
enum libinput_key_state state)
{
int down_count;
down_count = evdev_update_key_down_count(device, key, state);
if ((state == LIBINPUT_KEY_STATE_PRESSED && down_count == 1) ||
(state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0))
keyboard_notify_key(&device->base, time, key, state);
}
static void
fallback_lid_notify_toggle(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
if (dispatch->lid.is_closed ^ dispatch->lid.is_closed_client_state) {
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_LID,
dispatch->lid.is_closed);
dispatch->lid.is_closed_client_state = dispatch->lid.is_closed;
}
}
void
fallback_notify_physical_button(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time,
int button,
enum libinput_button_state state)
{
if (button == BTN_MIDDLE)
dispatch->wheel.is_inhibited = (state == LIBINPUT_BUTTON_STATE_PRESSED);
/* Lenovo TrackPoint Keyboard II sends its own scroll events when its
* trackpoint is moved while the middle button is pressed.
* Do not inhibit the scroll events.
* https://gitlab.freedesktop.org/libinput/libinput/-/issues/651
*/
if (evdev_device_has_model_quirk(device,
QUIRK_MODEL_LENOVO_TRACKPOINT_KEYBOARD_2))
dispatch->wheel.is_inhibited = false;
evdev_pointer_notify_physical_button(device, time, button, state);
}
static enum libinput_switch_state
fallback_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch,
enum libinput_switch sw)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
switch (sw) {
case LIBINPUT_SWITCH_TABLET_MODE:
break;
default:
/* Internal function only, so we can abort here */
abort();
}
return dispatch->tablet_mode.sw.state ?
LIBINPUT_SWITCH_STATE_ON :
LIBINPUT_SWITCH_STATE_OFF;
}
static inline void
normalize_delta(struct evdev_device *device,
const struct device_coords *delta,
struct normalized_coords *normalized)
{
normalized->x = delta->x * DEFAULT_MOUSE_DPI / (double)device->dpi;
normalized->y = delta->y * DEFAULT_MOUSE_DPI / (double)device->dpi;
}
static inline bool
post_trackpoint_scroll(struct evdev_device *device,
struct normalized_coords unaccel,
uint64_t time)
{
if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN)
return false;
switch(device->scroll.button_scroll_state) {
case BUTTONSCROLL_IDLE:
return false;
case BUTTONSCROLL_BUTTON_DOWN:
/* if the button is down but scroll is not active, we're within the
timeout where we swallow motion events but don't post
scroll buttons */
evdev_log_debug(device, "btnscroll: discarding\n");
return true;
case BUTTONSCROLL_READY:
device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING;
_fallthrough_;
case BUTTONSCROLL_SCROLLING:
evdev_post_scroll(device, time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
&unaccel);
return true;
}
assert(!"invalid scroll button state");
}
static inline bool
fallback_filter_defuzz_touch(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct mt_slot *slot)
{
struct device_coords point;
if (!dispatch->mt.want_hysteresis)
return false;
point = evdev_hysteresis(&slot->point,
&slot->hysteresis_center,
&dispatch->mt.hysteresis_margin);
slot->point = point;
if (point.x == slot->hysteresis_center.x &&
point.y == slot->hysteresis_center.y)
return true;
slot->hysteresis_center = point;
return false;
}
static inline void
fallback_rotate_relative(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
struct device_coords rel = dispatch->rel;
if (!device->base.config.rotation)
return;
/* loss of precision for non-90 degrees, but we only support 90 deg
* right now anyway */
matrix_mult_vec(&dispatch->rotation.matrix, &rel.x, &rel.y);
dispatch->rel = rel;
}
static void
fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct normalized_coords accel, unaccel;
struct device_float_coords raw;
if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
return;
fallback_rotate_relative(dispatch, device);
normalize_delta(device, &dispatch->rel, &unaccel);
raw.x = dispatch->rel.x;
raw.y = dispatch->rel.y;
dispatch->rel.x = 0;
dispatch->rel.y = 0;
/* Use unaccelerated deltas for pointing stick scroll */
if (post_trackpoint_scroll(device, unaccel, time))
return;
if (device->pointer.filter) {
/* Apply pointer acceleration. */
accel = filter_dispatch(device->pointer.filter,
&raw,
device,
time);
} else {
evdev_log_bug_libinput(device,
"accel filter missing\n");
accel = unaccel;
}
if (normalized_is_zero(accel) && normalized_is_zero(unaccel))
return;
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 };
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
struct wheel_v120 v120 = { 0.0, 0.0 };
if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
return;
if (!dispatch->wheel.emulate_hi_res_wheel &&
!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.emulate_hi_res_wheel = true;
dispatch->wheel.hi_res.x = dispatch->wheel.lo_res.x * 120;
dispatch->wheel.hi_res.y = dispatch->wheel.lo_res.y * 120;
}
if (dispatch->wheel.is_inhibited) {
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
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 (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
struct normalized_coords unaccel = { 0.0, 0.0 };
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.hi_res.y *= -1;
normalize_delta(device, &dispatch->wheel.hi_res, &unaccel);
evdev_post_scroll(device,
time,
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
&unaccel);
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.hi_res.x = 0;
dispatch->wheel.hi_res.y = 0;
return;
}
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
if (dispatch->wheel.hi_res.y != 0) {
int value = dispatch->wheel.hi_res.y;
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
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);
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.lo_res.y = 0;
}
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
if (dispatch->wheel.hi_res.x != 0) {
int value = dispatch->wheel.hi_res.x;
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
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);
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.lo_res.x = 0;
}
}
static void
fallback_flush_absolute_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
return;
point = dispatch->abs.point;
evdev_transform_absolute(device, &point);
pointer_notify_motion_absolute(base, time, &point);
}
static bool
fallback_flush_mt_down(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
struct device_coords point;
struct mt_slot *slot;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
slot = &dispatch->mt.slots[slot_idx];
if (slot->seat_slot != -1) {
evdev_log_bug_kernel(device,
"driver sent multiple touch down for the same slot");
return false;
}
seat_slot = ffs(~seat->slot_map) - 1;
slot->seat_slot = seat_slot;
if (seat_slot == -1)
return false;
seat->slot_map |= bit(seat_slot);
point = slot->point;
slot->hysteresis_center = point;
evdev_transform_absolute(device, &point);
touch_notify_touch_down(base, time, slot_idx, seat_slot,
&point);
return true;
}
static bool
fallback_flush_mt_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
struct mt_slot *slot;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
slot = &dispatch->mt.slots[slot_idx];
seat_slot = slot->seat_slot;
point = slot->point;
if (seat_slot == -1)
return false;
if (fallback_filter_defuzz_touch(dispatch, device, slot))
return false;
evdev_transform_absolute(device, &point);
touch_notify_touch_motion(base, time, slot_idx, seat_slot,
&point);
return true;
}
static bool
fallback_flush_mt_up(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
struct mt_slot *slot;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
slot = &dispatch->mt.slots[slot_idx];
seat_slot = slot->seat_slot;
slot->seat_slot = -1;
if (seat_slot == -1)
return false;
seat->slot_map &= ~bit(seat_slot);
touch_notify_touch_up(base, time, slot_idx, seat_slot);
return true;
}
static bool
fallback_flush_mt_cancel(struct fallback_dispatch *dispatch,
struct evdev_device *device,
int slot_idx,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
struct mt_slot *slot;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
slot = &dispatch->mt.slots[slot_idx];
seat_slot = slot->seat_slot;
slot->seat_slot = -1;
if (seat_slot == -1)
return false;
seat->slot_map &= ~bit(seat_slot);
touch_notify_touch_cancel(base, time, slot_idx, seat_slot);
return true;
}
static bool
fallback_flush_st_down(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
struct device_coords point;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
if (dispatch->abs.seat_slot != -1) {
evdev_log_bug_kernel(device,
"driver sent multiple touch down for the same slot");
return false;
}
seat_slot = ffs(~seat->slot_map) - 1;
dispatch->abs.seat_slot = seat_slot;
if (seat_slot == -1)
return false;
seat->slot_map |= bit(seat_slot);
point = dispatch->abs.point;
evdev_transform_absolute(device, &point);
touch_notify_touch_down(base, time, -1, seat_slot, &point);
return true;
}
static bool
fallback_flush_st_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct device_coords point;
int seat_slot;
point = dispatch->abs.point;
evdev_transform_absolute(device, &point);
seat_slot = dispatch->abs.seat_slot;
if (seat_slot == -1)
return false;
touch_notify_touch_motion(base, time, -1, seat_slot, &point);
return true;
}
static bool
fallback_flush_st_up(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
seat_slot = dispatch->abs.seat_slot;
dispatch->abs.seat_slot = -1;
if (seat_slot == -1)
return false;
seat->slot_map &= ~bit(seat_slot);
touch_notify_touch_up(base, time, -1, seat_slot);
return true;
}
static bool
fallback_flush_st_cancel(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
struct libinput_device *base = &device->base;
struct libinput_seat *seat = base->seat;
int seat_slot;
if (!(device->seat_caps & EVDEV_DEVICE_TOUCH))
return false;
seat_slot = dispatch->abs.seat_slot;
dispatch->abs.seat_slot = -1;
if (seat_slot == -1)
return false;
seat->slot_map &= ~bit(seat_slot);
touch_notify_touch_cancel(base, time, -1, seat_slot);
return true;
}
static void
fallback_process_touch_button(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time, int value)
{
dispatch->pending_event |= (value) ?
EVDEV_ABSOLUTE_TOUCH_DOWN :
EVDEV_ABSOLUTE_TOUCH_UP;
}
static inline void
fallback_process_key(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e, uint64_t time)
{
enum key_type type;
/* ignore kernel key repeat */
if (e->value == 2)
return;
if (e->code == BTN_TOUCH) {
if (!device->is_mt)
fallback_process_touch_button(dispatch,
device,
time,
e->value);
return;
}
type = get_key_type(e->code);
/* Ignore key release events from the kernel for keys that libinput
* never got a pressed event for or key presses for keys that we
* think are still down */
switch (type) {
case KEY_TYPE_NONE:
break;
case KEY_TYPE_KEY:
case KEY_TYPE_BUTTON:
if ((e->value && hw_is_key_down(dispatch, e->code)) ||
(e->value == 0 && !hw_is_key_down(dispatch, e->code)))
return;
dispatch->pending_event |= EVDEV_KEY;
break;
}
hw_set_key_down(dispatch, e->code, e->value);
switch (type) {
case KEY_TYPE_NONE:
break;
case KEY_TYPE_KEY:
fallback_keyboard_notify_key(
dispatch,
device,
time,
e->code,
e->value ? LIBINPUT_KEY_STATE_PRESSED :
LIBINPUT_KEY_STATE_RELEASED);
break;
case KEY_TYPE_BUTTON:
break;
}
}
static void
fallback_process_touch(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
if (e->code == ABS_MT_SLOT) {
if ((size_t)e->value >= dispatch->mt.slots_len) {
evdev_log_bug_libinput(device,
"exceeded slot count (%d of max %zd)\n",
e->value,
dispatch->mt.slots_len);
e->value = dispatch->mt.slots_len - 1;
}
dispatch->mt.slot = e->value;
return;
}
switch (e->code) {
case ABS_MT_TRACKING_ID:
if (e->value >= 0) {
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
slot->state = SLOT_STATE_BEGIN;
if (dispatch->mt.has_palm) {
int v;
v = libevdev_get_slot_value(device->evdev,
dispatch->mt.slot,
ABS_MT_TOOL_TYPE);
switch (v) {
case MT_TOOL_PALM:
/* new touch, no cancel needed */
slot->palm_state = PALM_WAS_PALM;
break;
default:
slot->palm_state = PALM_NONE;
break;
}
} else {
slot->palm_state = PALM_NONE;
}
} else {
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
slot->state = SLOT_STATE_END;
}
slot->dirty = true;
break;
case ABS_MT_POSITION_X:
evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->mt.slots[dispatch->mt.slot].point.x = e->value;
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
slot->dirty = true;
break;
case ABS_MT_POSITION_Y:
evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->mt.slots[dispatch->mt.slot].point.y = e->value;
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
slot->dirty = true;
break;
case ABS_MT_TOOL_TYPE:
/* The transitions matter - we (may) need to send a touch
* cancel event if we just switched to a palm touch. And the
* kernel may switch back to finger but we keep the touch as
* palm - but then we need to reset correctly on a new touch
* sequence.
*/
switch (e->value) {
case MT_TOOL_PALM:
if (slot->palm_state == PALM_NONE)
slot->palm_state = PALM_NEW;
break;
default:
if (slot->palm_state == PALM_IS_PALM)
slot->palm_state = PALM_WAS_PALM;
break;
}
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
slot->dirty = true;
break;
}
}
static inline void
fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e)
{
switch (e->code) {
case ABS_X:
evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->abs.point.x = e->value;
dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
break;
case ABS_Y:
evdev_device_check_abs_axis_range(device, e->code, e->value);
dispatch->abs.point.y = e->value;
dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
break;
}
}
static void
fallback_lid_keyboard_event(uint64_t time,
struct libinput_event *event,
void *data)
{
struct fallback_dispatch *dispatch = fallback_dispatch(data);
if (!dispatch->lid.is_closed)
return;
if (event->type != LIBINPUT_EVENT_KEYBOARD_KEY)
return;
if (dispatch->lid.reliability == RELIABILITY_WRITE_OPEN) {
int fd = libevdev_get_fd(dispatch->device->evdev);
int rc;
struct input_event ev[2];
ev[0] = input_event_init(0, EV_SW, SW_LID, 0);
ev[1] = input_event_init(0, EV_SYN, SYN_REPORT, 0);
rc = write(fd, ev, sizeof(ev));
if (rc < 0)
evdev_log_error(dispatch->device,
"failed to write SW_LID state (%s)",
strerror(errno));
/* In case write() fails, we sync the lid state manually
* regardless. */
}
/* Posting the event here means we preempt the keyboard events that
* caused us to wake up, so the lid event is always passed on before
* the key event.
*/
dispatch->lid.is_closed = false;
fallback_lid_notify_toggle(dispatch, dispatch->device, time);
}
static void
fallback_lid_toggle_keyboard_listener(struct fallback_dispatch *dispatch,
struct evdev_paired_keyboard *kbd,
bool is_closed)
{
assert(kbd->device);
libinput_device_remove_event_listener(&kbd->listener);
if (is_closed) {
libinput_device_add_event_listener(
&kbd->device->base,
&kbd->listener,
fallback_lid_keyboard_event,
dispatch);
} else {
libinput_device_init_event_listener(&kbd->listener);
}
}
static void
fallback_lid_toggle_keyboard_listeners(struct fallback_dispatch *dispatch,
bool is_closed)
{
struct evdev_paired_keyboard *kbd;
list_for_each(kbd, &dispatch->lid.paired_keyboard_list, link) {
if (!kbd->device)
continue;
fallback_lid_toggle_keyboard_listener(dispatch,
kbd,
is_closed);
}
}
static inline void
fallback_process_switch(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
enum libinput_switch_state state;
bool is_closed;
/* TODO: this should to move to handle_state */
switch (e->code) {
case SW_LID:
is_closed = !!e->value;
fallback_lid_toggle_keyboard_listeners(dispatch, is_closed);
if (dispatch->lid.is_closed == is_closed)
return;
dispatch->lid.is_closed = is_closed;
fallback_lid_notify_toggle(dispatch, device, time);
break;
case SW_TABLET_MODE:
if (dispatch->tablet_mode.sw.state == e->value)
return;
dispatch->tablet_mode.sw.state = e->value;
if (e->value)
state = LIBINPUT_SWITCH_STATE_ON;
else
state = LIBINPUT_SWITCH_STATE_OFF;
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_TABLET_MODE,
state);
break;
}
}
static inline bool
fallback_reject_relative(struct evdev_device *device,
const struct input_event *e,
uint64_t time)
{
if ((e->code == REL_X || e->code == REL_Y) &&
(device->seat_caps & EVDEV_DEVICE_POINTER) == 0) {
evdev_log_bug_libinput_ratelimit(device,
&device->nonpointer_rel_limit,
"REL_X/Y from a non-pointer device\n");
return true;
}
return false;
}
static inline void
fallback_process_relative(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e, uint64_t time)
{
if (fallback_reject_relative(device, e, time))
return;
switch (e->code) {
case REL_X:
dispatch->rel.x += e->value;
dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
break;
case REL_Y:
dispatch->rel.y += e->value;
dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
break;
case REL_WHEEL:
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.lo_res.y += e->value;
if (dispatch->wheel.emulate_hi_res_wheel)
dispatch->wheel.hi_res.y += e->value * 120;
dispatch->pending_event |= EVDEV_WHEEL;
break;
case REL_HWHEEL:
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->wheel.lo_res.x += e->value;
if (dispatch->wheel.emulate_hi_res_wheel)
dispatch->wheel.hi_res.x += e->value * 120;
dispatch->pending_event |= EVDEV_WHEEL;
break;
case REL_WHEEL_HI_RES:
dispatch->wheel.hi_res.y += e->value;
dispatch->wheel.hi_res_event_received = true;
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
dispatch->pending_event |= EVDEV_WHEEL;
break;
case REL_HWHEEL_HI_RES:
dispatch->wheel.hi_res.x += e->value;
dispatch->wheel.hi_res_event_received = true;
dispatch->pending_event |= EVDEV_WHEEL;
break;
}
}
static inline void
fallback_process_absolute(struct fallback_dispatch *dispatch,
struct evdev_device *device,
struct input_event *e,
uint64_t time)
{
if (device->is_mt) {
fallback_process_touch(dispatch, device, e, time);
} else {
fallback_process_absolute_motion(dispatch, device, e);
}
}
static inline bool
fallback_any_button_down(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
unsigned int button;
for (button = BTN_LEFT; button < BTN_JOYSTICK; button++) {
if (libevdev_has_event_code(device->evdev, EV_KEY, button) &&
hw_is_key_down(dispatch, button))
return true;
}
return false;
}
static inline bool
fallback_arbitrate_touch(struct fallback_dispatch *dispatch,
struct mt_slot *slot)
{
bool discard = false;
if (dispatch->arbitration.state == ARBITRATION_IGNORE_RECT &&
point_in_rect(&slot->point, &dispatch->arbitration.rect)) {
slot->palm_state = PALM_IS_PALM;
discard = true;
}
return discard;
}
static inline bool
fallback_flush_mt_events(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
bool sent = false;
for (size_t i = 0; i < dispatch->mt.slots_len; i++) {
struct mt_slot *slot = &dispatch->mt.slots[i];
if (!slot->dirty)
continue;
slot->dirty = false;
/* Any palm state other than PALM_NEW means we've either
* already cancelled the touch or the touch was never
2019-02-06 09:05:48 +10:00
* a finger anyway and we didn't send the begin.
*/
if (slot->palm_state == PALM_NEW) {
if (slot->state != SLOT_STATE_BEGIN)
sent = fallback_flush_mt_cancel(dispatch,
device,
i,
time);
slot->palm_state = PALM_IS_PALM;
} else if (slot->palm_state == PALM_NONE) {
switch (slot->state) {
case SLOT_STATE_BEGIN:
if (!fallback_arbitrate_touch(dispatch,
slot)) {
sent = fallback_flush_mt_down(dispatch,
device,
i,
time);
}
break;
case SLOT_STATE_UPDATE:
sent = fallback_flush_mt_motion(dispatch,
device,
i,
time);
break;
case SLOT_STATE_END:
sent = fallback_flush_mt_up(dispatch,
device,
i,
time);
break;
case SLOT_STATE_NONE:
break;
}
}
/* State machine continues independent of the palm state */
switch (slot->state) {
case SLOT_STATE_BEGIN:
slot->state = SLOT_STATE_UPDATE;
break;
case SLOT_STATE_UPDATE:
break;
case SLOT_STATE_END:
slot->state = SLOT_STATE_NONE;
break;
case SLOT_STATE_NONE:
/* touch arbitration may swallow the begin,
* so we may get updates for a touch still
* in NONE state */
break;
}
}
return sent;
}
static void
fallback_handle_state(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
bool need_touch_frame = false;
/* Relative motion */
if (dispatch->pending_event & EVDEV_RELATIVE_MOTION)
fallback_flush_relative_motion(dispatch, device, time);
/* Single touch or absolute pointer devices */
if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_DOWN) {
if (fallback_flush_st_down(dispatch, device, time))
need_touch_frame = true;
} else if (dispatch->pending_event & EVDEV_ABSOLUTE_MOTION) {
if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
if (fallback_flush_st_motion(dispatch,
device,
time))
need_touch_frame = true;
} else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
fallback_flush_absolute_motion(dispatch,
device,
time);
}
}
if (dispatch->pending_event & EVDEV_ABSOLUTE_TOUCH_UP) {
if (fallback_flush_st_up(dispatch, device, time))
need_touch_frame = true;
}
/* Multitouch devices */
if (dispatch->pending_event & EVDEV_ABSOLUTE_MT)
need_touch_frame = fallback_flush_mt_events(dispatch,
device,
time);
if (need_touch_frame)
touch_notify_frame(&device->base, time);
fallback_flush_wheels(dispatch, device, time);
/* Buttons and keys */
if (dispatch->pending_event & EVDEV_KEY) {
bool want_debounce = false;
for (unsigned int code = 0; code <= KEY_MAX; code++) {
if (!hw_key_has_changed(dispatch, code))
continue;
if (get_key_type(code) == KEY_TYPE_BUTTON) {
want_debounce = true;
break;
}
}
if (want_debounce)
fallback_debounce_handle_state(dispatch, time);
hw_key_update_last_state(dispatch);
}
dispatch->pending_event = EVDEV_NONE;
}
static void
fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
struct input_event *event,
uint64_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
if (dispatch->arbitration.in_arbitration)
return;
switch (event->type) {
case EV_REL:
fallback_process_relative(dispatch, device, event, time);
break;
case EV_ABS:
fallback_process_absolute(dispatch, device, event, time);
break;
case EV_KEY:
fallback_process_key(dispatch, device, event, time);
break;
case EV_SW:
fallback_process_switch(dispatch, device, event, time);
break;
case EV_SYN:
fallback_handle_state(dispatch, device, time);
break;
}
}
static void
cancel_touches(struct fallback_dispatch *dispatch,
struct evdev_device *device,
const struct device_coord_rect *rect,
uint64_t time)
{
unsigned int idx;
bool need_frame = false;
if (!rect || point_in_rect(&dispatch->abs.point, rect))
need_frame = fallback_flush_st_cancel(dispatch,
device,
time);
for (idx = 0; idx < dispatch->mt.slots_len; idx++) {
struct mt_slot *slot = &dispatch->mt.slots[idx];
if (slot->seat_slot == -1)
continue;
if ((!rect || point_in_rect(&slot->point, rect)) &&
fallback_flush_mt_cancel(dispatch, device, idx, time))
need_frame = true;
}
if (need_frame)
touch_notify_frame(&device->base, time);
}
static void
release_pressed_keys(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
{
int code;
for (code = 0; code < KEY_CNT; code++) {
int count = get_key_down_count(device, code);
if (count == 0)
continue;
if (count > 1) {
evdev_log_bug_libinput(device,
"key %d is down %d times.\n",
code,
count);
}
switch (get_key_type(code)) {
case KEY_TYPE_NONE:
break;
case KEY_TYPE_KEY:
fallback_keyboard_notify_key(
dispatch,
device,
time,
code,
LIBINPUT_KEY_STATE_RELEASED);
break;
case KEY_TYPE_BUTTON:
evdev_pointer_notify_button(
device,
time,
evdev_to_left_handed(device, code),
LIBINPUT_BUTTON_STATE_RELEASED);
break;
}
count = get_key_down_count(device, code);
if (count != 0) {
evdev_log_bug_libinput(device,
"releasing key %d failed.\n",
code);
break;
}
}
}
static void
fallback_return_to_neutral_state(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
struct libinput *libinput = evdev_libinput_context(device);
uint64_t time;
if ((time = libinput_now(libinput)) == 0)
return;
cancel_touches(dispatch, device, NULL, time);
release_pressed_keys(dispatch, device, time);
memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask));
memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask));
}
static void
fallback_interface_suspend(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
fallback_return_to_neutral_state(dispatch, device);
}
static void
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->debounce.timer);
libinput_timer_cancel(&dispatch->debounce.timer_short);
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener);
list_for_each_safe(kbd,
&dispatch->lid.paired_keyboard_list,
link) {
evdev_paired_keyboard_destroy(kbd);
}
}
static void
fallback_interface_sync_initial_state(struct evdev_device *device,
struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
uint64_t time = libinput_now(evdev_libinput_context(device));
if (device->tags & EVDEV_TAG_LID_SWITCH) {
struct libevdev *evdev = device->evdev;
dispatch->lid.is_closed = libevdev_get_event_value(evdev,
EV_SW,
SW_LID);
dispatch->lid.is_closed_client_state = false;
/* For the initial state sync, we depend on whether the lid switch
* is reliable. If we know it's reliable, we sync as expected.
* If we're not sure, we ignore the initial state and only sync on
* the first future lid close event. Laptops with a broken switch
* that always have the switch in 'on' state thus don't mess up our
* touchpad.
*/
if (dispatch->lid.is_closed &&
dispatch->lid.reliability == RELIABILITY_RELIABLE) {
fallback_lid_notify_toggle(dispatch, device, time);
}
}
if (dispatch->tablet_mode.sw.state) {
switch_notify_toggle(&device->base,
time,
LIBINPUT_SWITCH_TABLET_MODE,
LIBINPUT_SWITCH_STATE_ON);
}
}
static void
fallback_interface_update_rect(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
const struct phys_rect *phys_rect,
uint64_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
struct device_coord_rect rect;
assert(phys_rect);
/* Existing touches do not change, we just update the rect and only
* new touches in these areas will be ignored. If you want to paint
* over your finger, be my guest. */
rect = evdev_phys_rect_to_units(device, phys_rect);
dispatch->arbitration.rect = rect;
}
static void
fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
struct evdev_device *device,
enum evdev_arbitration_state which,
const struct phys_rect *phys_rect,
uint64_t time)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
struct device_coord_rect rect = {0};
if (which == dispatch->arbitration.state)
return;
switch (which) {
case ARBITRATION_NOT_ACTIVE:
/* if in-kernel arbitration is in use and there is a touch
* and a pen in proximity, lifting the pen out of proximity
* causes a touch begin for the touch. On a hand-lift the
* proximity out precedes the touch up by a few ms, so we
* get what looks like a tap. Fix this by delaying
* arbitration by just a little bit so that any touch in
* event is caught as palm touch. */
libinput_timer_set(&dispatch->arbitration.arbitration_timer,
time + ms2us(90));
break;
case ARBITRATION_IGNORE_RECT:
assert(phys_rect);
rect = evdev_phys_rect_to_units(device, phys_rect);
cancel_touches(dispatch, device, &rect, time);
dispatch->arbitration.rect = rect;
break;
case ARBITRATION_IGNORE_ALL:
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
fallback_return_to_neutral_state(dispatch, device);
dispatch->arbitration.in_arbitration = true;
break;
}
dispatch->arbitration.state = which;
}
static void
fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
libinput_timer_destroy(&dispatch->debounce.timer);
libinput_timer_destroy(&dispatch->debounce.timer_short);
free(dispatch->mt.slots);
free(dispatch);
}
static void
fallback_lid_pair_keyboard(struct evdev_device *lid_switch,
struct evdev_device *keyboard)
{
struct fallback_dispatch *dispatch =
fallback_dispatch(lid_switch->dispatch);
struct evdev_paired_keyboard *kbd;
size_t count = 0;
if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 ||
(lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
return;
if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0)
return;
list_for_each(kbd, &dispatch->lid.paired_keyboard_list, link) {
count++;
if (count > 3) {
evdev_log_info(lid_switch,
"lid: too many internal keyboards\n");
break;
}
}
kbd = zalloc(sizeof(*kbd));
kbd->device = keyboard;
libinput_device_init_event_listener(&kbd->listener);
list_insert(&dispatch->lid.paired_keyboard_list, &kbd->link);
evdev_log_debug(lid_switch,
"lid: keyboard paired with %s<->%s\n",
lid_switch->devname,
keyboard->devname);
/* We need to init the event listener now only if the
* reported state is closed. */
if (dispatch->lid.is_closed)
fallback_lid_toggle_keyboard_listener(dispatch,
kbd,
dispatch->lid.is_closed);
}
static void
fallback_resume(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
if (dispatch->base.sendevents.current_mode ==
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED)
return;
evdev_device_resume(device);
}
static void
fallback_suspend(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
evdev_device_suspend(device);
}
static void
fallback_tablet_mode_switch_event(uint64_t time,
struct libinput_event *event,
void *data)
{
struct fallback_dispatch *dispatch = data;
struct evdev_device *device = dispatch->device;
struct libinput_event_switch *swev;
if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE)
return;
swev = libinput_event_get_switch_event(event);
if (libinput_event_switch_get_switch(swev) !=
LIBINPUT_SWITCH_TABLET_MODE)
return;
switch (libinput_event_switch_get_switch_state(swev)) {
case LIBINPUT_SWITCH_STATE_OFF:
fallback_resume(dispatch, device);
evdev_log_debug(device, "tablet-mode: resuming device\n");
break;
case LIBINPUT_SWITCH_STATE_ON:
fallback_suspend(dispatch, device);
evdev_log_debug(device, "tablet-mode: suspending device\n");
break;
}
}
static void
fallback_pair_tablet_mode(struct evdev_device *keyboard,
struct evdev_device *tablet_mode_switch)
{
struct fallback_dispatch *dispatch =
fallback_dispatch(keyboard->dispatch);
if ((keyboard->tags & EVDEV_TAG_EXTERNAL_KEYBOARD))
return;
if ((keyboard->tags & EVDEV_TAG_TRACKPOINT)) {
if (keyboard->tags & EVDEV_TAG_EXTERNAL_MOUSE)
return;
/* This filters out all internal keyboard-like devices (Video
* Switch) */
} else if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0) {
return;
}
if (evdev_device_has_model_quirk(keyboard,
QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
return;
if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
return;
if (dispatch->tablet_mode.other.sw_device)
return;
evdev_log_debug(keyboard,
"tablet-mode: paired %s<->%s\n",
keyboard->devname,
tablet_mode_switch->devname);
libinput_device_add_event_listener(&tablet_mode_switch->base,
&dispatch->tablet_mode.other.listener,
fallback_tablet_mode_switch_event,
dispatch);
dispatch->tablet_mode.other.sw_device = tablet_mode_switch;
if (evdev_device_switch_get_state(tablet_mode_switch,
LIBINPUT_SWITCH_TABLET_MODE)
== LIBINPUT_SWITCH_STATE_ON) {
evdev_log_debug(keyboard, "tablet-mode: suspending device\n");
fallback_suspend(dispatch, keyboard);
}
}
static void
fallback_interface_device_added(struct evdev_device *device,
struct evdev_device *added_device)
{
fallback_lid_pair_keyboard(device, added_device);
fallback_pair_tablet_mode(device, added_device);
}
static void
fallback_interface_device_removed(struct evdev_device *device,
struct evdev_device *removed_device)
{
struct fallback_dispatch *dispatch =
fallback_dispatch(device->dispatch);
struct evdev_paired_keyboard *kbd;
list_for_each_safe(kbd,
&dispatch->lid.paired_keyboard_list,
link) {
if (!kbd->device)
continue;
if (kbd->device != removed_device)
continue;
evdev_paired_keyboard_destroy(kbd);
}
if (removed_device == dispatch->tablet_mode.other.sw_device) {
libinput_device_remove_event_listener(
&dispatch->tablet_mode.other.listener);
libinput_device_init_event_listener(
&dispatch->tablet_mode.other.listener);
dispatch->tablet_mode.other.sw_device = NULL;
}
}
struct evdev_dispatch_interface fallback_interface = {
.process = fallback_interface_process,
.suspend = fallback_interface_suspend,
.remove = fallback_interface_remove,
.destroy = fallback_interface_destroy,
.device_added = fallback_interface_device_added,
.device_removed = fallback_interface_device_removed,
.device_suspended = fallback_interface_device_removed, /* treat as remove */
.device_resumed = fallback_interface_device_added, /* treat as add */
.post_added = fallback_interface_sync_initial_state,
.touch_arbitration_toggle = fallback_interface_toggle_touch,
.touch_arbitration_update_rect = fallback_interface_update_rect,
.get_switch_state = fallback_interface_get_switch_state,
};
static void
fallback_change_to_left_handed(struct evdev_device *device)
{
struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
if (device->left_handed.want_enabled == device->left_handed.enabled)
return;
if (fallback_any_button_down(dispatch, device))
return;
device->left_handed.enabled = device->left_handed.want_enabled;
}
static void
fallback_change_scroll_method(struct evdev_device *device)
{
struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
if (device->scroll.want_method == device->scroll.method &&
device->scroll.want_button == device->scroll.button &&
device->scroll.want_lock_enabled == device->scroll.lock_enabled)
return;
if (fallback_any_button_down(dispatch, device))
return;
device->scroll.method = device->scroll.want_method;
device->scroll.button = device->scroll.want_button;
device->scroll.lock_enabled = device->scroll.want_lock_enabled;
evdev_set_button_scroll_lock_enabled(device, device->scroll.lock_enabled);
}
static int
fallback_rotation_config_is_available(struct libinput_device *device)
{
/* This function only gets called when we support rotation */
return 1;
}
static enum libinput_config_status
fallback_rotation_config_set_angle(struct libinput_device *libinput_device,
unsigned int degrees_cw)
{
struct evdev_device *device = evdev_device(libinput_device);
struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
dispatch->rotation.angle = degrees_cw;
matrix_init_rotate(&dispatch->rotation.matrix, degrees_cw);
return LIBINPUT_CONFIG_STATUS_SUCCESS;
}
static unsigned int
fallback_rotation_config_get_angle(struct libinput_device *libinput_device)
{
struct evdev_device *device = evdev_device(libinput_device);
struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
return dispatch->rotation.angle;
}
static unsigned int
fallback_rotation_config_get_default_angle(struct libinput_device *device)
{
return 0;
}
static void
fallback_init_rotation(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
if ((device->model_flags & EVDEV_MODEL_TRACKBALL) == 0)
return;
dispatch->rotation.config.is_available = fallback_rotation_config_is_available;
dispatch->rotation.config.set_angle = fallback_rotation_config_set_angle;
dispatch->rotation.config.get_angle = fallback_rotation_config_get_angle;
dispatch->rotation.config.get_default_angle = fallback_rotation_config_get_default_angle;
dispatch->rotation.is_enabled = false;
matrix_init_identity(&dispatch->rotation.matrix);
device->base.config.rotation = &dispatch->rotation.config;
}
static inline int
fallback_dispatch_init_slots(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
struct mt_slot *slots;
int num_slots;
int active_slot;
int slot;
if (evdev_is_fake_mt_device(device) ||
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_X) ||
!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_POSITION_Y))
return 0;
/* We only handle the slotted Protocol B in libinput.
Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT
require mtdev for conversion. */
if (evdev_need_mtdev(device)) {
device->mtdev = mtdev_new_open(device->fd);
if (!device->mtdev)
return -1;
/* pick 10 slots as default for type A
devices. */
num_slots = 10;
active_slot = device->mtdev->caps.slot.value;
} else {
num_slots = libevdev_get_num_slots(device->evdev);
active_slot = libevdev_get_current_slot(evdev);
}
slots = zalloc(num_slots * sizeof(struct mt_slot));
for (slot = 0; slot < num_slots; ++slot) {
slots[slot].seat_slot = -1;
if (evdev_need_mtdev(device))
continue;
slots[slot].point.x = libevdev_get_slot_value(evdev,
slot,
ABS_MT_POSITION_X);
slots[slot].point.y = libevdev_get_slot_value(evdev,
slot,
ABS_MT_POSITION_Y);
}
dispatch->mt.slots = slots;
dispatch->mt.slots_len = num_slots;
dispatch->mt.slot = active_slot;
dispatch->mt.has_palm = libevdev_has_event_code(evdev,
EV_ABS,
ABS_MT_TOOL_TYPE);
if (device->abs.absinfo_x->fuzz || device->abs.absinfo_y->fuzz) {
dispatch->mt.want_hysteresis = true;
dispatch->mt.hysteresis_margin.x = device->abs.absinfo_x->fuzz/2;
dispatch->mt.hysteresis_margin.y = device->abs.absinfo_y->fuzz/2;
}
return 0;
}
static inline void
fallback_dispatch_init_rel(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
dispatch->rel.x = 0;
dispatch->rel.y = 0;
}
static inline void
fallback_dispatch_init_abs(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
if (!libevdev_has_event_code(device->evdev, EV_ABS, ABS_X))
return;
dispatch->abs.point.x = device->abs.absinfo_x->value;
dispatch->abs.point.y = device->abs.absinfo_y->value;
dispatch->abs.seat_slot = -1;
evdev_device_init_abs_range_warnings(device);
}
static inline void
fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
int val;
list_init(&dispatch->lid.paired_keyboard_list);
if (device->tags & EVDEV_TAG_LID_SWITCH) {
dispatch->lid.reliability = evdev_read_switch_reliability_prop(device);
dispatch->lid.is_closed = false;
}
if (device->tags & EVDEV_TAG_TABLET_MODE_SWITCH) {
val = libevdev_get_event_value(device->evdev,
EV_SW,
SW_TABLET_MODE);
dispatch->tablet_mode.sw.state = val;
}
libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener);
}
static void
fallback_arbitration_timeout(uint64_t now, void *data)
{
struct fallback_dispatch *dispatch = data;
if (dispatch->arbitration.in_arbitration)
dispatch->arbitration.in_arbitration = false;
}
static void
fallback_init_arbitration(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
char timer_name[64];
snprintf(timer_name,
sizeof(timer_name),
"%s arbitration",
evdev_device_get_sysname(device));
libinput_timer_init(&dispatch->arbitration.arbitration_timer,
evdev_libinput_context(device),
timer_name,
fallback_arbitration_timeout,
dispatch);
dispatch->arbitration.in_arbitration = false;
}
struct evdev_dispatch *
fallback_dispatch_create(struct libinput_device *libinput_device)
{
struct evdev_device *device = evdev_device(libinput_device);
struct fallback_dispatch *dispatch;
dispatch = zalloc(sizeof *dispatch);
dispatch->device = evdev_device(libinput_device);
dispatch->base.dispatch_type = DISPATCH_FALLBACK;
dispatch->base.interface = &fallback_interface;
dispatch->pending_event = EVDEV_NONE;
list_init(&dispatch->lid.paired_keyboard_list);
fallback_dispatch_init_rel(dispatch, device);
fallback_dispatch_init_abs(dispatch, device);
if (fallback_dispatch_init_slots(dispatch, device) == -1) {
free(dispatch);
return NULL;
}
fallback_dispatch_init_switch(dispatch, device);
if (device->left_handed.want_enabled)
evdev_init_left_handed(device,
fallback_change_to_left_handed);
if (device->scroll.want_button)
evdev_init_button_scroll(device,
fallback_change_scroll_method);
if (device->scroll.natural_scrolling_enabled)
evdev_init_natural_scroll(device);
evdev_init_calibration(device, &dispatch->calibration);
evdev_init_sendevents(device, &dispatch->base);
fallback_init_rotation(dispatch, device);
/* BTN_MIDDLE is set on mice even when it's not present. So
* we can only use the absence of BTN_MIDDLE to mean something, i.e.
* we enable it by default on anything that only has L&R.
* If we have L&R and no middle, we don't expose it as config
* option */
if (libevdev_has_event_code(device->evdev, EV_KEY, BTN_LEFT) &&
libevdev_has_event_code(device->evdev, EV_KEY, BTN_RIGHT)) {
bool has_middle = libevdev_has_event_code(device->evdev,
EV_KEY,
BTN_MIDDLE);
bool want_config = has_middle;
bool enable_by_default = !has_middle;
evdev_init_middlebutton(device,
enable_by_default,
want_config);
}
High-resolution scroll wheel support Starting with kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES. Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. This commit adds a new API for scroll events. Three new event types that encode the axis source in the event type name and a new API to get a normalized-to-120 value that also used by Windows and the kernel (each multiple of 120 represents a logical scroll click). This addresses a main shortcoming with the existing API - it was unreliable to calculate the click angle based on the axis value+discrete events and thus any caller using the axis value alone would be left with some ambiguity. With the v120 API it's now possible to (usually) calculate the click angle, but more importantly it provides the simplest hw-independent way of scrolling by a click or a fraction of a click. A new event type is required, the only way to integrate the v120 value otherwise was to start sending events with a discrete value of 0. This would break existing xf86-input-libinput (divide by zero, fixed in 0.28.2) and weston (general confusion). mutter, kwin are unaffected. With the new API, the old POINTER_AXIS event are deprecated - callers should use the new API where available and discard any POINTER_AXIS events. Notable: REL_WHEEL/REL_HWHEEL are emulated by the kernel but there's no guarantee that they'll come every accumulated 120 values, e.g. Logitech mice often send events that don't add up to 120 per detent. We use the kernel's wheel click emulation instead of doing our own. libinput guarantees high-resolution events even on pre-5.0 kernels. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Signed-off-by: José Expósito <jose.exposito89@gmail.com>
2018-11-22 10:24:54 +10:00
/* On kernel < 5.0 we need to emulate high-resolution
wheel scroll events */
if ((libevdev_has_event_code(device->evdev,
EV_REL,
REL_WHEEL) &&
!libevdev_has_event_code(device->evdev,
EV_REL,
REL_WHEEL_HI_RES)) ||
(libevdev_has_event_code(device->evdev,
EV_REL,
REL_HWHEEL) &&
!libevdev_has_event_code(device->evdev,
EV_REL,
REL_HWHEEL_HI_RES)))
dispatch->wheel.emulate_hi_res_wheel = true;
fallback_init_debounce(dispatch);
fallback_init_arbitration(dispatch, device);
return &dispatch->base;
}