2 * Copyright © 2014 Red Hat, Inc.
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 #include "evdev-mt-touchpad.h"
32 #define DEFAULT_ACCEL_NUMERATOR 1200.0
33 #define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
34 #define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
37 tp_hysteresis(int in, int center, int margin)
39 int diff = in - center;
40 if (abs(diff) <= margin)
44 return center + diff - margin;
46 return center + diff + margin;
49 static inline struct tp_motion *
50 tp_motion_history_offset(struct tp_touch *t, int offset)
53 (t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
54 TOUCHPAD_HISTORY_LENGTH;
56 return &t->history.samples[offset_index];
60 tp_filter_motion(struct tp_dispatch *tp,
61 double *dx, double *dy,
62 double *dx_unaccel, double *dy_unaccel,
65 struct motion_params motion;
67 motion.dx = *dx * tp->accel.x_scale_coeff;
68 motion.dy = *dy * tp->accel.y_scale_coeff;
71 *dx_unaccel = motion.dx;
73 *dy_unaccel = motion.dy;
75 if (motion.dx != 0.0 || motion.dy != 0.0)
76 filter_dispatch(tp->device->pointer.filter, &motion, tp, time);
83 tp_motion_history_push(struct tp_touch *t)
85 int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
87 if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
90 t->history.samples[motion_index].x = t->x;
91 t->history.samples[motion_index].y = t->y;
92 t->history.index = motion_index;
96 tp_motion_hysteresis(struct tp_dispatch *tp,
102 if (t->history.count == 0) {
103 t->hysteresis.center_x = t->x;
104 t->hysteresis.center_y = t->y;
107 t->hysteresis.center_x,
108 tp->hysteresis.margin_x);
110 t->hysteresis.center_y,
111 tp->hysteresis.margin_y);
112 t->hysteresis.center_x = x;
113 t->hysteresis.center_y = y;
120 tp_motion_history_reset(struct tp_touch *t)
122 t->history.count = 0;
125 static inline struct tp_touch *
126 tp_current_touch(struct tp_dispatch *tp)
128 return &tp->touches[min(tp->slot, tp->ntouches - 1)];
131 static inline struct tp_touch *
132 tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
134 assert(slot < tp->ntouches);
135 return &tp->touches[slot];
139 tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
141 if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE)
144 tp_motion_history_reset(t);
146 t->state = TOUCH_BEGIN;
147 t->pinned.is_pinned = false;
150 assert(tp->nfingers_down >= 1);
151 tp->queued |= TOUCHPAD_EVENT_MOTION;
155 tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
157 if (t->state == TOUCH_END || t->state == TOUCH_NONE)
161 t->is_pointer = false;
162 t->palm.is_palm = false;
163 t->state = TOUCH_END;
164 t->pinned.is_pinned = false;
166 assert(tp->nfingers_down >= 1);
168 tp->queued |= TOUCHPAD_EVENT_MOTION;
172 tp_estimate_delta(int x0, int x1, int x2, int x3)
174 return (x0 + x1 - x2 - x3) / 4.0;
178 tp_get_delta(struct tp_touch *t, double *dx, double *dy)
180 if (t->history.count < 4) {
186 *dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
187 tp_motion_history_offset(t, 1)->x,
188 tp_motion_history_offset(t, 2)->x,
189 tp_motion_history_offset(t, 3)->x);
190 *dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
191 tp_motion_history_offset(t, 1)->y,
192 tp_motion_history_offset(t, 2)->y,
193 tp_motion_history_offset(t, 3)->y);
197 tp_process_absolute(struct tp_dispatch *tp,
198 const struct input_event *e,
201 struct tp_touch *t = tp_current_touch(tp);
204 case ABS_MT_POSITION_X:
208 tp->queued |= TOUCHPAD_EVENT_MOTION;
210 case ABS_MT_POSITION_Y:
214 tp->queued |= TOUCHPAD_EVENT_MOTION;
219 case ABS_MT_TRACKING_ID:
221 tp_begin_touch(tp, t, time);
223 tp_end_touch(tp, t, time);
228 tp_process_absolute_st(struct tp_dispatch *tp,
229 const struct input_event *e,
232 struct tp_touch *t = tp_current_touch(tp);
239 tp->queued |= TOUCHPAD_EVENT_MOTION;
245 tp->queued |= TOUCHPAD_EVENT_MOTION;
251 tp_process_fake_touch(struct tp_dispatch *tp,
252 const struct input_event *e,
256 unsigned int fake_touches;
257 unsigned int nfake_touches;
258 unsigned int i, start;
261 if (e->code != BTN_TOUCH &&
262 (e->code < BTN_TOOL_DOUBLETAP || e->code > BTN_TOOL_QUADTAP))
265 shift = e->code == BTN_TOUCH ? 0 : (e->code - BTN_TOOL_DOUBLETAP + 1);
268 tp->fake_touches |= 1 << shift;
270 tp->fake_touches &= ~(0x1 << shift);
272 fake_touches = tp->fake_touches;
274 while (fake_touches) {
279 /* For single touch tps we use BTN_TOUCH for begin / end of touch 0 */
280 start = tp->has_mt ? tp->real_touches : 0;
281 for (i = start; i < tp->ntouches; i++) {
282 t = tp_get_touch(tp, i);
283 if (i < nfake_touches)
284 tp_begin_touch(tp, t, time);
286 tp_end_touch(tp, t, time);
289 /* On mt the actual touch info may arrive after BTN_TOOL_FOO */
290 assert(tp->has_mt || tp->nfingers_down == nfake_touches);
294 tp_process_key(struct tp_dispatch *tp,
295 const struct input_event *e,
302 tp_process_button(tp, e, time);
305 case BTN_TOOL_DOUBLETAP:
306 case BTN_TOOL_TRIPLETAP:
307 case BTN_TOOL_QUADTAP:
308 tp_process_fake_touch(tp, e, time);
314 tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
316 unsigned int xdist, ydist;
318 if (!t->pinned.is_pinned)
321 xdist = abs(t->x - t->pinned.center_x);
322 ydist = abs(t->y - t->pinned.center_y);
324 if (xdist * xdist + ydist * ydist >=
325 tp->buttons.motion_dist * tp->buttons.motion_dist) {
326 t->pinned.is_pinned = false;
327 tp_set_pointer(tp, t);
332 tp_pin_fingers(struct tp_dispatch *tp)
336 tp_for_each_touch(tp, t) {
337 t->is_pointer = false;
338 t->pinned.is_pinned = true;
339 t->pinned.center_x = t->x;
340 t->pinned.center_y = t->y;
345 tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
347 return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
349 !t->pinned.is_pinned &&
350 tp_button_touch_active(tp, t) &&
351 tp_edge_scroll_touch_active(tp, t);
355 tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
357 struct tp_touch *tmp = NULL;
359 /* Only set the touch as pointer if we don't have one yet */
360 tp_for_each_touch(tp, tmp) {
365 if (tp_touch_active(tp, t))
366 t->is_pointer = true;
370 tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
372 const int PALM_TIMEOUT = 200; /* ms */
373 const int DIRECTIONS = NE|E|SE|SW|W|NW;
375 /* If labelled a touch as palm, we unlabel as palm when
376 we move out of the palm edge zone within the timeout, provided
377 the direction is within 45 degrees of the horizontal.
379 if (t->palm.is_palm) {
380 if (time < t->palm.time + PALM_TIMEOUT &&
381 (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
382 int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
383 if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
384 t->palm.is_palm = false;
385 tp_set_pointer(tp, t);
391 /* palm must start in exclusion zone, it's ok to move into
392 the zone without being a palm */
393 if (t->state != TOUCH_BEGIN ||
394 (t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
397 /* don't detect palm in software button areas, it's
398 likely that legitimate touches start in the area
399 covered by the exclusion zone */
400 if (tp->buttons.is_clickpad &&
401 tp_button_is_inside_softbutton_area(tp, t))
404 t->palm.is_palm = true;
411 tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
415 double dx = 0, dy =0;
418 tp_for_each_touch(tp, t) {
419 if (tp_touch_active(tp, t) && t->dirty) {
421 tp_get_delta(t, &tmpx, &tmpy);
426 /* Stop spurious MOTION events at the end of scrolling */
427 t->is_pointer = false;
436 tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
438 evdev_post_scroll(tp->device, time, dx, dy);
442 tp_twofinger_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
445 int nfingers_down = 0;
447 /* No 2fg scrolling during tap-n-drag */
448 if (tp_tap_dragging(tp))
451 /* Only count active touches for 2 finger scrolling */
452 tp_for_each_touch(tp, t) {
453 if (tp_touch_active(tp, t))
457 if (nfingers_down != 2) {
458 evdev_stop_scroll(tp->device, time);
462 tp_post_twofinger_scroll(tp, time);
467 tp_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
469 /* Note this must be always called, so that it knows the state of
470 * touches when the scroll-mode changes.
472 tp_edge_scroll_handle_state(tp, time);
476 tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
478 struct libinput *libinput = tp->device->base.seat->libinput;
480 switch (tp->scroll.method) {
481 case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
483 case LIBINPUT_CONFIG_SCROLL_2FG:
484 return tp_twofinger_scroll_post_events(tp, time);
485 case LIBINPUT_CONFIG_SCROLL_EDGE:
486 return tp_edge_scroll_post_events(tp, time);
487 case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
488 log_bug_libinput(libinput, "Unexpected scroll mode\n");
495 tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
497 struct libinput *libinput = tp->device->base.seat->libinput;
499 switch (tp->scroll.method) {
500 case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
502 case LIBINPUT_CONFIG_SCROLL_2FG:
503 evdev_stop_scroll(tp->device, time);
505 case LIBINPUT_CONFIG_SCROLL_EDGE:
506 tp_edge_scroll_stop_events(tp, time);
508 case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
509 log_bug_libinput(libinput, "Unexpected scroll mode\n");
515 tp_remove_scroll(struct tp_dispatch *tp)
517 tp_remove_edge_scroll(tp);
521 tp_process_state(struct tp_dispatch *tp, uint64_t time)
524 struct tp_touch *first = tp_get_touch(tp, 0);
527 for (i = 0; i < tp->ntouches; i++) {
528 t = tp_get_touch(tp, i);
530 /* semi-mt finger postions may "jump" when nfingers changes */
531 if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
532 tp_motion_history_reset(t);
534 if (i >= tp->real_touches && t->state != TOUCH_NONE) {
538 t->dirty = first->dirty;
544 tp_palm_detect(tp, t, time);
546 tp_motion_hysteresis(tp, t);
547 tp_motion_history_push(t);
549 tp_unpin_finger(tp, t);
552 tp_button_handle_state(tp, time);
553 tp_scroll_handle_state(tp, time);
556 * We have a physical button down event on a clickpad. To avoid
557 * spurious pointer moves by the clicking finger we pin all fingers.
558 * We unpin fingers when they move more then a certain threshold to
559 * to allow drag and drop.
561 if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
562 tp->buttons.is_clickpad)
567 tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
571 tp_for_each_touch(tp, t) {
575 if (t->state == TOUCH_END)
576 t->state = TOUCH_NONE;
577 else if (t->state == TOUCH_BEGIN)
578 t->state = TOUCH_UPDATE;
583 tp->old_nfingers_down = tp->nfingers_down;
584 tp->buttons.old_state = tp->buttons.state;
586 tp->queued = TOUCHPAD_EVENT_NONE;
591 tp_post_events(struct tp_dispatch *tp, uint64_t time)
593 struct tp_touch *t = tp_current_touch(tp);
595 int filter_motion = 0;
596 double dx_unaccel, dy_unaccel;
598 /* Only post (top) button events while suspended */
599 if (tp->device->suspended) {
600 tp_post_button_events(tp, time);
604 filter_motion |= tp_tap_handle_state(tp, time);
605 filter_motion |= tp_post_button_events(tp, time);
607 if (filter_motion || tp->sendevents.trackpoint_active) {
608 tp_stop_scroll_events(tp, time);
612 if (tp_post_scroll_events(tp, time) != 0)
615 if (!t->is_pointer) {
616 tp_for_each_touch(tp, t) {
622 if (!t->is_pointer ||
624 t->history.count < TOUCHPAD_MIN_SAMPLES)
627 tp_get_delta(t, &dx, &dy);
628 tp_filter_motion(tp, &dx, &dy, &dx_unaccel, &dy_unaccel, time);
630 if (dx != 0.0 || dy != 0.0 || dx_unaccel != 0.0 || dy_unaccel != 0.0) {
631 pointer_notify_motion(&tp->device->base, time,
632 dx, dy, dx_unaccel, dy_unaccel);
637 tp_handle_state(struct tp_dispatch *tp,
640 tp_process_state(tp, time);
641 tp_post_events(tp, time);
642 tp_post_process_state(tp, time);
646 tp_process(struct evdev_dispatch *dispatch,
647 struct evdev_device *device,
648 struct input_event *e,
651 struct tp_dispatch *tp =
652 (struct tp_dispatch *)dispatch;
657 tp_process_absolute(tp, e, time);
659 tp_process_absolute_st(tp, e, time);
662 tp_process_key(tp, e, time);
665 tp_handle_state(tp, time);
671 tp_remove_sendevents(struct tp_dispatch *tp)
673 libinput_timer_cancel(&tp->sendevents.trackpoint_timer);
675 if (tp->buttons.trackpoint)
676 libinput_device_remove_event_listener(
677 &tp->sendevents.trackpoint_listener);
681 tp_remove(struct evdev_dispatch *dispatch)
683 struct tp_dispatch *tp =
684 (struct tp_dispatch*)dispatch;
687 tp_remove_buttons(tp);
688 tp_remove_sendevents(tp);
689 tp_remove_scroll(tp);
693 tp_destroy(struct evdev_dispatch *dispatch)
695 struct tp_dispatch *tp =
696 (struct tp_dispatch*)dispatch;
704 tp_clear_state(struct tp_dispatch *tp)
706 uint64_t now = libinput_now(tp->device->base.seat->libinput);
709 /* Unroll the touchpad state.
710 * Release buttons first. If tp is a clickpad, the button event
711 * must come before the touch up. If it isn't, the order doesn't
714 * Then cancel all timeouts on the taps, triggering the last set
717 * Then lift all touches so the touchpad is in a neutral state.
720 tp_release_all_buttons(tp, now);
721 tp_release_all_taps(tp, now);
723 tp_for_each_touch(tp, t) {
724 tp_end_touch(tp, t, now);
727 tp_handle_state(tp, now);
731 tp_suspend(struct tp_dispatch *tp, struct evdev_device *device)
735 /* On devices with top softwarebuttons we don't actually suspend the
736 * device, to keep the "trackpoint" buttons working. tp_post_events()
737 * will only send events for the trackpoint while suspended.
739 if (tp->buttons.has_topbuttons) {
740 evdev_notify_suspended_device(device);
741 /* Enlarge topbutton area while suspended */
742 tp_init_softbuttons(tp, device, 1.5);
744 evdev_device_suspend(device);
749 tp_resume(struct tp_dispatch *tp, struct evdev_device *device)
751 if (tp->buttons.has_topbuttons) {
752 /* tap state-machine is offline while suspended, reset state */
754 /* restore original topbutton area size */
755 tp_init_softbuttons(tp, device, 1.0);
756 evdev_notify_resumed_device(device);
758 evdev_device_resume(device);
763 tp_trackpoint_timeout(uint64_t now, void *data)
765 struct tp_dispatch *tp = data;
767 tp_tap_resume(tp, now);
768 tp->sendevents.trackpoint_active = false;
772 tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
774 struct tp_dispatch *tp = data;
776 /* Buttons do not count as trackpad activity, as people may use
777 the trackpoint buttons in combination with the touchpad. */
778 if (event->type == LIBINPUT_EVENT_POINTER_BUTTON)
781 if (!tp->sendevents.trackpoint_active) {
782 evdev_stop_scroll(tp->device, time);
783 tp_tap_suspend(tp, time);
784 tp->sendevents.trackpoint_active = true;
787 libinput_timer_set(&tp->sendevents.trackpoint_timer,
788 time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);
792 tp_device_added(struct evdev_device *device,
793 struct evdev_device *added_device)
795 struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
797 if (tp->buttons.trackpoint == NULL &&
798 (added_device->tags & EVDEV_TAG_TRACKPOINT)) {
799 /* Don't send any pending releases to the new trackpoint */
800 tp->buttons.active_is_topbutton = false;
801 tp->buttons.trackpoint = added_device;
802 libinput_device_add_event_listener(&added_device->base,
803 &tp->sendevents.trackpoint_listener,
804 tp_trackpoint_event, tp);
807 if (tp->sendevents.current_mode !=
808 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
811 if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE)
812 tp_suspend(tp, device);
816 tp_device_removed(struct evdev_device *device,
817 struct evdev_device *removed_device)
819 struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
820 struct libinput_device *dev;
822 if (removed_device == tp->buttons.trackpoint) {
823 /* Clear any pending releases for the trackpoint */
824 if (tp->buttons.active && tp->buttons.active_is_topbutton) {
825 tp->buttons.active = 0;
826 tp->buttons.active_is_topbutton = false;
828 libinput_device_remove_event_listener(
829 &tp->sendevents.trackpoint_listener);
830 tp->buttons.trackpoint = NULL;
833 if (tp->sendevents.current_mode !=
834 LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
837 list_for_each(dev, &device->base.seat->devices_list, link) {
838 struct evdev_device *d = (struct evdev_device*)dev;
839 if (d != removed_device &&
840 (d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) {
845 tp_resume(tp, device);
849 tp_tag_device(struct evdev_device *device,
850 struct udev_device *udev_device)
854 /* simple approach: touchpads on USB or Bluetooth are considered
855 * external, anything else is internal. Exception is Apple -
856 * internal touchpads are connected over USB and it doesn't have
857 * external USB touchpads anyway.
859 bustype = libevdev_get_id_bustype(device->evdev);
860 if (bustype == BUS_USB) {
861 if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE)
862 device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
863 } else if (bustype != BUS_BLUETOOTH)
864 device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
867 static struct evdev_dispatch_interface tp_interface = {
873 tp_device_removed, /* device_suspended, treat as remove */
874 tp_device_added, /* device_resumed, treat as add */
879 tp_init_touch(struct tp_dispatch *tp,
886 tp_init_slots(struct tp_dispatch *tp,
887 struct evdev_device *device)
889 const struct input_absinfo *absinfo;
894 { BTN_TOOL_QUINTTAP, 5 },
895 { BTN_TOOL_QUADTAP, 4 },
896 { BTN_TOOL_TRIPLETAP, 3 },
897 { BTN_TOOL_DOUBLETAP, 2 },
900 unsigned int i, n_btn_tool_touches = 1;
902 absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
904 tp->real_touches = absinfo->maximum + 1;
905 tp->slot = absinfo->value;
908 tp->real_touches = 1;
913 tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT);
915 ARRAY_FOR_EACH(max_touches, m) {
916 if (libevdev_has_event_code(device->evdev,
919 n_btn_tool_touches = m->ntouches;
924 tp->ntouches = max(tp->real_touches, n_btn_tool_touches);
925 tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
929 for (i = 0; i < tp->ntouches; i++)
930 tp_init_touch(tp, &tp->touches[i]);
936 tp_init_accel(struct tp_dispatch *tp, double diagonal)
941 res_x = libevdev_get_abs_resolution(tp->device->evdev,
943 res_y = libevdev_get_abs_resolution(tp->device->evdev,
946 res_x = libevdev_get_abs_resolution(tp->device->evdev,
948 res_y = libevdev_get_abs_resolution(tp->device->evdev,
953 * Not all touchpads report the same amount of units/mm (resolution).
954 * Normalize motion events to the default mouse DPI as base
955 * (unaccelerated) speed. This also evens out any differences in x
956 * and y resolution, so that a circle on the
957 * touchpad does not turn into an elipse on the screen.
959 if (res_x > 1 && res_y > 1) {
960 tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
961 tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
963 /* FIXME: once normalized, touchpads see the same
964 acceleration as mice. that is technically correct but
965 subjectively wrong, we expect a touchpad to be a lot
967 For now, apply a magic factor here until this is
968 fixed in the actual filter code.
971 const double MAGIC = 0.4;
972 tp->accel.x_scale_coeff *= MAGIC;
973 tp->accel.y_scale_coeff *= MAGIC;
977 * For touchpads where the driver does not provide resolution, fall
978 * back to scaling motion events based on the diagonal size in units.
980 tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
981 tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
984 if (evdev_device_init_pointer_acceleration(tp->device) == -1)
991 tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
993 struct evdev_device *evdev = (struct evdev_device*)device;
994 struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
995 uint32_t methods = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
997 if (tp->ntouches >= 2)
998 methods |= LIBINPUT_CONFIG_SCROLL_2FG;
1000 if (!tp->buttons.is_clickpad)
1001 methods |= LIBINPUT_CONFIG_SCROLL_EDGE;
1006 static enum libinput_config_status
1007 tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
1008 enum libinput_config_scroll_method method)
1010 struct evdev_device *evdev = (struct evdev_device*)device;
1011 struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
1013 if (method == tp->scroll.method)
1014 return LIBINPUT_CONFIG_STATUS_SUCCESS;
1016 tp_stop_scroll_events(tp, libinput_now(device->seat->libinput));
1017 tp->scroll.method = method;
1019 return LIBINPUT_CONFIG_STATUS_SUCCESS;
1022 static enum libinput_config_scroll_method
1023 tp_scroll_config_scroll_method_get_method(struct libinput_device *device)
1025 struct evdev_device *evdev = (struct evdev_device*)device;
1026 struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
1028 return tp->scroll.method;
1031 static enum libinput_config_scroll_method
1032 tp_scroll_get_default_method(struct tp_dispatch *tp)
1034 if (tp->ntouches >= 2)
1035 return LIBINPUT_CONFIG_SCROLL_2FG;
1037 return LIBINPUT_CONFIG_SCROLL_EDGE;
1040 static enum libinput_config_scroll_method
1041 tp_scroll_config_scroll_method_get_default_method(struct libinput_device *device)
1043 struct evdev_device *evdev = (struct evdev_device*)device;
1044 struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
1046 return tp_scroll_get_default_method(tp);
1050 tp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
1052 if (tp_edge_scroll_init(tp, device) != 0)
1055 evdev_init_natural_scroll(device);
1057 tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods;
1058 tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method;
1059 tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method;
1060 tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method;
1061 tp->scroll.method = tp_scroll_get_default_method(tp);
1062 tp->device->base.config.scroll_method = &tp->scroll.config_method;
1064 /* In mm for touchpads with valid resolution, see tp_init_accel() */
1065 tp->device->scroll.threshold = 5.0;
1071 tp_init_palmdetect(struct tp_dispatch *tp,
1072 struct evdev_device *device)
1076 tp->palm.right_edge = INT_MAX;
1077 tp->palm.left_edge = INT_MIN;
1079 width = abs(device->abs.absinfo_x->maximum -
1080 device->abs.absinfo_x->minimum);
1082 /* Apple touchpads are always big enough to warrant palm detection */
1083 if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
1084 /* We don't know how big the touchpad is */
1085 if (device->abs.absinfo_x->resolution == 1)
1088 /* Enable palm detection on touchpads >= 80 mm. Anything smaller
1089 probably won't need it, until we find out it does */
1090 if (width/device->abs.absinfo_x->resolution < 80)
1094 /* palm edges are 5% of the width on each side */
1095 tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
1096 tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
1102 tp_init_sendevents(struct tp_dispatch *tp,
1103 struct evdev_device *device)
1105 libinput_timer_init(&tp->sendevents.trackpoint_timer,
1106 tp->device->base.seat->libinput,
1107 tp_trackpoint_timeout, tp);
1112 tp_init(struct tp_dispatch *tp,
1113 struct evdev_device *device)
1118 tp->base.interface = &tp_interface;
1119 tp->device = device;
1121 if (tp_init_slots(tp, device) != 0)
1124 width = abs(device->abs.absinfo_x->maximum -
1125 device->abs.absinfo_x->minimum);
1126 height = abs(device->abs.absinfo_y->maximum -
1127 device->abs.absinfo_y->minimum);
1128 diagonal = sqrt(width*width + height*height);
1130 tp->hysteresis.margin_x =
1131 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
1132 tp->hysteresis.margin_y =
1133 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
1135 if (tp_init_accel(tp, diagonal) != 0)
1138 if (tp_init_tap(tp) != 0)
1141 if (tp_init_buttons(tp, device) != 0)
1144 if (tp_init_palmdetect(tp, device) != 0)
1147 if (tp_init_sendevents(tp, device) != 0)
1150 if (tp_init_scroll(tp, device) != 0)
1153 device->seat_caps |= EVDEV_DEVICE_POINTER;
1159 tp_sendevents_get_modes(struct libinput_device *device)
1161 struct evdev_device *evdev = (struct evdev_device*)device;
1162 uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
1164 if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD)
1165 modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
1171 tp_suspend_conditional(struct tp_dispatch *tp,
1172 struct evdev_device *device)
1174 struct libinput_device *dev;
1176 list_for_each(dev, &device->base.seat->devices_list, link) {
1177 struct evdev_device *d = (struct evdev_device*)dev;
1178 if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) {
1179 tp_suspend(tp, device);
1185 static enum libinput_config_status
1186 tp_sendevents_set_mode(struct libinput_device *device,
1187 enum libinput_config_send_events_mode mode)
1189 struct evdev_device *evdev = (struct evdev_device*)device;
1190 struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
1192 /* DISABLED overrides any DISABLED_ON_ */
1193 if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) &&
1194 (mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE))
1195 mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
1197 if (mode == tp->sendevents.current_mode)
1198 return LIBINPUT_CONFIG_STATUS_SUCCESS;
1201 case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
1202 tp_resume(tp, evdev);
1204 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
1205 tp_suspend(tp, evdev);
1207 case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
1208 tp_suspend_conditional(tp, evdev);
1211 return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
1214 tp->sendevents.current_mode = mode;
1216 return LIBINPUT_CONFIG_STATUS_SUCCESS;
1219 static enum libinput_config_send_events_mode
1220 tp_sendevents_get_mode(struct libinput_device *device)
1222 struct evdev_device *evdev = (struct evdev_device*)device;
1223 struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch;
1225 return dispatch->sendevents.current_mode;
1228 static enum libinput_config_send_events_mode
1229 tp_sendevents_get_default_mode(struct libinput_device *device)
1231 return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
1235 tp_change_to_left_handed(struct evdev_device *device)
1237 struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
1239 if (device->buttons.want_left_handed == device->buttons.left_handed)
1242 if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */
1245 /* tapping and clickfinger aren't affected by left-handed config,
1246 * so checking physical buttons is enough */
1248 device->buttons.left_handed = device->buttons.want_left_handed;
1251 struct model_lookup_t {
1253 uint16_t product_start;
1254 uint16_t product_end;
1255 enum touchpad_model model;
1258 static struct model_lookup_t model_lookup_table[] = {
1259 { 0x0002, 0x0007, 0x0007, MODEL_SYNAPTICS },
1260 { 0x0002, 0x0008, 0x0008, MODEL_ALPS },
1261 { 0x0002, 0x000e, 0x000e, MODEL_ELANTECH },
1262 { 0x05ac, 0, 0x0222, MODEL_APPLETOUCH },
1263 { 0x05ac, 0x0223, 0x0228, MODEL_UNIBODY_MACBOOK },
1264 { 0x05ac, 0x0229, 0x022b, MODEL_APPLETOUCH },
1265 { 0x05ac, 0x022c, 0xffff, MODEL_UNIBODY_MACBOOK },
1269 static enum touchpad_model
1270 tp_get_model(struct evdev_device *device)
1272 struct model_lookup_t *lookup;
1273 uint16_t vendor = libevdev_get_id_vendor(device->evdev);
1274 uint16_t product = libevdev_get_id_product(device->evdev);
1276 for (lookup = model_lookup_table; lookup->vendor; lookup++) {
1277 if (lookup->vendor == vendor &&
1278 lookup->product_start <= product &&
1279 product <= lookup->product_end)
1280 return lookup->model;
1282 return MODEL_UNKNOWN;
1285 struct evdev_dispatch *
1286 evdev_mt_touchpad_create(struct evdev_device *device)
1288 struct tp_dispatch *tp;
1290 tp = zalloc(sizeof *tp);
1294 tp->model = tp_get_model(device);
1296 if (tp_init(tp, device) != 0) {
1297 tp_destroy(&tp->base);
1301 device->base.config.sendevents = &tp->sendevents.config;
1303 tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
1304 tp->sendevents.config.get_modes = tp_sendevents_get_modes;
1305 tp->sendevents.config.set_mode = tp_sendevents_set_mode;
1306 tp->sendevents.config.get_mode = tp_sendevents_get_mode;
1307 tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode;
1309 evdev_init_left_handed(device, tp_change_to_left_handed);