[PATCH libinput 5/7] fallback: move the fallback code into a separate file
Peter Hutterer
peter.hutterer at who-t.net
Thu Sep 21 00:57:14 UTC 2017
Split out the fallback-specific device handling from the more generic
evdev-specific handling (which is supposed to be available for all devices).
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
meson.build | 1 +
src/evdev-fallback.c | 1622 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/evdev.c | 1535 +----------------------------------------------
src/evdev.h | 93 +--
4 files changed, 1653 insertions(+), 1598 deletions(-)
create mode 100644 src/evdev-fallback.c
diff --git a/meson.build b/meson.build
index dc3ab1b6..362e9a58 100644
--- a/meson.build
+++ b/meson.build
@@ -156,6 +156,7 @@ src_libinput = [
'src/libinput-private.h',
'src/evdev.c',
'src/evdev.h',
+ 'src/evdev-fallback.c',
'src/evdev-middle-button.c',
'src/evdev-mt-touchpad.c',
'src/evdev-mt-touchpad.h',
diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
new file mode 100644
index 00000000..916254e4
--- /dev/null
+++ b/src/evdev-fallback.c
@@ -0,0 +1,1622 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ * Copyright © 2013 Jonas Ådahl
+ * Copyright © 2013-2017 Red Hat, Inc.
+ * Copyright © 2017 James Ye <jye836 at 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.h"
+
+#define DEBOUNCE_TIME ms2us(12)
+
+struct fallback_dispatch {
+ struct evdev_dispatch base;
+ struct evdev_device *device;
+
+ struct libinput_device_config_calibration calibration;
+
+ struct {
+ bool is_enabled;
+ int angle;
+ struct matrix matrix;
+ struct libinput_device_config_rotation config;
+ } rotation;
+
+ struct {
+ struct device_coords point;
+ int32_t seat_slot;
+
+ struct {
+ struct device_coords min, max;
+ struct ratelimit range_warn_limit;
+ } warning_range;
+ } abs;
+
+ struct {
+ int slot;
+ struct mt_slot *slots;
+ size_t slots_len;
+ bool want_hysteresis;
+ struct device_coords hysteresis_margin;
+ } mt;
+
+ struct device_coords rel;
+
+ struct {
+ int state;
+ } tablet_mode;
+
+ /* Bitmask of pressed keys used to ignore initial release events from
+ * the kernel. */
+ unsigned long hw_key_mask[NLONGS(KEY_CNT)];
+
+ enum evdev_event_type pending_event;
+
+ /* true if we're reading events (i.e. not suspended) but we're
+ ignoring them */
+ bool ignore_events;
+
+ struct {
+ enum evdev_debounce_state state;
+ unsigned int button_code;
+ uint64_t button_up_time;
+ struct libinput_timer timer;
+ } debounce;
+
+ struct {
+ enum switch_reliability reliability;
+
+ bool is_closed;
+ bool is_closed_client_state;
+ struct evdev_device *keyboard;
+ struct libinput_event_listener listener;
+ } lid;
+};
+
+static inline struct fallback_dispatch*
+fallback_dispatch(struct evdev_dispatch *dispatch)
+{
+ evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK);
+
+ return container_of(dispatch, struct fallback_dispatch, base);
+}
+enum key_type {
+ KEY_TYPE_NONE,
+ KEY_TYPE_KEY,
+ KEY_TYPE_BUTTON,
+};
+
+static void
+hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
+{
+ long_set_bit_state(dispatch->hw_key_mask, code, pressed);
+}
+
+static bool
+hw_is_key_down(struct fallback_dispatch *dispatch, int code)
+{
+ return long_bit_is_set(dispatch->hw_key_mask, code);
+}
+
+static int
+get_key_down_count(struct evdev_device *device, int code)
+{
+ return device->key_count[code];
+}
+
+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;
+ }
+}
+
+static enum libinput_switch_state
+fallback_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.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 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.x = evdev_hysteresis(slot->point.x,
+ slot->hysteresis_center.x,
+ dispatch->mt.hysteresis_margin.x);
+ point.y = evdev_hysteresis(slot->point.y,
+ slot->hysteresis_center.y,
+ dispatch->mt.hysteresis_margin.y);
+
+ slot->hysteresis_center = slot->point;
+ if (point.x == slot->point.x && point.y == slot->point.y)
+ return true;
+
+ slot->point = 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_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 |= 1 << 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 &= ~(1 << seat_slot);
+
+ touch_notify_touch_up(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 |= 1 << 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 &= ~(1 << seat_slot);
+
+ touch_notify_touch_up(base, time, -1, seat_slot);
+
+ return true;
+}
+
+static enum evdev_event_type
+fallback_flush_pending_event(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ uint64_t time)
+{
+ enum evdev_event_type sent_event;
+ int slot_idx;
+
+ sent_event = dispatch->pending_event;
+
+ switch (dispatch->pending_event) {
+ case EVDEV_NONE:
+ break;
+ case EVDEV_RELATIVE_MOTION:
+ fallback_flush_relative_motion(dispatch, device, time);
+ break;
+ case EVDEV_ABSOLUTE_MT_DOWN:
+ slot_idx = dispatch->mt.slot;
+ if (!fallback_flush_mt_down(dispatch,
+ device,
+ slot_idx,
+ time))
+ sent_event = EVDEV_NONE;
+ break;
+ case EVDEV_ABSOLUTE_MT_MOTION:
+ slot_idx = dispatch->mt.slot;
+ if (!fallback_flush_mt_motion(dispatch,
+ device,
+ slot_idx,
+ time))
+ sent_event = EVDEV_NONE;
+ break;
+ case EVDEV_ABSOLUTE_MT_UP:
+ slot_idx = dispatch->mt.slot;
+ if (!fallback_flush_mt_up(dispatch,
+ device,
+ slot_idx,
+ time))
+ sent_event = EVDEV_NONE;
+ break;
+ case EVDEV_ABSOLUTE_TOUCH_DOWN:
+ if (!fallback_flush_st_down(dispatch, device, time))
+ sent_event = EVDEV_NONE;
+ break;
+ case EVDEV_ABSOLUTE_MOTION:
+ if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
+ if (fallback_flush_st_motion(dispatch,
+ device,
+ time))
+ sent_event = EVDEV_ABSOLUTE_MT_MOTION;
+ else
+ sent_event = EVDEV_NONE;
+ } else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
+ fallback_flush_absolute_motion(dispatch,
+ device,
+ time);
+ }
+ break;
+ case EVDEV_ABSOLUTE_TOUCH_UP:
+ if (!fallback_flush_st_up(dispatch, device, time))
+ sent_event = EVDEV_NONE;
+ break;
+ default:
+ assert(0 && "Unknown pending event type");
+ break;
+ }
+
+ dispatch->pending_event = EVDEV_NONE;
+
+ return sent_event;
+}
+
+static enum key_type
+get_key_type(uint16_t code)
+{
+ switch (code) {
+ case BTN_TOOL_PEN:
+ case BTN_TOOL_RUBBER:
+ case BTN_TOOL_BRUSH:
+ case BTN_TOOL_PENCIL:
+ case BTN_TOOL_AIRBRUSH:
+ case BTN_TOOL_MOUSE:
+ case BTN_TOOL_LENS:
+ case BTN_TOOL_QUINTTAP:
+ case BTN_TOOL_DOUBLETAP:
+ case BTN_TOOL_TRIPLETAP:
+ case BTN_TOOL_QUADTAP:
+ case BTN_TOOL_FINGER:
+ case BTN_TOUCH:
+ return KEY_TYPE_NONE;
+ }
+
+ if (code >= KEY_ESC && code <= KEY_MICMUTE)
+ return KEY_TYPE_KEY;
+ if (code >= BTN_MISC && code <= BTN_GEAR_UP)
+ return KEY_TYPE_BUTTON;
+ if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE)
+ return KEY_TYPE_KEY;
+ if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT)
+ return KEY_TYPE_BUTTON;
+ if (code >= KEY_ALS_TOGGLE && code <= KEY_ONSCREEN_KEYBOARD)
+ return KEY_TYPE_KEY;
+ if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40)
+ return KEY_TYPE_BUTTON;
+ return KEY_TYPE_NONE;
+}
+
+static void
+fallback_process_touch_button(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ uint64_t time, int value)
+{
+ if (dispatch->pending_event != EVDEV_NONE &&
+ dispatch->pending_event != EVDEV_ABSOLUTE_MOTION)
+ fallback_flush_pending_event(dispatch, device, time);
+
+ dispatch->pending_event = (value ?
+ EVDEV_ABSOLUTE_TOUCH_DOWN :
+ EVDEV_ABSOLUTE_TOUCH_UP);
+}
+
+static inline void
+fallback_flush_debounce(struct fallback_dispatch *dispatch,
+ struct evdev_device *device)
+{
+ int code = dispatch->debounce.button_code;
+ int button;
+
+ if (dispatch->debounce.state != DEBOUNCE_ACTIVE)
+ return;
+
+ if (hw_is_key_down(dispatch, code)) {
+ button = evdev_to_left_handed(device, code);
+ evdev_pointer_notify_physical_button(device,
+ dispatch->debounce.button_up_time,
+ button,
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ hw_set_key_down(dispatch, code, 0);
+ }
+
+ dispatch->debounce.state = DEBOUNCE_ON;
+}
+
+static void
+fallback_debounce_timeout(uint64_t now, void *data)
+{
+ struct evdev_device *device = data;
+ struct fallback_dispatch *dispatch =
+ fallback_dispatch(device->dispatch);
+
+ fallback_flush_debounce(dispatch, device);
+}
+
+static bool
+fallback_filter_debounce_press(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint64_t time)
+{
+ bool filter = false;
+ uint64_t tdelta;
+
+ /* If other button is pressed while we're holding back the release,
+ * flush the pending release (if any) and continue. We don't handle
+ * this situation, if you have a mouse that needs per-button
+ * debouncing, consider writing to santa for a new mouse.
+ */
+ if (e->code != dispatch->debounce.button_code) {
+ if (dispatch->debounce.state == DEBOUNCE_ACTIVE) {
+ libinput_timer_cancel(&dispatch->debounce.timer);
+ fallback_flush_debounce(dispatch, device);
+ }
+ return false;
+ }
+
+ tdelta = time - dispatch->debounce.button_up_time;
+ assert((int64_t)tdelta >= 0);
+
+ if (tdelta < DEBOUNCE_TIME) {
+ switch (dispatch->debounce.state) {
+ case DEBOUNCE_INIT:
+ /* This is the first time we debounce, enable proper debouncing
+ from now on but filter this press event */
+ filter = true;
+ evdev_log_info(device,
+ "Enabling button debouncing, "
+ "see %sbutton_debouncing.html for details\n",
+ HTTP_DOC_LINK);
+ dispatch->debounce.state = DEBOUNCE_NEEDED;
+ break;
+ case DEBOUNCE_NEEDED:
+ case DEBOUNCE_ON:
+ break;
+ /* If a release event is pending and, filter press
+ * events until we flushed the release */
+ case DEBOUNCE_ACTIVE:
+ filter = true;
+ break;
+ }
+ } else if (dispatch->debounce.state == DEBOUNCE_ACTIVE) {
+ /* call libinput_dispatch() more frequently */
+ evdev_log_bug_client(device,
+ "Debouncing still active past timeout\n");
+ }
+
+ return filter;
+}
+
+static bool
+fallback_filter_debounce_release(struct fallback_dispatch *dispatch,
+ struct input_event *e,
+ uint64_t time)
+{
+ bool filter = false;
+
+ dispatch->debounce.button_code = e->code;
+ dispatch->debounce.button_up_time = time;
+
+ switch (dispatch->debounce.state) {
+ case DEBOUNCE_INIT:
+ break;
+ case DEBOUNCE_NEEDED:
+ filter = true;
+ dispatch->debounce.state = DEBOUNCE_ON;
+ break;
+ case DEBOUNCE_ON:
+ libinput_timer_set(&dispatch->debounce.timer,
+ time + DEBOUNCE_TIME);
+ filter = true;
+ dispatch->debounce.state = DEBOUNCE_ACTIVE;
+ break;
+ case DEBOUNCE_ACTIVE:
+ filter = true;
+ break;
+ }
+
+ return filter;
+}
+
+static bool
+fallback_filter_debounce(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *e, uint64_t time)
+{
+ bool filter = false;
+
+ /* Behavior: we monitor the time deltas between release and press
+ * events. Proper debouncing is disabled on init, but the first
+ * time we see a bouncing press event we enable it.
+ *
+ * The first bounced event is simply discarded, which ends up in the
+ * button being released sooner than it should be. Subsequent button
+ * presses are timer-based and thus released a bit later because we
+ * then wait for a timeout before we post the release event.
+ */
+ if (e->value)
+ filter = fallback_filter_debounce_press(dispatch, device, e, time);
+ else
+ filter = fallback_filter_debounce_release(dispatch, e, time);
+
+ return filter;
+}
+
+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;
+ }
+
+ fallback_flush_pending_event(dispatch, device, time);
+
+ 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:
+ if ((e->value && hw_is_key_down(dispatch, e->code)) ||
+ (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
+ return;
+ break;
+ case KEY_TYPE_BUTTON:
+ if (fallback_filter_debounce(dispatch, device, e, time))
+ return;
+
+ if ((e->value && hw_is_key_down(dispatch, e->code)) ||
+ (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
+ return;
+ 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:
+ evdev_pointer_notify_physical_button(
+ device,
+ time,
+ evdev_to_left_handed(device, e->code),
+ e->value ? LIBINPUT_BUTTON_STATE_PRESSED :
+ LIBINPUT_BUTTON_STATE_RELEASED);
+ break;
+ }
+}
+
+static void
+fallback_process_touch(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ struct input_event *e,
+ uint64_t time)
+{
+ switch (e->code) {
+ case 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;
+ }
+ fallback_flush_pending_event(dispatch, device, time);
+ dispatch->mt.slot = e->value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (dispatch->pending_event != EVDEV_NONE &&
+ dispatch->pending_event != EVDEV_ABSOLUTE_MT_MOTION)
+ fallback_flush_pending_event(dispatch, device, time);
+ if (e->value >= 0)
+ dispatch->pending_event = EVDEV_ABSOLUTE_MT_DOWN;
+ else
+ dispatch->pending_event = EVDEV_ABSOLUTE_MT_UP;
+ 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;
+ if (dispatch->pending_event == EVDEV_NONE)
+ dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+ 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;
+ if (dispatch->pending_event == EVDEV_NONE)
+ dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
+ 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;
+ if (dispatch->pending_event == EVDEV_NONE)
+ 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;
+ if (dispatch->pending_event == EVDEV_NONE)
+ 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);
+ struct input_event ev[2] = {
+ {{ 0, 0 }, EV_SW, SW_LID, 0 },
+ {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 },
+ };
+
+ (void)write(fd, ev, sizeof(ev));
+ /* 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,
+ bool is_closed)
+{
+ if (!dispatch->lid.keyboard)
+ return;
+
+ if (is_closed) {
+ libinput_device_add_event_listener(
+ &dispatch->lid.keyboard->base,
+ &dispatch->lid.listener,
+ fallback_lid_keyboard_event,
+ dispatch);
+ } else {
+ libinput_device_remove_event_listener(
+ &dispatch->lid.listener);
+ libinput_device_init_event_listener(
+ &dispatch->lid.listener);
+ }
+}
+
+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;
+
+ switch (e->code) {
+ case SW_LID:
+ is_closed = !!e->value;
+
+ if (dispatch->lid.is_closed == is_closed)
+ return;
+ fallback_lid_toggle_keyboard_listener(dispatch, is_closed);
+
+ dispatch->lid.is_closed = is_closed;
+ fallback_lid_notify_toggle(dispatch, device, time);
+ break;
+ case SW_TABLET_MODE:
+ if (dispatch->tablet_mode.state == e->value)
+ return;
+
+ dispatch->tablet_mode.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)
+{
+ struct normalized_coords wheel_degrees = { 0.0, 0.0 };
+ struct discrete_coords discrete = { 0.0, 0.0 };
+ enum libinput_pointer_axis_source source;
+
+ if (fallback_reject_relative(device, e, time))
+ return;
+
+ switch (e->code) {
+ case REL_X:
+ if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
+ fallback_flush_pending_event(dispatch, device, time);
+ dispatch->rel.x += e->value;
+ dispatch->pending_event = EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_Y:
+ if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
+ fallback_flush_pending_event(dispatch, device, time);
+ dispatch->rel.y += e->value;
+ dispatch->pending_event = EVDEV_RELATIVE_MOTION;
+ break;
+ case REL_WHEEL:
+ fallback_flush_pending_event(dispatch, device, time);
+ wheel_degrees.y = -1 * e->value *
+ device->scroll.wheel_click_angle.x;
+ discrete.y = -1 * e->value;
+
+ source = device->scroll.is_tilt.vertical ?
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
+
+ evdev_notify_axis(
+ device,
+ time,
+ AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
+ source,
+ &wheel_degrees,
+ &discrete);
+ break;
+ case REL_HWHEEL:
+ fallback_flush_pending_event(dispatch, device, time);
+ wheel_degrees.x = e->value *
+ device->scroll.wheel_click_angle.y;
+ discrete.x = e->value;
+
+ source = device->scroll.is_tilt.horizontal ?
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
+ LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
+
+ evdev_notify_axis(
+ device,
+ time,
+ AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
+ source,
+ &wheel_degrees,
+ &discrete);
+ 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 void
+fallback_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);
+ enum evdev_event_type sent;
+
+ if (dispatch->ignore_events)
+ 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:
+ sent = fallback_flush_pending_event(dispatch, device, time);
+ switch (sent) {
+ case EVDEV_ABSOLUTE_TOUCH_DOWN:
+ case EVDEV_ABSOLUTE_TOUCH_UP:
+ case EVDEV_ABSOLUTE_MT_DOWN:
+ case EVDEV_ABSOLUTE_MT_MOTION:
+ case EVDEV_ABSOLUTE_MT_UP:
+ touch_notify_frame(&device->base, time);
+ break;
+ case EVDEV_ABSOLUTE_MOTION:
+ case EVDEV_RELATIVE_MOTION:
+ case EVDEV_NONE:
+ break;
+ }
+ break;
+ }
+}
+
+static void
+release_touches(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ uint64_t time)
+{
+ unsigned int idx;
+ bool need_frame = false;
+
+ need_frame = fallback_flush_st_up(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 (fallback_flush_mt_up(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_physical_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;
+
+ release_touches(dispatch, device, time);
+ release_pressed_keys(dispatch, device, time);
+ memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask));
+}
+
+static void
+fallback_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_remove(struct evdev_dispatch *evdev_dispatch)
+{
+ struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+
+ if (!dispatch->lid.keyboard)
+ return;
+
+ libinput_device_remove_event_listener(&dispatch->lid.listener);
+}
+
+static void
+fallback_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.state) {
+ switch_notify_toggle(&device->base,
+ time,
+ LIBINPUT_SWITCH_TABLET_MODE,
+ LIBINPUT_SWITCH_STATE_ON);
+ }
+}
+
+static void
+fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch,
+ struct evdev_device *device,
+ bool enable)
+{
+ struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+ bool ignore_events = !enable;
+
+ if (ignore_events == dispatch->ignore_events)
+ return;
+
+ if (ignore_events)
+ fallback_return_to_neutral_state(dispatch, device);
+
+ dispatch->ignore_events = ignore_events;
+}
+
+static void
+fallback_destroy(struct evdev_dispatch *evdev_dispatch)
+{
+ struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+
+ libinput_timer_cancel(&dispatch->debounce.timer);
+ libinput_timer_destroy(&dispatch->debounce.timer);
+ 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);
+
+ if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 ||
+ (lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
+ return;
+
+ if (dispatch->lid.keyboard)
+ return;
+
+ if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) {
+ dispatch->lid.keyboard = keyboard;
+ 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,
+ dispatch->lid.is_closed);
+ }
+}
+
+static void
+fallback_interface_device_added(struct evdev_device *device,
+ struct evdev_device *added_device)
+{
+ fallback_lid_pair_keyboard(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);
+
+ if (removed_device == dispatch->lid.keyboard) {
+ libinput_device_remove_event_listener(
+ &dispatch->lid.listener);
+ libinput_device_init_event_listener(
+ &dispatch->lid.listener);
+ dispatch->lid.keyboard = NULL;
+ }
+}
+
+struct evdev_dispatch_interface fallback_interface = {
+ .process = fallback_process,
+ .suspend = fallback_suspend,
+ .remove = fallback_remove,
+ .destroy = fallback_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_sync_initial_state,
+ .toggle_touch = fallback_toggle_touch,
+ .get_switch_state = fallback_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)
+ return;
+
+ if (fallback_any_button_down(dispatch, device))
+ return;
+
+ device->scroll.method = device->scroll.want_method;
+ device->scroll.button = device->scroll.want_button;
+}
+
+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;
+
+ 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;
+
+ if (device->tags & EVDEV_TAG_LID_SWITCH) {
+ libinput_device_init_event_listener(&dispatch->lid.listener);
+ 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.state = val;
+ }
+}
+
+struct evdev_dispatch *
+fallback_dispatch_create(struct libinput_device *libinput_device)
+{
+ struct evdev_device *device = evdev_device(libinput_device);
+ struct fallback_dispatch *dispatch;
+ char timer_name[64];
+
+ 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;
+
+ 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);
+ }
+
+ snprintf(timer_name,
+ sizeof(timer_name),
+ "%s debounce",
+ evdev_device_get_sysname(device));
+ libinput_timer_init(&dispatch->debounce.timer,
+ evdev_libinput_context(device),
+ timer_name,
+ fallback_debounce_timeout,
+ device);
+ return &dispatch->base;
+}
diff --git a/src/evdev.c b/src/evdev.c
index 8875f06a..bd6674e7 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -50,13 +50,6 @@
#define DEFAULT_WHEEL_CLICK_ANGLE 15
#define DEFAULT_BUTTON_SCROLL_TIMEOUT ms2us(200)
-#define DEBOUNCE_TIME ms2us(12)
-
-enum key_type {
- KEY_TYPE_NONE,
- KEY_TYPE_KEY,
- KEY_TYPE_BUTTON,
-};
enum evdev_device_udev_tags {
EVDEV_UDEV_TAG_INPUT = (1 << 0),
@@ -115,26 +108,10 @@ parse_udev_flag(struct evdev_device *device,
return false;
}
-static void
-hw_set_key_down(struct fallback_dispatch *dispatch, int code, int pressed)
-{
- long_set_bit_state(dispatch->hw_key_mask, code, pressed);
-}
-
-static bool
-hw_is_key_down(struct fallback_dispatch *dispatch, int code)
-{
- return long_bit_is_set(dispatch->hw_key_mask, code);
-}
-
-static int
-get_key_down_count(struct evdev_device *device, int code)
-{
- return device->key_count[code];
-}
-
-static int
-update_key_down_count(struct evdev_device *device, int code, int pressed)
+int
+evdev_update_key_down_count(struct evdev_device *device,
+ int code,
+ int pressed)
{
int key_count;
assert(code >= 0 && code < KEY_CNT);
@@ -155,36 +132,6 @@ update_key_down_count(struct evdev_device *device, int code, int pressed)
return key_count;
}
-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 = 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;
- }
-}
-
enum libinput_switch_state
evdev_device_switch_get_state(struct evdev_device *device,
enum libinput_switch sw)
@@ -196,25 +143,6 @@ evdev_device_switch_get_state(struct evdev_device *device,
return dispatch->interface->get_switch_state(dispatch, sw);
}
-static enum libinput_switch_state
-fallback_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.state ?
- LIBINPUT_SWITCH_STATE_ON :
- LIBINPUT_SWITCH_STATE_OFF;
-}
-
void
evdev_pointer_notify_physical_button(struct evdev_device *device,
uint64_t time,
@@ -241,7 +169,7 @@ evdev_pointer_post_button(struct evdev_device *device,
{
int down_count;
- down_count = update_key_down_count(device, button, state);
+ down_count = evdev_update_key_down_count(device, button, state);
if ((state == LIBINPUT_BUTTON_STATE_PRESSED && down_count == 1) ||
(state == LIBINPUT_BUTTON_STATE_RELEASED && down_count == 0)) {
@@ -417,811 +345,6 @@ evdev_device_transform_y(struct evdev_device *device,
return scale_axis(device->abs.absinfo_y, y, height);
}
-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
-evdev_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 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.x = evdev_hysteresis(slot->point.x,
- slot->hysteresis_center.x,
- dispatch->mt.hysteresis_margin.x);
- point.y = evdev_hysteresis(slot->point.y,
- slot->hysteresis_center.y,
- dispatch->mt.hysteresis_margin.y);
-
- slot->hysteresis_center = slot->point;
- if (point.x == slot->point.x && point.y == slot->point.y)
- return true;
-
- slot->point = 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 (evdev_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_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 |= 1 << 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 &= ~(1 << seat_slot);
-
- touch_notify_touch_up(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 |= 1 << 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 &= ~(1 << seat_slot);
-
- touch_notify_touch_up(base, time, -1, seat_slot);
-
- return true;
-}
-
-static enum evdev_event_type
-fallback_flush_pending_event(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- uint64_t time)
-{
- enum evdev_event_type sent_event;
- int slot_idx;
-
- sent_event = dispatch->pending_event;
-
- switch (dispatch->pending_event) {
- case EVDEV_NONE:
- break;
- case EVDEV_RELATIVE_MOTION:
- fallback_flush_relative_motion(dispatch, device, time);
- break;
- case EVDEV_ABSOLUTE_MT_DOWN:
- slot_idx = dispatch->mt.slot;
- if (!fallback_flush_mt_down(dispatch,
- device,
- slot_idx,
- time))
- sent_event = EVDEV_NONE;
- break;
- case EVDEV_ABSOLUTE_MT_MOTION:
- slot_idx = dispatch->mt.slot;
- if (!fallback_flush_mt_motion(dispatch,
- device,
- slot_idx,
- time))
- sent_event = EVDEV_NONE;
- break;
- case EVDEV_ABSOLUTE_MT_UP:
- slot_idx = dispatch->mt.slot;
- if (!fallback_flush_mt_up(dispatch,
- device,
- slot_idx,
- time))
- sent_event = EVDEV_NONE;
- break;
- case EVDEV_ABSOLUTE_TOUCH_DOWN:
- if (!fallback_flush_st_down(dispatch, device, time))
- sent_event = EVDEV_NONE;
- break;
- case EVDEV_ABSOLUTE_MOTION:
- if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
- if (fallback_flush_st_motion(dispatch,
- device,
- time))
- sent_event = EVDEV_ABSOLUTE_MT_MOTION;
- else
- sent_event = EVDEV_NONE;
- } else if (device->seat_caps & EVDEV_DEVICE_POINTER) {
- fallback_flush_absolute_motion(dispatch,
- device,
- time);
- }
- break;
- case EVDEV_ABSOLUTE_TOUCH_UP:
- if (!fallback_flush_st_up(dispatch, device, time))
- sent_event = EVDEV_NONE;
- break;
- default:
- assert(0 && "Unknown pending event type");
- break;
- }
-
- dispatch->pending_event = EVDEV_NONE;
-
- return sent_event;
-}
-
-static enum key_type
-get_key_type(uint16_t code)
-{
- switch (code) {
- case BTN_TOOL_PEN:
- case BTN_TOOL_RUBBER:
- case BTN_TOOL_BRUSH:
- case BTN_TOOL_PENCIL:
- case BTN_TOOL_AIRBRUSH:
- case BTN_TOOL_MOUSE:
- case BTN_TOOL_LENS:
- case BTN_TOOL_QUINTTAP:
- case BTN_TOOL_DOUBLETAP:
- case BTN_TOOL_TRIPLETAP:
- case BTN_TOOL_QUADTAP:
- case BTN_TOOL_FINGER:
- case BTN_TOUCH:
- return KEY_TYPE_NONE;
- }
-
- if (code >= KEY_ESC && code <= KEY_MICMUTE)
- return KEY_TYPE_KEY;
- if (code >= BTN_MISC && code <= BTN_GEAR_UP)
- return KEY_TYPE_BUTTON;
- if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE)
- return KEY_TYPE_KEY;
- if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT)
- return KEY_TYPE_BUTTON;
- if (code >= KEY_ALS_TOGGLE && code <= KEY_ONSCREEN_KEYBOARD)
- return KEY_TYPE_KEY;
- if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40)
- return KEY_TYPE_BUTTON;
- return KEY_TYPE_NONE;
-}
-
-static void
-fallback_process_touch_button(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- uint64_t time, int value)
-{
- if (dispatch->pending_event != EVDEV_NONE &&
- dispatch->pending_event != EVDEV_ABSOLUTE_MOTION)
- fallback_flush_pending_event(dispatch, device, time);
-
- dispatch->pending_event = (value ?
- EVDEV_ABSOLUTE_TOUCH_DOWN :
- EVDEV_ABSOLUTE_TOUCH_UP);
-}
-
-static inline void
-fallback_flush_debounce(struct fallback_dispatch *dispatch,
- struct evdev_device *device)
-{
- int code = dispatch->debounce.button_code;
- int button;
-
- if (dispatch->debounce.state != DEBOUNCE_ACTIVE)
- return;
-
- if (hw_is_key_down(dispatch, code)) {
- button = evdev_to_left_handed(device, code);
- evdev_pointer_notify_physical_button(device,
- dispatch->debounce.button_up_time,
- button,
- LIBINPUT_BUTTON_STATE_RELEASED);
- hw_set_key_down(dispatch, code, 0);
- }
-
- dispatch->debounce.state = DEBOUNCE_ON;
-}
-
-static void
-fallback_debounce_timeout(uint64_t now, void *data)
-{
- struct evdev_device *device = data;
- struct fallback_dispatch *dispatch =
- fallback_dispatch(device->dispatch);
-
- fallback_flush_debounce(dispatch, device);
-}
-
-static bool
-fallback_filter_debounce_press(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- struct input_event *e,
- uint64_t time)
-{
- bool filter = false;
- uint64_t tdelta;
-
- /* If other button is pressed while we're holding back the release,
- * flush the pending release (if any) and continue. We don't handle
- * this situation, if you have a mouse that needs per-button
- * debouncing, consider writing to santa for a new mouse.
- */
- if (e->code != dispatch->debounce.button_code) {
- if (dispatch->debounce.state == DEBOUNCE_ACTIVE) {
- libinput_timer_cancel(&dispatch->debounce.timer);
- fallback_flush_debounce(dispatch, device);
- }
- return false;
- }
-
- tdelta = time - dispatch->debounce.button_up_time;
- assert((int64_t)tdelta >= 0);
-
- if (tdelta < DEBOUNCE_TIME) {
- switch (dispatch->debounce.state) {
- case DEBOUNCE_INIT:
- /* This is the first time we debounce, enable proper debouncing
- from now on but filter this press event */
- filter = true;
- evdev_log_info(device,
- "Enabling button debouncing, "
- "see %sbutton_debouncing.html for details\n",
- HTTP_DOC_LINK);
- dispatch->debounce.state = DEBOUNCE_NEEDED;
- break;
- case DEBOUNCE_NEEDED:
- case DEBOUNCE_ON:
- break;
- /* If a release event is pending and, filter press
- * events until we flushed the release */
- case DEBOUNCE_ACTIVE:
- filter = true;
- break;
- }
- } else if (dispatch->debounce.state == DEBOUNCE_ACTIVE) {
- /* call libinput_dispatch() more frequently */
- evdev_log_bug_client(device,
- "Debouncing still active past timeout\n");
- }
-
- return filter;
-}
-
-static bool
-fallback_filter_debounce_release(struct fallback_dispatch *dispatch,
- struct input_event *e,
- uint64_t time)
-{
- bool filter = false;
-
- dispatch->debounce.button_code = e->code;
- dispatch->debounce.button_up_time = time;
-
- switch (dispatch->debounce.state) {
- case DEBOUNCE_INIT:
- break;
- case DEBOUNCE_NEEDED:
- filter = true;
- dispatch->debounce.state = DEBOUNCE_ON;
- break;
- case DEBOUNCE_ON:
- libinput_timer_set(&dispatch->debounce.timer,
- time + DEBOUNCE_TIME);
- filter = true;
- dispatch->debounce.state = DEBOUNCE_ACTIVE;
- break;
- case DEBOUNCE_ACTIVE:
- filter = true;
- break;
- }
-
- return filter;
-}
-
-static bool
-fallback_filter_debounce(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- struct input_event *e, uint64_t time)
-{
- bool filter = false;
-
- /* Behavior: we monitor the time deltas between release and press
- * events. Proper debouncing is disabled on init, but the first
- * time we see a bouncing press event we enable it.
- *
- * The first bounced event is simply discarded, which ends up in the
- * button being released sooner than it should be. Subsequent button
- * presses are timer-based and thus released a bit later because we
- * then wait for a timeout before we post the release event.
- */
- if (e->value)
- filter = fallback_filter_debounce_press(dispatch, device, e, time);
- else
- filter = fallback_filter_debounce_release(dispatch, e, time);
-
- return filter;
-}
-
-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;
- }
-
- fallback_flush_pending_event(dispatch, device, time);
-
- 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:
- if ((e->value && hw_is_key_down(dispatch, e->code)) ||
- (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
- return;
- break;
- case KEY_TYPE_BUTTON:
- if (fallback_filter_debounce(dispatch, device, e, time))
- return;
-
- if ((e->value && hw_is_key_down(dispatch, e->code)) ||
- (e->value == 0 && !hw_is_key_down(dispatch, e->code)))
- return;
- 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:
- evdev_pointer_notify_physical_button(
- device,
- time,
- evdev_to_left_handed(device, e->code),
- e->value ? LIBINPUT_BUTTON_STATE_PRESSED :
- LIBINPUT_BUTTON_STATE_RELEASED);
- break;
- }
-}
-
-static void
-fallback_process_touch(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- struct input_event *e,
- uint64_t time)
-{
- switch (e->code) {
- case 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;
- }
- fallback_flush_pending_event(dispatch, device, time);
- dispatch->mt.slot = e->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (dispatch->pending_event != EVDEV_NONE &&
- dispatch->pending_event != EVDEV_ABSOLUTE_MT_MOTION)
- fallback_flush_pending_event(dispatch, device, time);
- if (e->value >= 0)
- dispatch->pending_event = EVDEV_ABSOLUTE_MT_DOWN;
- else
- dispatch->pending_event = EVDEV_ABSOLUTE_MT_UP;
- 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;
- if (dispatch->pending_event == EVDEV_NONE)
- dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
- 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;
- if (dispatch->pending_event == EVDEV_NONE)
- dispatch->pending_event = EVDEV_ABSOLUTE_MT_MOTION;
- 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;
- if (dispatch->pending_event == EVDEV_NONE)
- 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;
- if (dispatch->pending_event == EVDEV_NONE)
- 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);
- struct input_event ev[2] = {
- {{ 0, 0 }, EV_SW, SW_LID, 0 },
- {{ 0, 0 }, EV_SYN, SYN_REPORT, 0 },
- };
-
- (void)write(fd, ev, sizeof(ev));
- /* 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,
- bool is_closed)
-{
- if (!dispatch->lid.keyboard)
- return;
-
- if (is_closed) {
- libinput_device_add_event_listener(
- &dispatch->lid.keyboard->base,
- &dispatch->lid.listener,
- fallback_lid_keyboard_event,
- dispatch);
- } else {
- libinput_device_remove_event_listener(
- &dispatch->lid.listener);
- libinput_device_init_event_listener(
- &dispatch->lid.listener);
- }
-}
-
-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;
-
- switch (e->code) {
- case SW_LID:
- is_closed = !!e->value;
-
- if (dispatch->lid.is_closed == is_closed)
- return;
- fallback_lid_toggle_keyboard_listener(dispatch, is_closed);
-
- dispatch->lid.is_closed = is_closed;
- fallback_lid_notify_toggle(dispatch, device, time);
- break;
- case SW_TABLET_MODE:
- if (dispatch->tablet_mode.state == e->value)
- return;
-
- dispatch->tablet_mode.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;
- }
-}
-
void
evdev_notify_axis(struct evdev_device *device,
uint64_t time,
@@ -1248,113 +371,6 @@ evdev_notify_axis(struct evdev_device *device,
&discrete);
}
-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)
-{
- struct normalized_coords wheel_degrees = { 0.0, 0.0 };
- struct discrete_coords discrete = { 0.0, 0.0 };
- enum libinput_pointer_axis_source source;
-
- if (fallback_reject_relative(device, e, time))
- return;
-
- switch (e->code) {
- case REL_X:
- if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
- fallback_flush_pending_event(dispatch, device, time);
- dispatch->rel.x += e->value;
- dispatch->pending_event = EVDEV_RELATIVE_MOTION;
- break;
- case REL_Y:
- if (dispatch->pending_event != EVDEV_RELATIVE_MOTION)
- fallback_flush_pending_event(dispatch, device, time);
- dispatch->rel.y += e->value;
- dispatch->pending_event = EVDEV_RELATIVE_MOTION;
- break;
- case REL_WHEEL:
- fallback_flush_pending_event(dispatch, device, time);
- wheel_degrees.y = -1 * e->value *
- device->scroll.wheel_click_angle.x;
- discrete.y = -1 * e->value;
-
- source = device->scroll.is_tilt.vertical ?
- LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
- LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
-
- evdev_notify_axis(
- device,
- time,
- AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL),
- source,
- &wheel_degrees,
- &discrete);
- break;
- case REL_HWHEEL:
- fallback_flush_pending_event(dispatch, device, time);
- wheel_degrees.x = e->value *
- device->scroll.wheel_click_angle.y;
- discrete.x = e->value;
-
- source = device->scroll.is_tilt.horizontal ?
- LIBINPUT_POINTER_AXIS_SOURCE_WHEEL_TILT:
- LIBINPUT_POINTER_AXIS_SOURCE_WHEEL;
-
- evdev_notify_axis(
- device,
- time,
- AS_MASK(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL),
- source,
- &wheel_degrees,
- &discrete);
- 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 void
evdev_tag_external_mouse(struct evdev_device *device,
struct udev_device *udev_device)
@@ -1425,223 +441,6 @@ evdev_tag_keyboard(struct evdev_device *device,
device->tags |= EVDEV_TAG_KEYBOARD;
}
-static void
-fallback_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);
- enum evdev_event_type sent;
-
- if (dispatch->ignore_events)
- 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:
- sent = fallback_flush_pending_event(dispatch, device, time);
- switch (sent) {
- case EVDEV_ABSOLUTE_TOUCH_DOWN:
- case EVDEV_ABSOLUTE_TOUCH_UP:
- case EVDEV_ABSOLUTE_MT_DOWN:
- case EVDEV_ABSOLUTE_MT_MOTION:
- case EVDEV_ABSOLUTE_MT_UP:
- touch_notify_frame(&device->base, time);
- break;
- case EVDEV_ABSOLUTE_MOTION:
- case EVDEV_RELATIVE_MOTION:
- case EVDEV_NONE:
- break;
- }
- break;
- }
-}
-
-static void
-release_touches(struct fallback_dispatch *dispatch,
- struct evdev_device *device,
- uint64_t time)
-{
- unsigned int idx;
- bool need_frame = false;
-
- need_frame = fallback_flush_st_up(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 (fallback_flush_mt_up(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_physical_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;
-
- release_touches(dispatch, device, time);
- release_pressed_keys(dispatch, device, time);
- memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask));
-}
-
-static void
-fallback_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_remove(struct evdev_dispatch *evdev_dispatch)
-{
- struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
-
- if (!dispatch->lid.keyboard)
- return;
-
- libinput_device_remove_event_listener(&dispatch->lid.listener);
-}
-
-static void
-fallback_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.state) {
- switch_notify_toggle(&device->base,
- time,
- LIBINPUT_SWITCH_TABLET_MODE,
- LIBINPUT_SWITCH_STATE_ON);
- }
-}
-
-static void
-fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch,
- struct evdev_device *device,
- bool enable)
-{
- struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
- bool ignore_events = !enable;
-
- if (ignore_events == dispatch->ignore_events)
- return;
-
- if (ignore_events)
- fallback_return_to_neutral_state(dispatch, device);
-
- dispatch->ignore_events = ignore_events;
-}
-
-static void
-fallback_destroy(struct evdev_dispatch *evdev_dispatch)
-{
- struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
-
- libinput_timer_cancel(&dispatch->debounce.timer);
- libinput_timer_destroy(&dispatch->debounce.timer);
- free(dispatch->mt.slots);
- free(dispatch);
-}
-
static int
evdev_calibration_has_matrix(struct libinput_device *libinput_device)
{
@@ -1683,72 +482,6 @@ evdev_calibration_get_default_matrix(struct libinput_device *libinput_device,
return !matrix_is_identity(&device->abs.default_calibration);
}
-static void
-fallback_lid_pair_keyboard(struct evdev_device *lid_switch,
- struct evdev_device *keyboard)
-{
- struct fallback_dispatch *dispatch =
- fallback_dispatch(lid_switch->dispatch);
-
- if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 ||
- (lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0)
- return;
-
- if (dispatch->lid.keyboard)
- return;
-
- if (keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) {
- dispatch->lid.keyboard = keyboard;
- 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,
- dispatch->lid.is_closed);
- }
-}
-
-static void
-fallback_interface_device_added(struct evdev_device *device,
- struct evdev_device *added_device)
-{
- fallback_lid_pair_keyboard(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);
-
- if (removed_device == dispatch->lid.keyboard) {
- libinput_device_remove_event_listener(
- &dispatch->lid.listener);
- libinput_device_init_event_listener(
- &dispatch->lid.listener);
- dispatch->lid.keyboard = NULL;
- }
-}
-
-struct evdev_dispatch_interface fallback_interface = {
- .process = fallback_process,
- .suspend = fallback_suspend,
- .remove = fallback_remove,
- .destroy = fallback_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_sync_initial_state,
- .toggle_touch = fallback_toggle_touch,
- .get_switch_state = fallback_get_switch_state,
-};
-
static uint32_t
evdev_sendevents_get_modes(struct libinput_device *device)
{
@@ -1804,20 +537,6 @@ evdev_left_handed_has(struct libinput_device *device)
return 1;
}
-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 enum libinput_config_status
evdev_left_handed_set(struct libinput_device *device, int left_handed)
{
@@ -1866,22 +585,6 @@ evdev_scroll_get_methods(struct libinput_device *device)
return LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN;
}
-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)
- return;
-
- if (fallback_any_button_down(dispatch, device))
- return;
-
- device->scroll.method = device->scroll.want_method;
- device->scroll.button = device->scroll.want_button;
-}
-
static enum libinput_config_status
evdev_scroll_set_method(struct libinput_device *device,
enum libinput_config_scroll_method method)
@@ -1964,7 +667,7 @@ evdev_scroll_get_default_button(struct libinput_device *device)
return 0;
}
-static void
+void
evdev_init_button_scroll(struct evdev_device *device,
void (*change_scroll_method)(struct evdev_device *))
{
@@ -2062,58 +765,7 @@ evdev_init_natural_scroll(struct evdev_device *device)
device->base.config.natural_scroll = &device->scroll.config_natural;
}
-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
+int
evdev_need_mtdev(struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
@@ -2126,7 +778,7 @@ evdev_need_mtdev(struct evdev_device *device)
/* Fake MT devices have the ABS_MT_SLOT bit set because of
the limited ABS_* range - they aren't MT devices, they
just have too many ABS_ axes */
-static inline bool
+bool
evdev_is_fake_mt_device(struct evdev_device *device)
{
struct libevdev *evdev = device->evdev;
@@ -2135,89 +787,7 @@ evdev_is_fake_mt_device(struct evdev_device *device)
libevdev_get_num_slots(evdev) == -1;
}
-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;
-
- 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 enum switch_reliability
+enum switch_reliability
evdev_read_switch_reliability_prop(struct evdev_device *device)
{
const char *prop;
@@ -2239,93 +809,6 @@ evdev_read_switch_reliability_prop(struct evdev_device *device)
}
static inline void
-fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
- struct evdev_device *device)
-{
- int val;
-
- if (device->tags & EVDEV_TAG_LID_SWITCH) {
- libinput_device_init_event_listener(&dispatch->lid.listener);
- 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.state = val;
- }
-}
-
-static struct evdev_dispatch *
-fallback_dispatch_create(struct libinput_device *libinput_device)
-{
- struct evdev_device *device = evdev_device(libinput_device);
- struct fallback_dispatch *dispatch;
- char timer_name[64];
-
- 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;
-
- 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);
- }
-
- snprintf(timer_name,
- sizeof(timer_name),
- "%s debounce",
- evdev_device_get_sysname(device));
- libinput_timer_init(&dispatch->debounce.timer,
- evdev_libinput_context(device),
- timer_name,
- fallback_debounce_timeout,
- device);
- return &dispatch->base;
-}
-
-static inline void
evdev_process_event(struct evdev_device *device, struct input_event *e)
{
struct evdev_dispatch *dispatch = device->dispatch;
diff --git a/src/evdev.h b/src/evdev.h
index d2aa85bd..4c42f6eb 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -345,78 +345,6 @@ evdev_verify_dispatch_type(struct evdev_dispatch *dispatch,
abort();
}
-struct fallback_dispatch {
- struct evdev_dispatch base;
- struct evdev_device *device;
-
- struct libinput_device_config_calibration calibration;
-
- struct {
- bool is_enabled;
- int angle;
- struct matrix matrix;
- struct libinput_device_config_rotation config;
- } rotation;
-
- struct {
- struct device_coords point;
- int32_t seat_slot;
-
- struct {
- struct device_coords min, max;
- struct ratelimit range_warn_limit;
- } warning_range;
- } abs;
-
- struct {
- int slot;
- struct mt_slot *slots;
- size_t slots_len;
- bool want_hysteresis;
- struct device_coords hysteresis_margin;
- } mt;
-
- struct device_coords rel;
-
- struct {
- int state;
- } tablet_mode;
-
- /* Bitmask of pressed keys used to ignore initial release events from
- * the kernel. */
- unsigned long hw_key_mask[NLONGS(KEY_CNT)];
-
- enum evdev_event_type pending_event;
-
- /* true if we're reading events (i.e. not suspended) but we're
- ignoring them */
- bool ignore_events;
-
- struct {
- enum evdev_debounce_state state;
- unsigned int button_code;
- uint64_t button_up_time;
- struct libinput_timer timer;
- } debounce;
-
- struct {
- enum switch_reliability reliability;
-
- bool is_closed;
- bool is_closed_client_state;
- struct evdev_device *keyboard;
- struct libinput_event_listener listener;
- } lid;
-};
-
-static inline struct fallback_dispatch*
-fallback_dispatch(struct evdev_dispatch *dispatch)
-{
- evdev_verify_dispatch_type(dispatch, DISPATCH_FALLBACK);
-
- return container_of(dispatch, struct fallback_dispatch, base);
-}
-
struct evdev_device *
evdev_device_create(struct libinput_seat *seat,
struct udev_device *device);
@@ -436,6 +364,9 @@ evdev_init_calibration(struct evdev_device *device,
void
evdev_read_calibration_prop(struct evdev_device *device);
+enum switch_reliability
+evdev_read_switch_reliability_prop(struct evdev_device *device);
+
void
evdev_init_sendevents(struct evdev_device *device,
struct evdev_dispatch *dispatch);
@@ -459,6 +390,15 @@ evdev_tablet_pad_create(struct evdev_device *device);
struct evdev_dispatch *
evdev_lid_switch_dispatch_create(struct evdev_device *device);
+struct evdev_dispatch *
+fallback_dispatch_create(struct libinput_device *libinput_device);
+
+bool
+evdev_is_fake_mt_device(struct evdev_device *device);
+
+int
+evdev_need_mtdev(struct evdev_device *device);
+
void
evdev_device_led_update(struct evdev_device *device, enum libinput_led leds);
@@ -565,6 +505,15 @@ void
evdev_init_natural_scroll(struct evdev_device *device);
void
+evdev_init_button_scroll(struct evdev_device *device,
+ void (*change_scroll_method)(struct evdev_device *));
+
+int
+evdev_update_key_down_count(struct evdev_device *device,
+ int code,
+ int pressed);
+
+void
evdev_notify_axis(struct evdev_device *device,
uint64_t time,
uint32_t axes,
--
2.13.5
More information about the wayland-devel
mailing list