/* * Copyright © 2014-2015 Red Hat, Inc. * * 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. */ #ifndef EVDEV_MT_TOUCHPAD_H #define EVDEV_MT_TOUCHPAD_H #include #include "evdev.h" #include "filter.h" #include "timer.h" #define TOUCHPAD_HISTORY_LENGTH 4 #define TOUCHPAD_MIN_SAMPLES 4 /* Convert mm to a distance normalized to DEFAULT_MOUSE_DPI */ #define TP_MM_TO_DPI_NORMALIZED(mm) (DEFAULT_MOUSE_DPI/25.4 * mm) enum touchpad_event { TOUCHPAD_EVENT_NONE = 0, TOUCHPAD_EVENT_MOTION = (1 << 0), TOUCHPAD_EVENT_BUTTON_PRESS = (1 << 1), TOUCHPAD_EVENT_BUTTON_RELEASE = (1 << 2), TOUCHPAD_EVENT_OTHERAXIS = (1 << 3), }; enum touchpad_model { MODEL_UNKNOWN = 0, MODEL_SYNAPTICS, MODEL_ALPS, MODEL_APPLETOUCH, MODEL_ELANTECH, MODEL_UNIBODY_MACBOOK }; enum touch_state { TOUCH_NONE = 0, TOUCH_HOVERING, TOUCH_BEGIN, TOUCH_UPDATE, TOUCH_END }; enum touch_palm_state { PALM_NONE = 0, PALM_EDGE, PALM_TYPING, PALM_TRACKPOINT, }; enum button_event { BUTTON_EVENT_IN_BOTTOM_R = 30, BUTTON_EVENT_IN_BOTTOM_M, BUTTON_EVENT_IN_BOTTOM_L, BUTTON_EVENT_IN_TOP_R, BUTTON_EVENT_IN_TOP_M, BUTTON_EVENT_IN_TOP_L, BUTTON_EVENT_IN_AREA, BUTTON_EVENT_UP, BUTTON_EVENT_PRESS, BUTTON_EVENT_RELEASE, BUTTON_EVENT_TIMEOUT, }; enum button_state { BUTTON_STATE_NONE, BUTTON_STATE_AREA, BUTTON_STATE_BOTTOM, BUTTON_STATE_TOP, BUTTON_STATE_TOP_NEW, BUTTON_STATE_TOP_TO_IGNORE, BUTTON_STATE_IGNORE, }; enum tp_tap_state { TAP_STATE_IDLE = 4, TAP_STATE_TOUCH, TAP_STATE_HOLD, TAP_STATE_TAPPED, TAP_STATE_TOUCH_2, TAP_STATE_TOUCH_2_HOLD, TAP_STATE_TOUCH_2_RELEASE, TAP_STATE_TOUCH_3, TAP_STATE_TOUCH_3_HOLD, TAP_STATE_DRAGGING_OR_DOUBLETAP, TAP_STATE_DRAGGING_OR_TAP, TAP_STATE_DRAGGING, TAP_STATE_DRAGGING_WAIT, TAP_STATE_DRAGGING_2, TAP_STATE_MULTITAP, TAP_STATE_MULTITAP_DOWN, TAP_STATE_DEAD, /**< finger count exceeded */ }; enum tp_tap_touch_state { TAP_TOUCH_STATE_IDLE = 16, /**< not in touch */ TAP_TOUCH_STATE_TOUCH, /**< touching, may tap */ TAP_TOUCH_STATE_DEAD, /**< exceeded motion/timeout */ }; /* For edge scrolling, so we only care about right and bottom */ enum tp_edge { EDGE_NONE = 0, EDGE_RIGHT = (1 << 0), EDGE_BOTTOM = (1 << 1), }; enum tp_edge_scroll_touch_state { EDGE_SCROLL_TOUCH_STATE_NONE, EDGE_SCROLL_TOUCH_STATE_EDGE_NEW, EDGE_SCROLL_TOUCH_STATE_EDGE, EDGE_SCROLL_TOUCH_STATE_AREA, }; enum tp_gesture_state { GESTURE_STATE_NONE, GESTURE_STATE_UNKNOWN, GESTURE_STATE_SCROLL, GESTURE_STATE_PINCH, GESTURE_STATE_SWIPE, }; enum tp_thumb_state { THUMB_STATE_NO, THUMB_STATE_YES, THUMB_STATE_MAYBE, }; struct tp_touch { struct tp_dispatch *tp; enum touch_state state; bool has_ended; /* TRACKING_ID == -1 */ bool dirty; struct device_coords point; uint64_t millis; int pressure; bool was_down; /* if distance == 0, false for pure hovering touches */ struct { /* A quirk mostly used on Synaptics touchpads. In a transition to/from fake touches > num_slots, the current event data is likely garbage and the subsequent event is likely too. This marker tells us to reset the motion history again -> this effectively swallows any motion */ bool reset_motion_history; } quirks; struct { struct device_coords samples[TOUCHPAD_HISTORY_LENGTH]; unsigned int index; unsigned int count; } history; struct device_coords hysteresis_center; /* A pinned touchpoint is the one that pressed the physical button * on a clickpad. After the release, it won't move until the center * moves more than a threshold away from the original coordinates */ struct { bool is_pinned; struct device_coords center; } pinned; /* Software-button state and timeout if applicable */ struct { enum button_state state; /* We use button_event here so we can use == on events */ enum button_event curr; struct libinput_timer timer; } button; struct { enum tp_tap_touch_state state; struct device_coords initial; bool is_thumb; } tap; struct { enum tp_edge_scroll_touch_state edge_state; uint32_t edge; int direction; struct libinput_timer timer; struct device_coords initial; } scroll; struct { enum touch_palm_state state; struct device_coords first; /* first coordinates if is_palm == true */ uint64_t time; /* first timestamp if is_palm == true */ } palm; struct { struct device_coords initial; } gesture; struct { enum tp_thumb_state state; uint64_t first_touch_time; struct device_coords initial; } thumb; }; struct tp_dispatch { struct evdev_dispatch base; struct evdev_device *device; unsigned int nfingers_down; /* number of fingers down */ unsigned int old_nfingers_down; /* previous no fingers down */ unsigned int slot; /* current slot */ bool has_mt; bool semi_mt; /* true if we're reading events (i.e. not suspended) but we're * ignoring them */ bool ignore_events; unsigned int num_slots; /* number of slots */ unsigned int ntouches; /* no slots inc. fakes */ struct tp_touch *touches; /* len == ntouches */ /* bit 0: BTN_TOUCH * bit 1: BTN_TOOL_FINGER * bit 2: BTN_TOOL_DOUBLETAP * ... */ unsigned int fake_touches; /* if pressure goes above high -> touch down, if pressure then goes below low -> touch up */ struct { bool use_pressure; int high; int low; } pressure; struct device_coords hysteresis_margin; struct { double x_scale_coeff; double y_scale_coeff; } accel; struct { bool enabled; bool started; unsigned int finger_count; unsigned int finger_count_pending; struct libinput_timer finger_count_switch_timer; enum tp_gesture_state state; struct tp_touch *touches[2]; uint64_t initial_time; double initial_distance; double prev_scale; double angle; struct device_float_coords center; } gesture; struct { bool is_clickpad; /* true for clickpads */ bool has_topbuttons; bool use_clickfinger; /* number of fingers decides button number */ bool click_pending; uint32_t state; uint32_t old_state; struct { double x_scale_coeff; double y_scale_coeff; } motion_dist; /* for pinned touches */ unsigned int active; /* currently active button, for release event */ bool active_is_topbutton; /* is active a top button? */ /* Only used for clickpads. The software button areas are * always 2 horizontal stripes across the touchpad. * The buttons are split according to the edge settings. */ struct { int32_t top_edge; /* in device coordinates */ int32_t rightbutton_left_edge; /* in device coordinates */ int32_t middlebutton_left_edge; /* in device coordinates */ } bottom_area; struct { int32_t bottom_edge; /* in device coordinates */ int32_t rightbutton_left_edge; /* in device coordinates */ int32_t leftbutton_right_edge; /* in device coordinates */ } top_area; struct evdev_device *trackpoint; enum libinput_config_click_method click_method; struct libinput_device_config_click_method config_method; } buttons; struct { struct libinput_device_config_scroll_method config_method; enum libinput_config_scroll_method method; int32_t right_edge; /* in device coordinates */ int32_t bottom_edge; /* in device coordinates */ } scroll; enum touchpad_event queued; struct { struct libinput_device_config_tap config; bool enabled; bool suspended; struct libinput_timer timer; enum tp_tap_state state; uint32_t buttons_pressed; uint64_t first_press_time; enum libinput_config_tap_button_map map; enum libinput_config_tap_button_map want_map; bool drag_enabled; bool drag_lock_enabled; } tap; struct { int32_t right_edge; /* in device coordinates */ int32_t left_edge; /* in device coordinates */ bool trackpoint_active; struct libinput_event_listener trackpoint_listener; struct libinput_timer trackpoint_timer; uint64_t trackpoint_last_event_time; uint32_t trackpoint_event_count; bool monitor_trackpoint; } palm; struct { struct libinput_device_config_send_events config; enum libinput_config_send_events_mode current_mode; } sendevents; struct { struct libinput_device_config_dwt config; bool dwt_enabled; bool keyboard_active; struct libinput_event_listener keyboard_listener; struct libinput_timer keyboard_timer; struct evdev_device *keyboard; unsigned long key_mask[NLONGS(KEY_CNT)]; unsigned long mod_mask[NLONGS(KEY_CNT)]; uint64_t keyboard_last_press_time; } dwt; struct { bool detect_thumbs; int threshold; int upper_thumb_line; int lower_thumb_line; } thumb; struct { /* A quirk used on the T450 series Synaptics hardware. * Slowly moving the finger causes multiple events with only * ABS_MT_PRESSURE but no x/y information. When the x/y * event comes, it will be a jump of ~20 units. We use the * below to count non-motion events to discard that first * event with the jump. */ unsigned int nonmotion_event_count; } quirks; struct { struct libinput_event_listener lid_switch_listener; struct evdev_device *lid_switch; } lid_switch; }; static inline struct tp_dispatch* tp_dispatch(struct evdev_dispatch *dispatch) { struct tp_dispatch *tp; evdev_verify_dispatch_type(dispatch, DISPATCH_TOUCHPAD); return container_of(dispatch, tp, base); } #define tp_for_each_touch(_tp, _t) \ for (unsigned int _i = 0; _i < (_tp)->ntouches && (_t = &(_tp)->touches[_i]); _i++) static inline struct libinput* tp_libinput_context(const struct tp_dispatch *tp) { return evdev_libinput_context(tp->device); } static inline struct normalized_coords tp_normalize_delta(const struct tp_dispatch *tp, struct device_float_coords delta) { struct normalized_coords normalized; normalized.x = delta.x * tp->accel.x_scale_coeff; normalized.y = delta.y * tp->accel.y_scale_coeff; return normalized; } static inline struct phys_coords tp_phys_delta(const struct tp_dispatch *tp, struct device_float_coords delta) { struct phys_coords mm; mm.x = delta.x / tp->device->abs.absinfo_x->resolution; mm.y = delta.y / tp->device->abs.absinfo_y->resolution; return mm; } /** * Takes a dpi-normalized set of coordinates, returns a set of coordinates * in the x-axis' coordinate space. */ static inline struct device_float_coords tp_unnormalize_for_xaxis(const struct tp_dispatch *tp, struct normalized_coords delta) { struct device_float_coords raw; raw.x = delta.x / tp->accel.x_scale_coeff; raw.y = delta.y / tp->accel.x_scale_coeff; /* <--- not a typo */ return raw; } struct normalized_coords tp_get_delta(struct tp_touch *t); struct normalized_coords tp_filter_motion(struct tp_dispatch *tp, const struct normalized_coords *unaccelerated, uint64_t time); struct normalized_coords tp_filter_motion_unaccelerated(struct tp_dispatch *tp, const struct normalized_coords *unaccelerated, uint64_t time); bool tp_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t); int tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time); void tp_tap_post_process_state(struct tp_dispatch *tp); void tp_init_tap(struct tp_dispatch *tp); void tp_remove_tap(struct tp_dispatch *tp); void tp_init_buttons(struct tp_dispatch *tp, struct evdev_device *device); void tp_init_top_softbuttons(struct tp_dispatch *tp, struct evdev_device *device, double topbutton_size_mult); void tp_remove_buttons(struct tp_dispatch *tp); void tp_process_button(struct tp_dispatch *tp, const struct input_event *e, uint64_t time); void tp_release_all_buttons(struct tp_dispatch *tp, uint64_t time); int tp_post_button_events(struct tp_dispatch *tp, uint64_t time); void tp_button_handle_state(struct tp_dispatch *tp, uint64_t time); bool tp_button_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t); bool tp_button_is_inside_softbutton_area(const struct tp_dispatch *tp, const struct tp_touch *t); void tp_release_all_taps(struct tp_dispatch *tp, uint64_t time); void tp_tap_suspend(struct tp_dispatch *tp, uint64_t time); void tp_tap_resume(struct tp_dispatch *tp, uint64_t time); bool tp_tap_dragging(const struct tp_dispatch *tp); void tp_edge_scroll_init(struct tp_dispatch *tp, struct evdev_device *device); void tp_remove_edge_scroll(struct tp_dispatch *tp); void tp_edge_scroll_handle_state(struct tp_dispatch *tp, uint64_t time); int tp_edge_scroll_post_events(struct tp_dispatch *tp, uint64_t time); void tp_edge_scroll_stop_events(struct tp_dispatch *tp, uint64_t time); int tp_edge_scroll_touch_active(const struct tp_dispatch *tp, const struct tp_touch *t); uint32_t tp_touch_get_edge(const struct tp_dispatch *tp, const struct tp_touch *t); void tp_init_gesture(struct tp_dispatch *tp); void tp_remove_gesture(struct tp_dispatch *tp); void tp_gesture_stop(struct tp_dispatch *tp, uint64_t time); void tp_gesture_cancel(struct tp_dispatch *tp, uint64_t time); void tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time); void tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time); void tp_gesture_stop_twofinger_scroll(struct tp_dispatch *tp, uint64_t time); bool tp_palm_tap_is_palm(const struct tp_dispatch *tp, const struct tp_touch *t); void tp_clickpad_middlebutton_apply_config(struct evdev_device *device); #endif