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 <[email protected]>
---
 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 <[email protected]>
+ *
+ * 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

_______________________________________________
wayland-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to