/* * 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 { /* The struct for the tablet mode switch device itself */ struct { int state; } sw; /* The struct for other devices listening to the tablet mode switch */ struct { struct evdev_device *sw_device; struct libinput_event_listener listener; } other; } 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_interface_get_switch_state(struct evdev_dispatch *evdev_dispatch, enum libinput_switch sw) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); switch (sw) { case LIBINPUT_SWITCH_TABLET_MODE: break; default: /* Internal function only, so we can abort here */ abort(); } return dispatch->tablet_mode.sw.state ? LIBINPUT_SWITCH_STATE_ON : LIBINPUT_SWITCH_STATE_OFF; } static inline void normalize_delta(struct evdev_device *device, const struct device_coords *delta, struct normalized_coords *normalized) { normalized->x = delta->x * DEFAULT_MOUSE_DPI / (double)device->dpi; normalized->y = delta->y * DEFAULT_MOUSE_DPI / (double)device->dpi; } static inline bool post_trackpoint_scroll(struct evdev_device *device, struct normalized_coords unaccel, uint64_t time) { if (device->scroll.method != LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) return false; switch(device->scroll.button_scroll_state) { case BUTTONSCROLL_IDLE: return false; case BUTTONSCROLL_BUTTON_DOWN: /* if the button is down but scroll is not active, we're within the timeout where 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); libinput_device_remove_event_listener(&kbd->listener); if (is_closed) { libinput_device_add_event_listener( &kbd->device->base, &kbd->listener, fallback_lid_keyboard_event, dispatch); } else { libinput_device_init_event_listener(&kbd->listener); } } static void fallback_lid_toggle_keyboard_listeners(struct fallback_dispatch *dispatch, bool is_closed) { struct 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; fallback_lid_toggle_keyboard_listeners(dispatch, is_closed); if (dispatch->lid.is_closed == is_closed) return; dispatch->lid.is_closed = is_closed; fallback_lid_notify_toggle(dispatch, device, time); break; case SW_TABLET_MODE: if (dispatch->tablet_mode.sw.state == e->value) return; dispatch->tablet_mode.sw.state = e->value; if (e->value) state = LIBINPUT_SWITCH_STATE_ON; else state = LIBINPUT_SWITCH_STATE_OFF; switch_notify_toggle(&device->base, time, LIBINPUT_SWITCH_TABLET_MODE, state); break; } } static inline bool fallback_reject_relative(struct evdev_device *device, const struct input_event *e, uint64_t time) { if ((e->code == REL_X || e->code == REL_Y) && (device->seat_caps & EVDEV_DEVICE_POINTER) == 0) { evdev_log_bug_libinput_ratelimit(device, &device->nonpointer_rel_limit, "REL_X/Y from a non-pointer device\n"); return true; } return false; } static inline void fallback_process_relative(struct fallback_dispatch *dispatch, struct evdev_device *device, struct input_event *e, uint64_t time) { 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_interface_process(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, struct input_event *event, uint64_t time) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); 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_interface_suspend(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); fallback_return_to_neutral_state(dispatch, device); } static void fallback_interface_remove(struct evdev_dispatch *evdev_dispatch) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); struct paired_keyboard *kbd; libinput_device_remove_event_listener(&dispatch->tablet_mode.other.listener); ARRAY_FOR_EACH(dispatch->lid.paired_keyboard, kbd) { if (!kbd->device) continue; libinput_device_remove_event_listener(&kbd->listener); } } static void fallback_interface_sync_initial_state(struct evdev_device *device, struct evdev_dispatch *evdev_dispatch) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); uint64_t time = libinput_now(evdev_libinput_context(device)); if (device->tags & EVDEV_TAG_LID_SWITCH) { struct libevdev *evdev = device->evdev; dispatch->lid.is_closed = libevdev_get_event_value(evdev, EV_SW, SW_LID); dispatch->lid.is_closed_client_state = false; /* For the initial state sync, we depend on whether the lid switch * is reliable. If we know it's reliable, we sync as expected. * If we're not sure, we ignore the initial state and only sync on * the first future lid close event. Laptops with a broken switch * that always have the switch in 'on' state thus don't mess up our * touchpad. */ if (dispatch->lid.is_closed && dispatch->lid.reliability == RELIABILITY_RELIABLE) { fallback_lid_notify_toggle(dispatch, device, time); } } if (dispatch->tablet_mode.sw.state) { switch_notify_toggle(&device->base, time, LIBINPUT_SWITCH_TABLET_MODE, LIBINPUT_SWITCH_STATE_ON); } } static void fallback_interface_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_interface_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_resume(struct fallback_dispatch *dispatch, struct evdev_device *device) { if (dispatch->base.sendevents.current_mode == LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) return; evdev_device_resume(device); } static void fallback_suspend(struct fallback_dispatch *dispatch, struct evdev_device *device) { evdev_device_suspend(device); } static void fallback_tablet_mode_switch_event(uint64_t time, struct libinput_event *event, void *data) { struct fallback_dispatch *dispatch = data; struct evdev_device *device = dispatch->device; struct libinput_event_switch *swev; if (libinput_event_get_type(event) != LIBINPUT_EVENT_SWITCH_TOGGLE) return; swev = libinput_event_get_switch_event(event); if (libinput_event_switch_get_switch(swev) != LIBINPUT_SWITCH_TABLET_MODE) return; switch (libinput_event_switch_get_switch_state(swev)) { case LIBINPUT_SWITCH_STATE_OFF: fallback_resume(dispatch, device); evdev_log_debug(device, "tablet-mode: resuming device\n"); break; case LIBINPUT_SWITCH_STATE_ON: fallback_suspend(dispatch, device); evdev_log_debug(device, "tablet-mode: suspending device\n"); break; } } static void fallback_keyboard_pair_tablet_mode(struct evdev_device *keyboard, struct evdev_device *tablet_mode_switch) { struct fallback_dispatch *dispatch = fallback_dispatch(keyboard->dispatch); if ((keyboard->tags & (EVDEV_TAG_TRACKPOINT|EVDEV_TAG_INTERNAL_KEYBOARD)) == 0) return; if ((tablet_mode_switch->tags & EVDEV_TAG_TABLET_MODE_SWITCH) == 0) return; if (dispatch->tablet_mode.other.sw_device) return; evdev_log_debug(keyboard, "tablet_mode_switch: activated for %s<->%s\n", keyboard->devname, tablet_mode_switch->devname); libinput_device_add_event_listener(&tablet_mode_switch->base, &dispatch->tablet_mode.other.listener, fallback_tablet_mode_switch_event, dispatch); dispatch->tablet_mode.other.sw_device = tablet_mode_switch; if (evdev_device_switch_get_state(tablet_mode_switch, LIBINPUT_SWITCH_TABLET_MODE) == LIBINPUT_SWITCH_STATE_ON) fallback_suspend(dispatch, keyboard); } static void fallback_interface_device_added(struct evdev_device *device, struct evdev_device *added_device) { fallback_lid_pair_keyboard(device, added_device); fallback_keyboard_pair_tablet_mode(device, added_device); } static void fallback_interface_device_removed(struct evdev_device *device, struct evdev_device *removed_device) { struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); struct 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; } if (removed_device == dispatch->tablet_mode.other.sw_device) { libinput_device_remove_event_listener( &dispatch->tablet_mode.other.listener); libinput_device_init_event_listener( &dispatch->tablet_mode.other.listener); dispatch->tablet_mode.other.sw_device = NULL; } } struct evdev_dispatch_interface fallback_interface = { .process = fallback_interface_process, .suspend = fallback_interface_suspend, .remove = fallback_interface_remove, .destroy = fallback_interface_destroy, .device_added = fallback_interface_device_added, .device_removed = fallback_interface_device_removed, .device_suspended = fallback_interface_device_removed, /* treat as remove */ .device_resumed = fallback_interface_device_added, /* treat as add */ .post_added = fallback_interface_sync_initial_state, .toggle_touch = fallback_interface_toggle_touch, .get_switch_state = fallback_interface_get_switch_state, }; static void fallback_change_to_left_handed(struct evdev_device *device) { struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); if (device->left_handed.want_enabled == device->left_handed.enabled) return; if (fallback_any_button_down(dispatch, device)) return; device->left_handed.enabled = device->left_handed.want_enabled; } static void fallback_change_scroll_method(struct evdev_device *device) { struct fallback_dispatch *dispatch = fallback_dispatch(device->dispatch); if (device->scroll.want_method == device->scroll.method && device->scroll.want_button == device->scroll.button) 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.sw.state = val; } libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener); } 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; }