2017-09-19 13:57:50 +10:00
|
|
|
/*
|
|
|
|
|
* 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"
|
|
|
|
|
|
2020-01-19 16:01:57 -06:00
|
|
|
#include "util-input-event.h"
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2017-11-13 09:30:30 +10:00
|
|
|
#include "evdev-fallback.h"
|
2025-07-01 16:30:11 +10:00
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
|
|
|
|
fallback_keyboard_notify_key(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
uint64_t time,
|
2025-06-05 13:53:55 +10:00
|
|
|
evdev_usage_t usage,
|
2017-09-19 13:57:50 +10:00
|
|
|
enum libinput_key_state state)
|
|
|
|
|
{
|
|
|
|
|
int down_count;
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
down_count = evdev_update_key_down_count(device, usage, state);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
if ((state == LIBINPUT_KEY_STATE_PRESSED && down_count == 1) ||
|
2025-06-06 10:48:46 +10:00
|
|
|
(state == LIBINPUT_KEY_STATE_RELEASED && down_count == 0)) {
|
|
|
|
|
keyboard_notify_key(&device->base,
|
|
|
|
|
time,
|
|
|
|
|
keycode_from_usage(usage),
|
|
|
|
|
state);
|
|
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-13 20:06:06 +01:00
|
|
|
void
|
|
|
|
|
fallback_notify_physical_button(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
uint64_t time,
|
2025-06-05 13:53:55 +10:00
|
|
|
evdev_usage_t button,
|
2021-03-13 20:06:06 +01:00
|
|
|
enum libinput_button_state state)
|
|
|
|
|
{
|
|
|
|
|
evdev_pointer_notify_physical_button(device, time, button, state);
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static enum libinput_switch_state
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch,
|
|
|
|
|
enum libinput_switch sw)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON
|
2017-09-19 13:57:50 +10:00
|
|
|
: LIBINPUT_SWITCH_STATE_OFF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
2022-09-05 09:15:39 +10:00
|
|
|
post_button_scroll(struct evdev_device *device,
|
2022-09-05 09:26:21 +10:00
|
|
|
struct device_float_coords raw,
|
2022-09-05 09:15:39 +10:00
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
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
|
2019-02-12 08:51:32 +10:00
|
|
|
timeout where we swallow motion events but don't post
|
|
|
|
|
scroll buttons */
|
2017-09-19 13:57:50 +10:00
|
|
|
evdev_log_debug(device, "btnscroll: discarding\n");
|
|
|
|
|
return true;
|
|
|
|
|
case BUTTONSCROLL_READY:
|
|
|
|
|
device->scroll.button_scroll_state = BUTTONSCROLL_SCROLLING;
|
2021-07-22 15:00:32 +10:00
|
|
|
_fallthrough_;
|
2017-09-19 13:57:50 +10:00
|
|
|
case BUTTONSCROLL_SCROLLING: {
|
2022-09-05 09:26:21 +10:00
|
|
|
const struct normalized_coords normalized =
|
2023-02-18 21:12:13 +02:00
|
|
|
filter_dispatch_scroll(device->pointer.filter,
|
|
|
|
|
&raw,
|
|
|
|
|
device,
|
|
|
|
|
time);
|
2017-09-19 13:57:50 +10:00
|
|
|
evdev_post_scroll(device,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
|
2022-09-05 09:26:21 +10:00
|
|
|
&normalized);
|
|
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
|
2018-03-01 17:03:28 +08:00
|
|
|
point = evdev_hysteresis(&slot->point,
|
|
|
|
|
&slot->hysteresis_center,
|
|
|
|
|
&dispatch->mt.hysteresis_margin);
|
2018-03-09 09:41:57 +10:00
|
|
|
slot->point = point;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-03-09 09:41:57 +10:00
|
|
|
if (point.x == slot->hysteresis_center.x &&
|
|
|
|
|
point.y == slot->hysteresis_center.y)
|
2017-09-19 13:57:50 +10:00
|
|
|
return true;
|
|
|
|
|
|
2018-03-09 09:41:57 +10:00
|
|
|
slot->hysteresis_center = point;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-05 22:57:51 +00:00
|
|
|
static inline struct device_float_coords
|
2017-09-19 13:57:50 +10:00
|
|
|
fallback_rotate_relative(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2022-12-05 22:57:51 +00:00
|
|
|
struct device_float_coords rel = { dispatch->rel.x, dispatch->rel.y };
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
if (!device->base.config.rotation)
|
2022-12-05 22:57:51 +00:00
|
|
|
return rel;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2022-12-05 22:57:51 +00:00
|
|
|
matrix_mult_vec_double(&dispatch->rotation.matrix, &rel.x, &rel.y);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2022-12-05 22:57:51 +00:00
|
|
|
return rel;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fallback_flush_relative_motion(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *base = &device->base;
|
2022-09-05 09:26:21 +10:00
|
|
|
struct normalized_coords accel;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
if (!(device->seat_caps & EVDEV_DEVICE_POINTER))
|
|
|
|
|
return;
|
|
|
|
|
|
2022-12-05 22:57:51 +00:00
|
|
|
struct device_float_coords raw = fallback_rotate_relative(dispatch, device);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
dispatch->rel.x = 0;
|
|
|
|
|
dispatch->rel.y = 0;
|
|
|
|
|
|
|
|
|
|
/* Use unaccelerated deltas for pointing stick scroll */
|
2022-09-05 09:26:21 +10:00
|
|
|
if (post_button_scroll(device, raw, time))
|
2017-09-19 13:57:50 +10:00
|
|
|
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");
|
2022-09-05 09:26:21 +10:00
|
|
|
accel.x = accel.y = 0;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2022-09-05 09:26:21 +10:00
|
|
|
if (normalized_is_zero(accel))
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
pointer_notify_motion(base, time, &accel, &raw);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 12:35:08 +02:00
|
|
|
static void
|
|
|
|
|
fallback_flush_wheels(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (!libinput_device_has_capability(&device->base, LIBINPUT_DEVICE_CAP_POINTER))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* This mouse has a trackstick instead of a mouse wheel and sends
|
|
|
|
|
* trackstick data via REL_WHEEL. Normalize it like normal x/y coordinates.
|
|
|
|
|
*/
|
|
|
|
|
if (device->model_flags & EVDEV_MODEL_LENOVO_SCROLLPOINT) {
|
|
|
|
|
const struct device_float_coords raw = {
|
|
|
|
|
.x = dispatch->wheel.lo_res.x,
|
|
|
|
|
.y = dispatch->wheel.lo_res.y * -1,
|
|
|
|
|
};
|
|
|
|
|
const struct normalized_coords normalized =
|
|
|
|
|
filter_dispatch_scroll(device->pointer.filter,
|
|
|
|
|
&raw,
|
|
|
|
|
device,
|
|
|
|
|
time);
|
|
|
|
|
evdev_post_scroll(device,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS,
|
|
|
|
|
&normalized);
|
|
|
|
|
dispatch->wheel.hi_res.x = 0;
|
|
|
|
|
dispatch->wheel.hi_res.y = 0;
|
|
|
|
|
dispatch->wheel.lo_res.x = 0;
|
|
|
|
|
dispatch->wheel.lo_res.y = 0;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-17 17:29:01 +03:00
|
|
|
/* High-resolution wheel events */
|
|
|
|
|
if (dispatch->wheel.hi_res.x != 0 || dispatch->wheel.hi_res.y != 0) {
|
|
|
|
|
const struct device_float_coords v120_unaccelerated = {
|
|
|
|
|
.x = dispatch->wheel.hi_res.x,
|
|
|
|
|
.y = -1 * dispatch->wheel.hi_res.y,
|
|
|
|
|
};
|
|
|
|
|
const struct normalized_coords v120_accelerated =
|
|
|
|
|
device->pointer.filter
|
|
|
|
|
? filter_dispatch_scroll(device->pointer.filter,
|
|
|
|
|
&v120_unaccelerated,
|
|
|
|
|
device,
|
|
|
|
|
time)
|
|
|
|
|
: (const struct normalized_coords){
|
|
|
|
|
.x = v120_unaccelerated.x,
|
|
|
|
|
.y = v120_unaccelerated.y
|
|
|
|
|
};
|
|
|
|
|
/* Truncate the fractional part when converting floating-point to
|
|
|
|
|
integer. This is acceptable because the v120 unit maps one logical
|
|
|
|
|
click to 120 units; values are effectively measured in 1/120 of a
|
|
|
|
|
click, so truncation does not lose meaningful resolution. */
|
|
|
|
|
const struct wheel_v120 v120 = {
|
|
|
|
|
.x = v120_accelerated.x,
|
|
|
|
|
.y = v120_accelerated.y,
|
|
|
|
|
};
|
|
|
|
|
const struct normalized_coords wheel_degrees = {
|
|
|
|
|
.x = v120.x / 120.0 * device->scroll.wheel_click_angle.x,
|
|
|
|
|
.y = v120.y / 120.0 * device->scroll.wheel_click_angle.y,
|
|
|
|
|
};
|
|
|
|
|
if (v120.x != 0) {
|
|
|
|
|
evdev_notify_axis_wheel(
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
|
|
|
|
&wheel_degrees,
|
|
|
|
|
&v120);
|
|
|
|
|
}
|
|
|
|
|
if (v120.y != 0) {
|
|
|
|
|
evdev_notify_axis_wheel(
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
|
|
|
|
&wheel_degrees,
|
|
|
|
|
&v120);
|
|
|
|
|
}
|
2025-06-23 12:35:08 +02:00
|
|
|
dispatch->wheel.hi_res.x = 0;
|
2025-09-17 17:29:01 +03:00
|
|
|
dispatch->wheel.hi_res.y = 0;
|
2025-06-23 12:35:08 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-17 17:29:01 +03:00
|
|
|
/* Low-resolution wheel events */
|
|
|
|
|
if (dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0) {
|
|
|
|
|
/* Do not accelerate low-resolution wheel events: they use different
|
|
|
|
|
units than high-resolution events and should not be accelerated with
|
|
|
|
|
the same function. */
|
|
|
|
|
const struct discrete_coords discrete = {
|
|
|
|
|
.x = dispatch->wheel.lo_res.x,
|
|
|
|
|
.y = -1 * dispatch->wheel.lo_res.y,
|
|
|
|
|
};
|
|
|
|
|
const struct normalized_coords wheel_degrees = {
|
|
|
|
|
.x = discrete.x * device->scroll.wheel_click_angle.x,
|
|
|
|
|
.y = discrete.y * device->scroll.wheel_click_angle.y,
|
|
|
|
|
};
|
|
|
|
|
if (discrete.x != 0) {
|
|
|
|
|
evdev_notify_axis_legacy_wheel(
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
bit(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
|
|
|
|
|
&wheel_degrees,
|
|
|
|
|
&discrete);
|
|
|
|
|
}
|
|
|
|
|
if (discrete.y != 0) {
|
|
|
|
|
evdev_notify_axis_legacy_wheel(
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
bit(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
|
|
|
|
|
&wheel_degrees,
|
|
|
|
|
&discrete);
|
|
|
|
|
}
|
2025-06-23 12:35:08 +02:00
|
|
|
dispatch->wheel.lo_res.x = 0;
|
2025-09-17 17:29:01 +03:00
|
|
|
dispatch->wheel.lo_res.y = 0;
|
2025-06-23 12:35:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map |= bit(seat_slot);
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map &= ~bit(seat_slot);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
touch_notify_touch_up(base, time, slot_idx, seat_slot);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-13 16:08:54 +10:00
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map &= ~bit(seat_slot);
|
2018-07-13 16:08:54 +10:00
|
|
|
|
|
|
|
|
touch_notify_touch_cancel(base, time, slot_idx, seat_slot);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map |= bit(seat_slot);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map &= ~bit(seat_slot);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
touch_notify_touch_up(base, time, -1, seat_slot);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-20 14:09:31 +10:00
|
|
|
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;
|
|
|
|
|
|
2019-04-10 09:55:25 -07:00
|
|
|
seat->slot_map &= ~bit(seat_slot);
|
2018-09-20 14:09:31 +10:00
|
|
|
|
|
|
|
|
touch_notify_touch_cancel(base, time, -1, seat_slot);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
|
|
|
|
fallback_process_touch_button(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
uint64_t time,
|
|
|
|
|
int value)
|
|
|
|
|
{
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |=
|
2017-09-19 13:57:50 +10:00
|
|
|
(value) ? EVDEV_ABSOLUTE_TOUCH_DOWN : EVDEV_ABSOLUTE_TOUCH_UP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
fallback_process_key(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e,
|
|
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
/* ignore kernel key repeat */
|
|
|
|
|
if (e->value == 2)
|
|
|
|
|
return;
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
if (evdev_usage_eq(e->usage, EVDEV_BTN_TOUCH)) {
|
2017-09-19 13:57:50 +10:00
|
|
|
if (!device->is_mt)
|
|
|
|
|
fallback_process_touch_button(dispatch, device, time, e->value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 15:56:47 +10:00
|
|
|
bool is_button = evdev_usage_is_button(e->usage);
|
|
|
|
|
bool is_key = evdev_usage_is_key(e->usage);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
/* 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 */
|
2025-06-16 15:56:47 +10:00
|
|
|
if (is_button || is_key) {
|
2025-06-05 13:53:55 +10:00
|
|
|
if ((e->value && hw_is_key_down(dispatch, e->usage)) ||
|
|
|
|
|
(e->value == 0 && !hw_is_key_down(dispatch, e->usage)))
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
2017-11-13 09:33:50 +10:00
|
|
|
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_KEY;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
hw_set_key_down(dispatch, e->usage, e->value);
|
2017-12-08 09:41:07 +10:00
|
|
|
|
2025-06-16 15:56:47 +10:00
|
|
|
if (is_key) {
|
|
|
|
|
fallback_keyboard_notify_key(dispatch,
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
e->usage,
|
|
|
|
|
e->value ? LIBINPUT_KEY_STATE_PRESSED
|
|
|
|
|
: LIBINPUT_KEY_STATE_RELEASED);
|
2017-12-08 09:41:07 +10:00
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fallback_process_touch(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e,
|
2017-09-19 13:57:50 +10:00
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2017-11-13 12:19:44 +10:00
|
|
|
struct mt_slot *slot = &dispatch->mt.slots[dispatch->mt.slot];
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_ABS_MT_SLOT:
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
2017-11-13 12:19:44 +10:00
|
|
|
return;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_ABS_MT_TRACKING_ID:
|
2017-11-13 12:19:44 +10:00
|
|
|
if (e->value >= 0) {
|
|
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
|
|
|
|
|
slot->state = SLOT_STATE_BEGIN;
|
2018-07-13 16:08:54 +10:00
|
|
|
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;
|
|
|
|
|
}
|
2018-09-28 14:10:48 +10:00
|
|
|
} else {
|
|
|
|
|
slot->palm_state = PALM_NONE;
|
2018-07-13 16:08:54 +10:00
|
|
|
}
|
2017-11-13 12:19:44 +10:00
|
|
|
} else {
|
|
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
|
|
|
|
|
slot->state = SLOT_STATE_END;
|
|
|
|
|
}
|
|
|
|
|
slot->dirty = true;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_ABS_MT_POSITION_X:
|
|
|
|
|
evdev_device_check_abs_axis_range(device, e->usage, e->value);
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->mt.slots[dispatch->mt.slot].point.x = e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
|
|
|
|
|
slot->dirty = true;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_ABS_MT_POSITION_Y:
|
|
|
|
|
evdev_device_check_abs_axis_range(device, e->usage, e->value);
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->mt.slots[dispatch->mt.slot].point.y = e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MT;
|
|
|
|
|
slot->dirty = true;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_ABS_MT_TOOL_TYPE:
|
2018-07-13 16:08:54 +10:00
|
|
|
/* 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;
|
2025-06-05 13:53:55 +10:00
|
|
|
default:
|
|
|
|
|
break;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
2017-11-13 12:19:44 +10:00
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static inline void
|
|
|
|
|
fallback_process_absolute_motion(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_ABS_X:
|
|
|
|
|
evdev_device_check_abs_axis_range(device, e->usage, e->value);
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->abs.point.x = e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_ABS_Y:
|
|
|
|
|
evdev_device_check_abs_axis_range(device, e->usage, e->value);
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->abs.point.y = e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_ABSOLUTE_MOTION;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
default:
|
|
|
|
|
break;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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);
|
2017-11-16 14:50:26 +10:00
|
|
|
int rc;
|
2020-01-19 16:01:57 -06:00
|
|
|
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);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2017-11-16 14:50:26 +10:00
|
|
|
rc = write(fd, ev, sizeof(ev));
|
|
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
evdev_log_error(dispatch->device,
|
|
|
|
|
"failed to write SW_LID state (%s)",
|
|
|
|
|
strerror(errno));
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
/* 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,
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd,
|
2017-09-19 13:57:50 +10:00
|
|
|
bool is_closed)
|
|
|
|
|
{
|
|
|
|
|
assert(kbd->device);
|
|
|
|
|
|
2017-10-27 15:03:52 +10:00
|
|
|
libinput_device_remove_event_listener(&kbd->listener);
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
if (is_closed) {
|
|
|
|
|
libinput_device_add_event_listener(&kbd->device->base,
|
|
|
|
|
&kbd->listener,
|
|
|
|
|
fallback_lid_keyboard_event,
|
|
|
|
|
dispatch);
|
|
|
|
|
} else {
|
2017-10-27 15:03:52 +10:00
|
|
|
libinput_device_init_event_listener(&kbd->listener);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fallback_lid_toggle_keyboard_listeners(struct fallback_dispatch *dispatch,
|
|
|
|
|
bool is_closed)
|
|
|
|
|
{
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
list_for_each(kbd, &dispatch->lid.paired_keyboard_list, link) {
|
2017-09-19 13:57:50 +10:00
|
|
|
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,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e,
|
2017-09-19 13:57:50 +10:00
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
enum libinput_switch_state state;
|
|
|
|
|
bool is_closed;
|
|
|
|
|
|
2017-11-13 12:19:44 +10:00
|
|
|
/* TODO: this should to move to handle_state */
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_SW_LID:
|
2017-09-19 13:57:50 +10:00
|
|
|
is_closed = !!e->value;
|
|
|
|
|
|
2017-10-27 15:03:52 +10:00
|
|
|
fallback_lid_toggle_keyboard_listeners(dispatch, is_closed);
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
if (dispatch->lid.is_closed == is_closed)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dispatch->lid.is_closed = is_closed;
|
|
|
|
|
fallback_lid_notify_toggle(dispatch, device, time);
|
|
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_SW_TABLET_MODE:
|
2017-09-20 14:40:59 +10:00
|
|
|
if (dispatch->tablet_mode.sw.state == e->value)
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
|
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
dispatch->tablet_mode.sw.state = e->value;
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
2025-06-05 13:53:55 +10:00
|
|
|
default:
|
|
|
|
|
break;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
fallback_reject_relative(struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
const struct evdev_event *e,
|
2017-09-19 13:57:50 +10:00
|
|
|
uint64_t time)
|
|
|
|
|
{
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_REL_X:
|
|
|
|
|
case EVDEV_REL_Y:
|
|
|
|
|
if ((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;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 12:35:08 +02:00
|
|
|
static void
|
|
|
|
|
fallback_rotate_wheel(struct fallback_dispatch *dispatch, struct evdev_event *e)
|
|
|
|
|
{
|
|
|
|
|
/* Special case: if we're upside down (-ish),
|
|
|
|
|
* swap the direction of the wheels so that user-down
|
|
|
|
|
* means scroll down. This isn't done for any other angle
|
|
|
|
|
* since it's not clear what the heuristics should be.*/
|
|
|
|
|
if (dispatch->rotation.angle >= 160.0 && dispatch->rotation.angle <= 220.0) {
|
|
|
|
|
e->value *= -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static inline void
|
|
|
|
|
fallback_process_relative(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e,
|
|
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
if (fallback_reject_relative(device, e, time))
|
|
|
|
|
return;
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (evdev_usage_enum(e->usage)) {
|
|
|
|
|
case EVDEV_REL_X:
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->rel.x += e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
case EVDEV_REL_Y:
|
2017-09-19 13:57:50 +10:00
|
|
|
dispatch->rel.y += e->value;
|
2017-11-13 12:19:44 +10:00
|
|
|
dispatch->pending_event |= EVDEV_RELATIVE_MOTION;
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
2025-06-23 12:35:08 +02:00
|
|
|
case EVDEV_REL_WHEEL:
|
|
|
|
|
fallback_rotate_wheel(dispatch, e);
|
|
|
|
|
dispatch->wheel.lo_res.y += e->value;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_REL_HWHEEL:
|
|
|
|
|
fallback_rotate_wheel(dispatch, e);
|
|
|
|
|
dispatch->wheel.lo_res.x += e->value;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_REL_WHEEL_HI_RES:
|
|
|
|
|
fallback_rotate_wheel(dispatch, e);
|
|
|
|
|
dispatch->wheel.hi_res.y += e->value;
|
|
|
|
|
break;
|
|
|
|
|
case EVDEV_REL_HWHEEL_HI_RES:
|
|
|
|
|
fallback_rotate_wheel(dispatch, e);
|
|
|
|
|
dispatch->wheel.hi_res.x += e->value;
|
|
|
|
|
break;
|
2025-06-05 13:53:55 +10:00
|
|
|
default:
|
|
|
|
|
break;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
fallback_process_absolute(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2025-06-05 13:53:55 +10:00
|
|
|
struct evdev_event *e,
|
2017-09-19 13:57:50 +10:00
|
|
|
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)
|
|
|
|
|
{
|
2025-06-06 11:58:53 +10:00
|
|
|
for (evdev_usage_t usage = evdev_usage_from(EVDEV_BTN_LEFT);
|
|
|
|
|
evdev_usage_lt(usage, EVDEV_BTN_JOYSTICK);
|
|
|
|
|
usage = evdev_usage_next(usage)) {
|
2025-06-05 13:53:55 +10:00
|
|
|
if (libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_KEY,
|
|
|
|
|
evdev_usage_code(usage)) &&
|
|
|
|
|
hw_is_key_down(dispatch, usage))
|
2017-09-19 13:57:50 +10:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
static inline bool
|
|
|
|
|
fallback_arbitrate_touch(struct fallback_dispatch *dispatch, struct mt_slot *slot)
|
|
|
|
|
{
|
|
|
|
|
bool discard = false;
|
2023-02-02 07:52:40 +01:00
|
|
|
struct device_coords point = slot->point;
|
|
|
|
|
evdev_transform_absolute(dispatch->device, &point);
|
2018-09-19 12:02:51 +10:00
|
|
|
|
|
|
|
|
if (dispatch->arbitration.state == ARBITRATION_IGNORE_RECT &&
|
2023-02-02 07:52:40 +01:00
|
|
|
point_in_rect(&point, &dispatch->arbitration.rect)) {
|
2018-09-19 12:02:51 +10:00
|
|
|
slot->palm_state = PALM_IS_PALM;
|
|
|
|
|
discard = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return discard;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-13 15:45:22 +10:00
|
|
|
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;
|
|
|
|
|
|
2018-07-13 16:08:54 +10:00
|
|
|
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.
|
2018-07-13 16:08:54 +10:00
|
|
|
*/
|
|
|
|
|
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:
|
2018-09-19 12:02:51 +10:00
|
|
|
if (!fallback_arbitrate_touch(dispatch, slot)) {
|
|
|
|
|
sent = fallback_flush_mt_down(dispatch,
|
|
|
|
|
device,
|
|
|
|
|
i,
|
|
|
|
|
time);
|
|
|
|
|
}
|
2018-07-13 16:08:54 +10:00
|
|
|
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 */
|
2018-07-13 15:45:22 +10:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-13 12:19:44 +10:00
|
|
|
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;
|
2019-10-16 12:50:44 +10:00
|
|
|
} else if (dispatch->pending_event & EVDEV_ABSOLUTE_MOTION) {
|
2017-11-13 12:19:44 +10:00
|
|
|
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 */
|
2018-07-13 15:45:22 +10:00
|
|
|
if (dispatch->pending_event & EVDEV_ABSOLUTE_MT)
|
|
|
|
|
need_touch_frame = fallback_flush_mt_events(dispatch, device, time);
|
2017-11-13 12:19:44 +10:00
|
|
|
|
|
|
|
|
if (need_touch_frame)
|
|
|
|
|
touch_notify_frame(&device->base, time);
|
|
|
|
|
|
2025-06-23 12:35:08 +02:00
|
|
|
fallback_flush_wheels(dispatch, device, time);
|
2017-11-13 12:19:44 +10:00
|
|
|
|
|
|
|
|
/* Buttons and keys */
|
|
|
|
|
if (dispatch->pending_event & EVDEV_KEY) {
|
2025-06-06 11:58:53 +10:00
|
|
|
for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_RESERVED);
|
|
|
|
|
evdev_usage_le(usage, EVDEV_KEY_MAX);
|
|
|
|
|
usage = evdev_usage_next(usage)) {
|
2025-06-05 13:53:55 +10:00
|
|
|
if (!hw_key_has_changed(dispatch, usage))
|
2017-11-13 12:19:44 +10:00
|
|
|
continue;
|
|
|
|
|
|
2025-06-16 15:56:47 +10:00
|
|
|
if (evdev_usage_is_button(usage)) {
|
2025-06-16 16:16:13 +10:00
|
|
|
enum libinput_button_state state =
|
|
|
|
|
hw_is_key_down(dispatch, usage)
|
|
|
|
|
? LIBINPUT_BUTTON_STATE_PRESSED
|
|
|
|
|
: LIBINPUT_BUTTON_STATE_RELEASED;
|
|
|
|
|
evdev_usage_t button =
|
|
|
|
|
evdev_to_left_handed(device, usage);
|
|
|
|
|
fallback_notify_physical_button(dispatch,
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
button,
|
|
|
|
|
state);
|
2017-11-13 12:19:44 +10:00
|
|
|
}
|
|
|
|
|
}
|
2017-11-13 09:33:50 +10:00
|
|
|
|
2017-11-13 12:19:44 +10:00
|
|
|
hw_key_update_last_state(dispatch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch->pending_event = EVDEV_NONE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
2025-06-23 15:06:36 +10:00
|
|
|
fallback_interface_process_event(struct evdev_dispatch *evdev_dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct evdev_event *event,
|
|
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
2023-11-13 20:05:13 +10:00
|
|
|
static bool warned = false;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2023-11-13 20:05:13 +10:00
|
|
|
if (dispatch->arbitration.in_arbitration) {
|
|
|
|
|
if (!warned) {
|
|
|
|
|
evdev_log_debug(device,
|
|
|
|
|
"dropping events due to touch arbitration\n");
|
|
|
|
|
warned = true;
|
|
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
2023-11-13 20:05:13 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
warned = false;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2025-06-05 19:39:15 +10:00
|
|
|
uint16_t type = evdev_event_type(event);
|
2025-06-05 13:53:55 +10:00
|
|
|
switch (type) {
|
2017-09-19 13:57:50 +10:00
|
|
|
case EV_REL:
|
2025-06-05 19:39:15 +10:00
|
|
|
fallback_process_relative(dispatch, device, event, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
|
|
|
|
case EV_ABS:
|
2025-06-05 19:39:15 +10:00
|
|
|
fallback_process_absolute(dispatch, device, event, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
|
|
|
|
case EV_KEY:
|
2025-06-05 19:39:15 +10:00
|
|
|
fallback_process_key(dispatch, device, event, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
|
|
|
|
case EV_SW:
|
2025-06-05 19:39:15 +10:00
|
|
|
fallback_process_switch(dispatch, device, event, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
|
|
|
|
case EV_SYN:
|
2017-11-13 12:19:44 +10:00
|
|
|
fallback_handle_state(dispatch, device, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-23 15:06:36 +10:00
|
|
|
static void
|
|
|
|
|
fallback_interface_process(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct evdev_frame *frame,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
size_t nevents;
|
|
|
|
|
struct evdev_event *events = evdev_frame_get_events(frame, &nevents);
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nevents; i++) {
|
|
|
|
|
fallback_interface_process_event(dispatch, device, &events[i], time);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
2018-09-20 14:09:31 +10:00
|
|
|
cancel_touches(struct fallback_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2018-09-19 12:02:51 +10:00
|
|
|
const struct device_coord_rect *rect,
|
2018-09-20 14:09:31 +10:00
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
unsigned int idx;
|
|
|
|
|
bool need_frame = false;
|
2023-02-02 07:52:40 +01:00
|
|
|
struct device_coords point;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2023-02-02 07:52:40 +01:00
|
|
|
point = dispatch->abs.point;
|
|
|
|
|
evdev_transform_absolute(device, &point);
|
|
|
|
|
if (!rect || point_in_rect(&point, rect))
|
2018-09-19 12:02:51 +10:00
|
|
|
need_frame = fallback_flush_st_cancel(dispatch, device, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
for (idx = 0; idx < dispatch->mt.slots_len; idx++) {
|
|
|
|
|
struct mt_slot *slot = &dispatch->mt.slots[idx];
|
2023-02-02 07:52:40 +01:00
|
|
|
point = slot->point;
|
|
|
|
|
evdev_transform_absolute(device, &point);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
if (slot->seat_slot == -1)
|
|
|
|
|
continue;
|
|
|
|
|
|
2023-02-02 07:52:40 +01:00
|
|
|
if ((!rect || point_in_rect(&point, rect)) &&
|
2018-09-19 12:02:51 +10:00
|
|
|
fallback_flush_mt_cancel(dispatch, device, idx, time))
|
2017-09-19 13:57:50 +10:00
|
|
|
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)
|
|
|
|
|
{
|
2025-06-06 11:58:53 +10:00
|
|
|
for (evdev_usage_t usage = evdev_usage_from(EVDEV_KEY_RESERVED);
|
|
|
|
|
evdev_usage_le(usage, EVDEV_KEY_MAX);
|
|
|
|
|
usage = evdev_usage_next(usage)) {
|
2025-06-05 13:53:55 +10:00
|
|
|
int count = get_key_down_count(device, usage);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
if (count == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (count > 1) {
|
|
|
|
|
evdev_log_bug_libinput(device,
|
|
|
|
|
"key %d is down %d times.\n",
|
2025-06-06 11:58:53 +10:00
|
|
|
evdev_usage_code(usage),
|
2017-09-19 13:57:50 +10:00
|
|
|
count);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-16 15:56:47 +10:00
|
|
|
if (evdev_usage_is_key(usage)) {
|
|
|
|
|
fallback_keyboard_notify_key(dispatch,
|
|
|
|
|
device,
|
|
|
|
|
time,
|
|
|
|
|
usage,
|
|
|
|
|
LIBINPUT_KEY_STATE_RELEASED);
|
|
|
|
|
} else if (evdev_usage_is_button(usage)) {
|
2023-03-29 10:56:41 +10:00
|
|
|
/* Note: the left-handed configuration is nonzero for
|
|
|
|
|
* the mapped button (not the physical button), in
|
|
|
|
|
* get_key_down_count(). We must not map this to left-handed
|
|
|
|
|
* again, see #881.
|
|
|
|
|
*/
|
2019-01-30 14:10:28 +10:00
|
|
|
evdev_pointer_notify_button(device,
|
2017-09-19 13:57:50 +10:00
|
|
|
time,
|
2025-06-05 13:53:55 +10:00
|
|
|
usage,
|
2017-09-19 13:57:50 +10:00
|
|
|
LIBINPUT_BUTTON_STATE_RELEASED);
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
count = get_key_down_count(device, usage);
|
2017-09-19 13:57:50 +10:00
|
|
|
if (count != 0) {
|
|
|
|
|
evdev_log_bug_libinput(device,
|
|
|
|
|
"releasing key %d failed.\n",
|
2025-06-05 13:53:55 +10:00
|
|
|
evdev_usage_enum(usage));
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
cancel_touches(dispatch, device, NULL, time);
|
2017-09-19 13:57:50 +10:00
|
|
|
release_pressed_keys(dispatch, device, time);
|
|
|
|
|
memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask));
|
2017-11-13 12:19:44 +10:00
|
|
|
memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask));
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_suspend(struct evdev_dispatch *evdev_dispatch,
|
|
|
|
|
struct evdev_device *device)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
|
|
|
|
|
|
|
|
|
fallback_return_to_neutral_state(dispatch, device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_remove(struct evdev_dispatch *evdev_dispatch)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
2021-02-22 19:52:40 +03:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-06-29 13:43:15 +10:00
|
|
|
libinput_timer_cancel(&dispatch->debounce.timer);
|
|
|
|
|
libinput_timer_cancel(&dispatch->debounce.timer_short);
|
2019-02-07 12:02:50 +10:00
|
|
|
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
|
2018-06-29 13:43:15 +10:00
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener);
|
|
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
list_for_each_safe(kbd, &dispatch->lid.paired_keyboard_list, link) {
|
2018-05-28 11:46:59 +10:00
|
|
|
evdev_paired_keyboard_destroy(kbd);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_sync_initial_state(struct evdev_device *device,
|
|
|
|
|
struct evdev_dispatch *evdev_dispatch)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
if (dispatch->tablet_mode.sw.state) {
|
2017-09-19 13:57:50 +10:00
|
|
|
switch_notify_toggle(&device->base,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_SWITCH_TABLET_MODE,
|
|
|
|
|
LIBINPUT_SWITCH_STATE_ON);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 11:47:32 +10:00
|
|
|
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);
|
2019-10-24 14:10:47 +10:00
|
|
|
struct device_coord_rect rect;
|
2018-09-28 11:47:32 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
|
|
|
|
|
struct evdev_device *device,
|
2018-09-19 12:02:51 +10:00
|
|
|
enum evdev_arbitration_state which,
|
2018-09-19 12:02:51 +10:00
|
|
|
const struct phys_rect *phys_rect,
|
2018-02-19 14:41:50 +10:00
|
|
|
uint64_t time)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
2018-09-19 12:02:51 +10:00
|
|
|
struct device_coord_rect rect = { 0 };
|
2023-11-13 20:05:13 +10:00
|
|
|
const char *state = NULL;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
if (which == dispatch->arbitration.state)
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
switch (which) {
|
|
|
|
|
case ARBITRATION_NOT_ACTIVE:
|
2019-01-07 08:27:22 +10:00
|
|
|
/* 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));
|
2023-11-13 20:05:13 +10:00
|
|
|
state = "not-active";
|
2018-09-19 12:02:51 +10:00
|
|
|
break;
|
2018-09-19 12:02:51 +10:00
|
|
|
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;
|
2023-11-13 20:05:13 +10:00
|
|
|
state = "ignore-rect";
|
2018-09-19 12:02:51 +10:00
|
|
|
break;
|
2018-09-19 12:02:51 +10:00
|
|
|
case ARBITRATION_IGNORE_ALL:
|
|
|
|
|
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
|
|
|
|
|
fallback_return_to_neutral_state(dispatch, device);
|
|
|
|
|
dispatch->arbitration.in_arbitration = true;
|
2023-11-13 20:05:13 +10:00
|
|
|
state = "ignore-all";
|
2018-09-19 12:02:51 +10:00
|
|
|
break;
|
2019-01-07 08:27:22 +10:00
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2023-11-13 20:05:13 +10:00
|
|
|
evdev_log_debug(device, "Touch arbitration state now %s\n", state);
|
|
|
|
|
|
2018-09-19 12:02:51 +10:00
|
|
|
dispatch->arbitration.state = which;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-09-20 14:10:49 +10:00
|
|
|
fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch)
|
2017-09-19 13:57:50 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
|
|
|
|
|
|
2019-01-07 08:27:22 +10:00
|
|
|
libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
|
2017-09-19 13:57:50 +10:00
|
|
|
libinput_timer_destroy(&dispatch->debounce.timer);
|
2017-11-13 09:33:50 +10:00
|
|
|
libinput_timer_destroy(&dispatch->debounce.timer_short);
|
2018-06-29 13:43:15 +10:00
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
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);
|
2018-05-28 11:46:59 +10:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
2018-05-28 11:27:06 +10:00
|
|
|
size_t count = 0;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
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;
|
|
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
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);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
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
|
2019-05-28 09:54:27 +10:00
|
|
|
fallback_pair_tablet_mode(struct evdev_device *keyboard,
|
|
|
|
|
struct evdev_device *tablet_mode_switch)
|
2017-09-20 14:40:59 +10:00
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(keyboard->dispatch);
|
|
|
|
|
|
2018-08-27 08:26:00 +10:00
|
|
|
if (keyboard->tags & EVDEV_TAG_EXTERNAL_KEYBOARD)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-05-28 09:54:27 +10:00
|
|
|
if (keyboard->tags & EVDEV_TAG_TRACKPOINT) {
|
|
|
|
|
if (keyboard->tags & EVDEV_TAG_EXTERNAL_MOUSE)
|
|
|
|
|
return;
|
|
|
|
|
/* This filters out all internal keyboard-like devices (Video
|
|
|
|
|
* Switch) */
|
2020-05-28 14:42:12 +10:00
|
|
|
} else if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0) {
|
2017-09-20 14:40:59 +10:00
|
|
|
return;
|
2020-05-28 14:42:12 +10:00
|
|
|
}
|
2017-09-20 14:40:59 +10:00
|
|
|
|
2018-09-20 10:01:49 +10:00
|
|
|
if (evdev_device_has_model_quirk(keyboard, QUIRK_MODEL_TABLET_MODE_NO_SUSPEND))
|
2018-04-17 15:22:42 +02:00
|
|
|
return;
|
|
|
|
|
|
2017-09-20 14:40:59 +10:00
|
|
|
if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (dispatch->tablet_mode.other.sw_device)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
evdev_log_debug(keyboard,
|
2019-05-27 09:02:06 +10:00
|
|
|
"tablet-mode: paired %s<->%s\n",
|
2017-09-20 14:40:59 +10:00
|
|
|
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) ==
|
2018-08-01 15:47:03 +10:00
|
|
|
LIBINPUT_SWITCH_STATE_ON) {
|
|
|
|
|
evdev_log_debug(keyboard, "tablet-mode: suspending device\n");
|
2017-09-20 14:40:59 +10:00
|
|
|
fallback_suspend(dispatch, keyboard);
|
2018-08-01 15:47:03 +10:00
|
|
|
}
|
2017-09-20 14:40:59 +10:00
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
static void
|
|
|
|
|
fallback_interface_device_added(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *added_device)
|
|
|
|
|
{
|
|
|
|
|
fallback_lid_pair_keyboard(device, added_device);
|
2019-05-28 09:54:27 +10:00
|
|
|
fallback_pair_tablet_mode(device, added_device);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
fallback_interface_device_removed(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *removed_device)
|
|
|
|
|
{
|
|
|
|
|
struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch);
|
2021-02-22 19:52:40 +03:00
|
|
|
struct evdev_paired_keyboard *kbd;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
list_for_each_safe(kbd, &dispatch->lid.paired_keyboard_list, link) {
|
2017-09-19 13:57:50 +10:00
|
|
|
if (!kbd->device)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (kbd->device != removed_device)
|
|
|
|
|
continue;
|
|
|
|
|
|
2018-05-28 11:46:59 +10:00
|
|
|
evdev_paired_keyboard_destroy(kbd);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
2017-09-20 14:40:59 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2024-03-18 16:24:11 +01:00
|
|
|
static struct evdev_dispatch_interface fallback_interface = {
|
2017-09-20 14:10:49 +10:00
|
|
|
.process = fallback_interface_process,
|
|
|
|
|
.suspend = fallback_interface_suspend,
|
|
|
|
|
.remove = fallback_interface_remove,
|
|
|
|
|
.destroy = fallback_interface_destroy,
|
2017-09-19 13:57:50 +10:00
|
|
|
.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 */
|
2017-09-20 14:10:49 +10:00
|
|
|
.post_added = fallback_interface_sync_initial_state,
|
2018-09-19 12:02:51 +10:00
|
|
|
.touch_arbitration_toggle = fallback_interface_toggle_touch,
|
2018-09-28 11:47:32 +10:00
|
|
|
.touch_arbitration_update_rect = fallback_interface_update_rect,
|
2017-09-20 14:10:49 +10:00
|
|
|
.get_switch_state = fallback_interface_get_switch_state,
|
2017-09-19 13:57:50 +10:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 &&
|
2025-06-05 13:53:55 +10:00
|
|
|
evdev_usage_cmp(device->scroll.want_button, device->scroll.button) == 0 &&
|
2019-03-20 10:56:51 +10:00
|
|
|
device->scroll.want_lock_enabled == device->scroll.lock_enabled)
|
2017-09-19 13:57:50 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (fallback_any_button_down(dispatch, device))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
device->scroll.method = device->scroll.want_method;
|
|
|
|
|
device->scroll.button = device->scroll.want_button;
|
2019-03-20 10:56:51 +10:00
|
|
|
device->scroll.lock_enabled = device->scroll.want_lock_enabled;
|
|
|
|
|
evdev_set_button_scroll_lock_enabled(device, device->scroll.lock_enabled);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2022-12-05 22:57:51 +00:00
|
|
|
if (device->tags & EVDEV_TAG_TRACKPOINT)
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
|
|
|
|
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. */
|
2025-06-23 15:55:02 +10:00
|
|
|
if (!libevdev_has_event_code(evdev, EV_ABS, ABS_MT_SLOT))
|
|
|
|
|
return 0;
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2025-06-23 15:55:02 +10:00
|
|
|
num_slots = libevdev_get_num_slots(device->evdev);
|
|
|
|
|
active_slot = libevdev_get_current_slot(evdev);
|
2017-09-19 13:57:50 +10:00
|
|
|
slots = zalloc(num_slots * sizeof(struct mt_slot));
|
|
|
|
|
|
|
|
|
|
for (slot = 0; slot < num_slots; ++slot) {
|
|
|
|
|
slots[slot].seat_slot = -1;
|
|
|
|
|
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;
|
2018-07-13 16:08:54 +10:00
|
|
|
dispatch->mt.has_palm =
|
|
|
|
|
libevdev_has_event_code(evdev, EV_ABS, ABS_MT_TOOL_TYPE);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
list_init(&dispatch->lid.paired_keyboard_list);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
2018-05-28 11:27:06 +10:00
|
|
|
if (device->tags & EVDEV_TAG_LID_SWITCH) {
|
2017-09-19 13:57:50 +10:00
|
|
|
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);
|
2017-09-20 14:40:59 +10:00
|
|
|
dispatch->tablet_mode.sw.state = val;
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
2017-09-20 14:40:59 +10:00
|
|
|
|
|
|
|
|
libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener);
|
2017-09-19 13:57:50 +10:00
|
|
|
}
|
|
|
|
|
|
2019-01-07 08:27:22 +10:00
|
|
|
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;
|
2023-11-13 20:05:13 +10:00
|
|
|
|
|
|
|
|
evdev_log_debug(dispatch->device, "touch arbitration timeout\n");
|
2019-01-07 08:27:22 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
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;
|
2018-05-28 11:27:06 +10:00
|
|
|
list_init(&dispatch->lid.paired_keyboard_list);
|
2017-09-19 13:57:50 +10:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2025-06-05 13:53:55 +10:00
|
|
|
if (evdev_usage_enum(device->scroll.want_button))
|
2017-09-19 13:57:50 +10:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-07 08:27:22 +10:00
|
|
|
fallback_init_arbitration(dispatch, device);
|
2017-11-13 09:33:50 +10:00
|
|
|
|
2017-09-19 13:57:50 +10:00
|
|
|
return &dispatch->base;
|
|
|
|
|
}
|