diff --git a/meson.build b/meson.build index fe9b18d2..1b372af4 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..d7096a7d --- /dev/null +++ b/src/evdev-fallback.c @@ -0,0 +1,1674 @@ +/* + * Copyright © 2010 Intel Corporation + * Copyright © 2013 Jonas Ådahl + * Copyright © 2013-2017 Red Hat, Inc. + * Copyright © 2017 James Ye + * + * 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 + +#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; + + /* We allow up to 3 paired keyboards for the lid switch + * listener. Only one keyboard should exist, but that can + * have more than one event node. + * + * Note: this is a sparse list, any element may have a + * non-NULL device. + */ + struct paired_keyboard { + struct evdev_device *device; + struct libinput_event_listener listener; + } paired_keyboard[3]; + } 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, + struct paired_keyboard *kbd, + bool is_closed) +{ + assert(kbd->device); + + if (is_closed) { + libinput_device_add_event_listener( + &kbd->device->base, + &kbd->listener, + fallback_lid_keyboard_event, + dispatch); + } else { + libinput_device_remove_event_listener( + &kbd->listener); + libinput_device_init_event_listener( + &kbd->listener); + } +} + +static void +fallback_lid_toggle_keyboard_listeners(struct fallback_dispatch *dispatch, + bool is_closed) +{ + struct paired_keyboard *kbd; + + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { + if (!kbd->device) + continue; + + fallback_lid_toggle_keyboard_listener(dispatch, + kbd, + is_closed); + } +} + +static inline void +fallback_process_switch(struct fallback_dispatch *dispatch, + struct evdev_device *device, + struct input_event *e, + uint64_t time) +{ + enum libinput_switch_state state; + bool is_closed; + + switch (e->code) { + case SW_LID: + is_closed = !!e->value; + + if (dispatch->lid.is_closed == is_closed) + return; + + fallback_lid_toggle_keyboard_listeners(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); + struct paired_keyboard *kbd; + + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { + if (!kbd->device) + continue; + + libinput_device_remove_event_listener(&kbd->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); + struct paired_keyboard *kbd; + bool paired = false; + + if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 || + (lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0) + return; + + if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0) + return; + + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { + if (kbd->device) + continue; + + kbd->device = 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, + kbd, + dispatch->lid.is_closed); + paired = true; + break; + } + + if (!paired) + evdev_log_bug_libinput(lid_switch, + "lid: too many internal keyboards\n"); +} + +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); + struct paired_keyboard *kbd; + + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { + if (!kbd->device) + continue; + + if (kbd->device != removed_device) + continue; + + libinput_device_remove_event_listener(&kbd->listener); + libinput_device_init_event_listener(&kbd->listener); + kbd->device = 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) { + struct paired_keyboard *kbd; + + ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) + libinput_device_init_event_listener(&kbd->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 9c6defa7..c490cae0 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,828 +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, - struct paired_keyboard *kbd, - bool is_closed) -{ - assert(kbd->device); - - if (is_closed) { - libinput_device_add_event_listener( - &kbd->device->base, - &kbd->listener, - fallback_lid_keyboard_event, - dispatch); - } else { - libinput_device_remove_event_listener( - &kbd->listener); - libinput_device_init_event_listener( - &kbd->listener); - } -} - -static void -fallback_lid_toggle_keyboard_listeners(struct fallback_dispatch *dispatch, - bool is_closed) -{ - struct paired_keyboard *kbd; - - ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { - if (!kbd->device) - continue; - - fallback_lid_toggle_keyboard_listener(dispatch, - kbd, - is_closed); - } -} - -static inline void -fallback_process_switch(struct fallback_dispatch *dispatch, - struct evdev_device *device, - struct input_event *e, - uint64_t time) -{ - enum libinput_switch_state state; - bool is_closed; - - switch (e->code) { - case SW_LID: - is_closed = !!e->value; - - if (dispatch->lid.is_closed == is_closed) - return; - - fallback_lid_toggle_keyboard_listeners(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, @@ -1265,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) @@ -1442,226 +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); - struct paired_keyboard *kbd; - - ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { - if (!kbd->device) - continue; - - libinput_device_remove_event_listener(&kbd->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) { @@ -1703,91 +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); - struct paired_keyboard *kbd; - bool paired = false; - - - if ((keyboard->tags & EVDEV_TAG_KEYBOARD) == 0 || - (lid_switch->tags & EVDEV_TAG_LID_SWITCH) == 0) - return; - - if ((keyboard->tags & EVDEV_TAG_INTERNAL_KEYBOARD) == 0) - return; - - ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { - if (kbd->device) - continue; - - kbd->device = 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, - kbd, - dispatch->lid.is_closed); - paired = true; - break; - } - - if (!paired) - evdev_log_bug_libinput(lid_switch, - "lid: too many internal keyboards\n"); -} - -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); - struct paired_keyboard *kbd; - - ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { - if (!kbd->device) - continue; - - if (kbd->device != removed_device) - continue; - - libinput_device_remove_event_listener(&kbd->listener); - libinput_device_init_event_listener(&kbd->listener); - kbd->device = 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) { @@ -1843,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) { @@ -1905,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) @@ -2003,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 *)) { @@ -2101,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; @@ -2165,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; @@ -2174,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; @@ -2277,97 +808,6 @@ evdev_read_switch_reliability_prop(struct evdev_device *device) return r; } -static inline void -fallback_dispatch_init_switch(struct fallback_dispatch *dispatch, - struct evdev_device *device) -{ - int val; - - if (device->tags & EVDEV_TAG_LID_SWITCH) { - struct paired_keyboard *kbd; - - ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) - libinput_device_init_event_listener(&kbd->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) { diff --git a/src/evdev.h b/src/evdev.h index b1cc2a75..4c42f6eb 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -345,88 +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; - - /* We allow up to 3 paired keyboards for the lid switch - * listener. Only one keyboard should exist, but that can - * have more than one event node. - * - * Note: this is a sparse list, any element may have a - * non-NULL device. - */ - struct paired_keyboard { - struct evdev_device *device; - struct libinput_event_listener listener; - } paired_keyboard[3]; - } 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); @@ -446,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); @@ -469,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); @@ -574,6 +504,15 @@ evdev_pointer_notify_physical_button(struct evdev_device *device, 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,