diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index 64007720..50c4afd1 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -871,6 +871,21 @@ fallback_any_button_down(struct fallback_dispatch *dispatch, return false; } +static inline bool +fallback_arbitrate_touch(struct fallback_dispatch *dispatch, + struct mt_slot *slot) +{ + bool discard = false; + + if (dispatch->arbitration.state == ARBITRATION_IGNORE_RECT && + point_in_rect(&slot->point, &dispatch->arbitration.rect)) { + slot->palm_state = PALM_IS_PALM; + discard = true; + } + + return discard; +} + static inline bool fallback_flush_mt_events(struct fallback_dispatch *dispatch, struct evdev_device *device, @@ -900,10 +915,13 @@ fallback_flush_mt_events(struct fallback_dispatch *dispatch, } else if (slot->palm_state == PALM_NONE) { switch (slot->state) { case SLOT_STATE_BEGIN: - sent = fallback_flush_mt_down(dispatch, - device, - i, - time); + if (!fallback_arbitrate_touch(dispatch, + slot)) { + sent = fallback_flush_mt_down(dispatch, + device, + i, + time); + } break; case SLOT_STATE_UPDATE: sent = fallback_flush_mt_motion(dispatch, @@ -1044,12 +1062,16 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch, static void cancel_touches(struct fallback_dispatch *dispatch, struct evdev_device *device, + const struct device_coord_rect *rect, uint64_t time) { unsigned int idx; bool need_frame = false; - need_frame = fallback_flush_st_cancel(dispatch, device, time); + if (!rect || point_in_rect(&dispatch->abs.point, rect)) + need_frame = fallback_flush_st_cancel(dispatch, + device, + time); for (idx = 0; idx < dispatch->mt.slots_len; idx++) { struct mt_slot *slot = &dispatch->mt.slots[idx]; @@ -1057,7 +1079,8 @@ cancel_touches(struct fallback_dispatch *dispatch, if (slot->seat_slot == -1) continue; - if (fallback_flush_mt_cancel(dispatch, device, idx, time)) + if ((!rect || point_in_rect(&slot->point, rect)) && + fallback_flush_mt_cancel(dispatch, device, idx, time)) need_frame = true; } @@ -1125,7 +1148,7 @@ fallback_return_to_neutral_state(struct fallback_dispatch *dispatch, if ((time = libinput_now(libinput)) == 0) return; - cancel_touches(dispatch, device, time); + cancel_touches(dispatch, device, NULL, time); release_pressed_keys(dispatch, device, time); memset(dispatch->hw_key_mask, 0, sizeof(dispatch->hw_key_mask)); memset(dispatch->hw_key_mask, 0, sizeof(dispatch->last_hw_key_mask)); @@ -1199,9 +1222,11 @@ static void fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch, struct evdev_device *device, enum evdev_arbitration_state which, + const struct phys_rect *phys_rect, uint64_t time) { struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch); + struct device_coord_rect rect = {0}; if (which == dispatch->arbitration.state) return; @@ -1218,6 +1243,12 @@ fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch, libinput_timer_set(&dispatch->arbitration.arbitration_timer, time + ms2us(90)); break; + case ARBITRATION_IGNORE_RECT: + assert(phys_rect); + rect = evdev_phys_rect_to_units(device, phys_rect); + cancel_touches(dispatch, device, &rect, time); + dispatch->arbitration.rect = rect; + break; case ARBITRATION_IGNORE_ALL: libinput_timer_cancel(&dispatch->arbitration.arbitration_timer); fallback_return_to_neutral_state(dispatch, device); @@ -1421,7 +1452,8 @@ struct evdev_dispatch_interface fallback_interface = { .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, + .touch_arbitration_toggle = fallback_interface_toggle_touch, + .touch_arbitration_update_rect = NULL, .get_switch_state = fallback_interface_get_switch_state, }; diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index 58694dcc..f57c4af2 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -147,6 +147,7 @@ struct fallback_dispatch { struct { enum evdev_arbitration_state state; bool in_arbitration; + struct device_coord_rect rect; struct libinput_timer arbitration_timer; } arbitration; }; diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 7385ab57..4798111a 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -2693,6 +2693,7 @@ static void tp_interface_toggle_touch(struct evdev_dispatch *dispatch, struct evdev_device *device, enum evdev_arbitration_state which, + const struct phys_rect *rect, uint64_t time) { struct tp_dispatch *tp = tp_dispatch(dispatch); @@ -2702,6 +2703,7 @@ tp_interface_toggle_touch(struct evdev_dispatch *dispatch, switch (which) { case ARBITRATION_IGNORE_ALL: + case ARBITRATION_IGNORE_RECT: libinput_timer_cancel(&tp->arbitration.arbitration_timer); tp_clear_state(tp); tp->arbitration.state = which; @@ -2730,7 +2732,8 @@ static struct evdev_dispatch_interface tp_interface = { .device_suspended = tp_interface_device_removed, /* treat as remove */ .device_resumed = tp_interface_device_added, /* treat as add */ .post_added = NULL, - .toggle_touch = tp_interface_toggle_touch, + .touch_arbitration_toggle = tp_interface_toggle_touch, + .touch_arbitration_update_rect = NULL, .get_switch_state = NULL, }; diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c index b3b374dc..f1cbc4a4 100644 --- a/src/evdev-tablet-pad.c +++ b/src/evdev-tablet-pad.c @@ -518,7 +518,8 @@ static struct evdev_dispatch_interface pad_interface = { .device_suspended = NULL, .device_resumed = NULL, .post_added = NULL, - .toggle_touch = NULL, + .touch_arbitration_toggle = NULL, + .touch_arbitration_update_rect = NULL, .get_switch_state = NULL, }; diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c index 6d177e18..e94fad53 100644 --- a/src/evdev-tablet.c +++ b/src/evdev-tablet.c @@ -1634,11 +1634,12 @@ tablet_set_touch_device_enabled(struct evdev_device *touch_device, which = ARBITRATION_IGNORE_ALL; dispatch = touch_device->dispatch; - if (dispatch->interface->toggle_touch) - dispatch->interface->toggle_touch(dispatch, - touch_device, - which, - time); + if (dispatch->interface->touch_arbitration_toggle) + dispatch->interface->touch_arbitration_toggle(dispatch, + touch_device, + which, + NULL, + time); } static inline void @@ -1941,7 +1942,8 @@ static struct evdev_dispatch_interface tablet_interface = { .device_suspended = NULL, .device_resumed = NULL, .post_added = tablet_check_initial_proximity, - .toggle_touch = NULL, + .touch_arbitration_toggle = NULL, + .touch_arbitration_update_rect = NULL, .get_switch_state = NULL, }; diff --git a/src/evdev.h b/src/evdev.h index e983fb1d..34ee64de 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -147,6 +147,7 @@ enum evdev_debounce_state { enum evdev_arbitration_state { ARBITRATION_NOT_ACTIVE, ARBITRATION_IGNORE_ALL, + ARBITRATION_IGNORE_RECT, }; struct evdev_device { @@ -303,11 +304,22 @@ struct evdev_dispatch_interface { struct evdev_dispatch *dispatch); /* For touch arbitration, called on the device that should - * enable/disable touch capabilities */ - void (*toggle_touch)(struct evdev_dispatch *dispatch, - struct evdev_device *device, - enum evdev_arbitration_state which, - uint64_t now); + * enable/disable touch capabilities. + */ + void (*touch_arbitration_toggle)(struct evdev_dispatch *dispatch, + struct evdev_device *device, + enum evdev_arbitration_state which, + const struct phys_rect *rect, /* may be NULL */ + uint64_t now); + + /* Called when touch arbitration is on, updates the area where touch + * arbitration should apply. + */ + void (*touch_arbitration_update_rect)(struct evdev_dispatch *dispatch, + struct evdev_device *device, + const struct phys_rect *rect, + uint64_t now); + /* Return the state of the given switch */ enum libinput_switch_state @@ -880,6 +892,33 @@ evdev_device_mm_to_units(const struct evdev_device *device, return units; } +static inline struct device_coord_rect +evdev_phys_rect_to_units(const struct evdev_device *device, + const struct phys_rect *mm) +{ + struct device_coord_rect units = {0}; + const struct input_absinfo *absx, *absy; + + if (device->abs.absinfo_x == NULL || + device->abs.absinfo_y == NULL) { + log_bug_libinput(evdev_libinput_context(device), + "%s: is not an abs device\n", + device->devname); + return units; + } + + absx = device->abs.absinfo_x; + absy = device->abs.absinfo_y; + + units.x = mm->x * absx->resolution + absx->minimum; + units.y = mm->y * absy->resolution + absy->minimum; + units.w = mm->w * absx->resolution; + units.h = mm->h * absy->resolution; + + return units; +} + + static inline void evdev_device_init_abs_range_warnings(struct evdev_device *device) { diff --git a/src/libinput-private.h b/src/libinput-private.h index ad92cde8..c0234a5b 100644 --- a/src/libinput-private.h +++ b/src/libinput-private.h @@ -95,6 +95,18 @@ struct phys_coords { double y; }; +/* A rectangle in mm, x/y is the top-left corner */ +struct phys_rect { + double x, y; + double w, h; +}; + +/* A rectangle in device coordinates, x/y is the top-left corner */ +struct device_coord_rect { + int x, y; + int w, h; +}; + /* A pair of tilt flags */ struct wheel_tilt_flags { bool vertical, horizontal; @@ -810,4 +822,19 @@ device_float_get_direction(const struct device_float_coords coords) { return xy_get_direction(coords.x, coords.y); } + +/** + * Returns true if the point is within the given rectangle, including the + * left edge but excluding the right edge. + */ +static inline bool +point_in_rect(const struct device_coords *point, + const struct device_coord_rect *rect) +{ + return (point->x >= rect->x && + point->x < rect->x + rect->w && + point->y >= rect->y && + point->y < rect->y + rect->h); +} + #endif /* LIBINPUT_PRIVATE_H */