summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2016-06-28 11:28:34 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2016-09-07 11:17:03 +1000
commitb519ea4ab52e76af585e800fd766bb188114a0b3 (patch)
tree63dadb0906c1b682a03fbca5ce4d424ad961f3d0 /src
parent1ce99fd69858bca72e78689ef28a57a66672d802 (diff)
tablet: add touch arbitration
So far we've relied on the wacom kernel module to do touch arbitration for us but that won't be the case in upcoming kernels. Implement touch arbitration in userspace by pairing the two devices and suspending the touch device whenever a tool comes into proximity. In the future more sophisticated arbitration can be done (e.g. only touches which are close to the pen) but let's burn that bridge when we have to cross it. Note that touch arbitration is "device suspend light", i.e. we leave the device enabled and the fd is active. Tablet interactions are comparatively short-lived, so closing the fd and asking logind for a new one every time the pen changes proximity is suboptimal. Instead, we just keep a boolean around and discard all events while it is set. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Jason Gerecke <jason.gerecke@wacom.com>
Diffstat (limited to 'src')
-rw-r--r--src/evdev-mt-touchpad.c21
-rw-r--r--src/evdev-mt-touchpad.h4
-rw-r--r--src/evdev-tablet-pad.c1
-rw-r--r--src/evdev-tablet.c80
-rw-r--r--src/evdev-tablet.h3
-rw-r--r--src/evdev.c21
-rw-r--r--src/evdev.h8
7 files changed, 135 insertions, 3 deletions
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 65b0abf..2354061 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -1165,6 +1165,9 @@ tp_interface_process(struct evdev_dispatch *dispatch,
struct tp_dispatch *tp =
(struct tp_dispatch *)dispatch;
+ if (tp->ignore_events)
+ return;
+
switch (e->type) {
case EV_ABS:
if (tp->has_mt)
@@ -1679,6 +1682,23 @@ evdev_tag_touchpad(struct evdev_device *device,
}
}
+static void
+tp_interface_toggle_touch(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ bool enable)
+{
+ struct tp_dispatch *tp = (struct tp_dispatch*)dispatch;
+ bool ignore_events = !enable;
+
+ if (ignore_events == tp->ignore_events)
+ return;
+
+ if (ignore_events)
+ tp_clear_state(tp);
+
+ tp->ignore_events = ignore_events;
+}
+
static struct evdev_dispatch_interface tp_interface = {
tp_interface_process,
tp_interface_suspend,
@@ -1689,6 +1709,7 @@ static struct evdev_dispatch_interface tp_interface = {
tp_interface_device_removed, /* device_suspended, treat as remove */
tp_interface_device_added, /* device_resumed, treat as add */
NULL, /* post_added */
+ tp_interface_toggle_touch,
};
static void
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 8a8d2db..de9bdb5 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -231,6 +231,10 @@ struct tp_dispatch {
bool semi_mt;
bool reports_distance; /* does the device support true hovering */
+ /* 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 */
diff --git a/src/evdev-tablet-pad.c b/src/evdev-tablet-pad.c
index 0e95408..82542bc 100644
--- a/src/evdev-tablet-pad.c
+++ b/src/evdev-tablet-pad.c
@@ -512,6 +512,7 @@ static struct evdev_dispatch_interface pad_interface = {
NULL, /* device_suspended */
NULL, /* device_resumed */
NULL, /* post_added */
+ NULL, /* toggle_touch */
};
static void
diff --git a/src/evdev-tablet.c b/src/evdev-tablet.c
index 254d669..14023b6 100644
--- a/src/evdev-tablet.c
+++ b/src/evdev-tablet.c
@@ -1434,6 +1434,39 @@ tablet_flush(struct tablet_dispatch *tablet,
}
static inline void
+tablet_set_touch_device_enabled(struct evdev_device *touch_device,
+ bool enable)
+{
+ struct evdev_dispatch *dispatch;
+
+ if (touch_device == NULL)
+ return;
+
+ dispatch = touch_device->dispatch;
+ if (dispatch->interface->toggle_touch)
+ dispatch->interface->toggle_touch(dispatch,
+ touch_device,
+ enable);
+}
+
+static inline void
+tablet_toggle_touch_device(struct tablet_dispatch *tablet,
+ struct evdev_device *tablet_device)
+{
+ bool enable_events;
+
+ enable_events = tablet_has_status(tablet,
+ TABLET_TOOL_OUT_OF_RANGE) ||
+ tablet_has_status(tablet, TABLET_NONE) ||
+ tablet_has_status(tablet,
+ TABLET_TOOL_LEAVING_PROXIMITY) ||
+ tablet_has_status(tablet,
+ TABLET_TOOL_OUT_OF_PROXIMITY);
+
+ tablet_set_touch_device_enabled(tablet->touch_device, enable_events);
+}
+
+static inline void
tablet_reset_state(struct tablet_dispatch *tablet)
{
/* Update state */
@@ -1466,6 +1499,7 @@ tablet_process(struct evdev_dispatch *dispatch,
break;
case EV_SYN:
tablet_flush(tablet, device, time);
+ tablet_toggle_touch_device(tablet, device);
tablet_reset_state(tablet);
break;
default:
@@ -1478,6 +1512,16 @@ tablet_process(struct evdev_dispatch *dispatch,
}
static void
+tablet_suspend(struct evdev_dispatch *dispatch,
+ struct evdev_device *device)
+{
+ struct tablet_dispatch *tablet =
+ (struct tablet_dispatch *)dispatch;
+
+ tablet_set_touch_device_enabled(tablet->touch_device, true);
+}
+
+static void
tablet_destroy(struct evdev_dispatch *dispatch)
{
struct tablet_dispatch *tablet =
@@ -1492,6 +1536,35 @@ tablet_destroy(struct evdev_dispatch *dispatch)
}
static void
+tablet_device_added(struct evdev_device *device,
+ struct evdev_device *added_device)
+{
+ struct tablet_dispatch *tablet =
+ (struct tablet_dispatch*)device->dispatch;
+
+ if (libinput_device_get_device_group(&device->base) !=
+ libinput_device_get_device_group(&added_device->base))
+ return;
+
+ /* Touch screens or external touchpads only */
+ if (evdev_device_has_capability(added_device, LIBINPUT_DEVICE_CAP_TOUCH) ||
+ (evdev_device_has_capability(added_device, LIBINPUT_DEVICE_CAP_POINTER) &&
+ (added_device->tags & EVDEV_TAG_EXTERNAL_TOUCHPAD)))
+ tablet->touch_device = added_device;
+}
+
+static void
+tablet_device_removed(struct evdev_device *device,
+ struct evdev_device *removed_device)
+{
+ struct tablet_dispatch *tablet =
+ (struct tablet_dispatch*)device->dispatch;
+
+ if (tablet->touch_device == removed_device)
+ tablet->touch_device = NULL;
+}
+
+static void
tablet_check_initial_proximity(struct evdev_device *device,
struct evdev_dispatch *dispatch)
{
@@ -1532,14 +1605,15 @@ tablet_check_initial_proximity(struct evdev_device *device,
static struct evdev_dispatch_interface tablet_interface = {
tablet_process,
- NULL, /* suspend */
+ tablet_suspend,
NULL, /* remove */
tablet_destroy,
- NULL, /* device_added */
- NULL, /* device_removed */
+ tablet_device_added,
+ tablet_device_removed,
NULL, /* device_suspended */
NULL, /* device_resumed */
tablet_check_initial_proximity,
+ NULL, /* toggle_touch */
};
static void
diff --git a/src/evdev-tablet.h b/src/evdev-tablet.h
index f66de5d..2279e03 100644
--- a/src/evdev-tablet.h
+++ b/src/evdev-tablet.h
@@ -71,6 +71,9 @@ struct tablet_dispatch {
uint32_t cursor_proximity_threshold;
struct libinput_device_config_calibration calibration;
+
+ /* The paired touch device on devices with both pen & touch */
+ struct evdev_device *touch_device;
};
static inline enum libinput_tablet_tool_axis
diff --git a/src/evdev.c b/src/evdev.c
index 3b48c3e..03384c8 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1055,6 +1055,9 @@ fallback_process(struct evdev_dispatch *evdev_dispatch,
struct fallback_dispatch *dispatch = (struct 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);
@@ -1184,6 +1187,23 @@ fallback_suspend(struct evdev_dispatch *evdev_dispatch,
}
static void
+fallback_toggle_touch(struct evdev_dispatch *evdev_dispatch,
+ struct evdev_device *device,
+ bool enable)
+{
+ struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch;
+ bool ignore_events = !enable;
+
+ if (ignore_events == dispatch->ignore_events)
+ return;
+
+ if (ignore_events)
+ fallback_return_to_neutral_state(dispatch, device);
+
+ dispatch->ignore_events = ignore_events;
+}
+
+static void
fallback_destroy(struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = (struct fallback_dispatch*)evdev_dispatch;
@@ -1243,6 +1263,7 @@ struct evdev_dispatch_interface fallback_interface = {
NULL, /* device_suspended */
NULL, /* device_resumed */
NULL, /* post_added */
+ fallback_toggle_touch, /* toggle_touch */
};
static uint32_t
diff --git a/src/evdev.h b/src/evdev.h
index 10b0e58..b240615 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -265,6 +265,10 @@ struct evdev_dispatch_interface {
* was sent */
void (*post_added)(struct evdev_device *device,
struct evdev_dispatch *dispatch);
+
+ void (*toggle_touch)(struct evdev_dispatch *dispatch,
+ struct evdev_device *device,
+ bool enable);
};
struct evdev_dispatch {
@@ -308,6 +312,10 @@ struct fallback_dispatch {
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 evdev_device *