[PATCH 1/6] evdev: add ABS_MT_* support for direct touch devices
Kristian Høgsberg
krh at bitplanet.net
Tue Dec 20 11:16:16 PST 2011
On Fri, Dec 16, 2011 at 10:59 AM, Tiago Vignatti
<vignatti at freedesktop.org> wrote:
> From: Tiago Vignatti <tiago.vignatti at intel.com>
>
> This adds support to ABS_MT_* support for direct touch devices and notifies
> the compositor. The compositor has a stub for now.
>
> Signed-off-by: Tiago Vignatti <tiago.vignatti at intel.com>
> ---
> compositor/compositor.c | 12 ++++
> compositor/compositor.h | 4 +
> compositor/evdev.c | 158 ++++++++++++++++++++++++++++++++++++++++-------
> 3 files changed, 151 insertions(+), 23 deletions(-)
>
> diff --git a/compositor/compositor.c b/compositor/compositor.c
> index cb9b41f..2c461d8 100644
> --- a/compositor/compositor.c
> +++ b/compositor/compositor.c
> @@ -1493,6 +1493,18 @@ notify_keyboard_focus(struct wl_input_device *device,
> }
> }
>
> +/**
> + * notify_touch - emulates button touches and notifies surfaces accordingly.
> + *
> + * It assumes always the correct cycle sequence until it gets here: touch_down
> + * → touch_update → ... → touch_update → touch_end. The driver is responsible
> + * for sending along such order.
> + */
> +WL_EXPORT void
> +notify_touch(struct wl_input_device *device, uint32_t time, int touch_id,
> + int x, int y, int touch_type)
> +{
> +}
>
> static void
> input_device_attach(struct wl_client *client,
> diff --git a/compositor/compositor.h b/compositor/compositor.h
> index 0b3f82e..676ded0 100644
> --- a/compositor/compositor.h
> +++ b/compositor/compositor.h
> @@ -296,6 +296,10 @@ notify_keyboard_focus(struct wl_input_device *device,
> struct wl_array *keys);
>
> void
> +notify_touch(struct wl_input_device *device, uint32_t time, int touch_id,
> + int x, int y, int touch_type);
> +
> +void
> wlsc_output_finish_frame(struct wlsc_output *output, int msecs);
> void
> wlsc_output_damage(struct wlsc_output *output);
> diff --git a/compositor/evdev.c b/compositor/evdev.c
> index 83812bf..262f452 100644
> --- a/compositor/evdev.c
> +++ b/compositor/evdev.c
> @@ -47,16 +47,19 @@ struct evdev_input_device {
> int min_x, max_x, min_y, max_y;
> int old_x, old_y, reset_x, reset_y;
> } abs;
> - int is_touchpad;
> + int slot_mt, slot_open, slot_close; /* TODO: inside abs */
> + int is_touchpad, is_mt;
> };
>
> /* event type flags */
> -#define EVDEV_ABSOLUTE_MOTION (1 << 0)
> -#define EVDEV_RELATIVE_MOTION (1 << 1)
> +#define EVDEV_ABSOLUTE_MOTION (1 << 0)
> +#define EVDEV_ABSOLUTE_MT_MOTION (1 << 1)
> +#define EVDEV_RELATIVE_MOTION (1 << 2)
>
> struct evdev_motion_accumulator {
> int x, y;
> int dx, dy;
> + int mt_x, mt_y;
> int type; /* event type flags */
> };
>
> @@ -82,8 +85,14 @@ evdev_process_key(struct evdev_input_device *device,
> device->abs.reset_y = 1;
> }
> break;
> -
> case BTN_TOUCH:
> + /* Multitouch touchscreen devices might not send individually
> + * button events each time a new finger is down. So we don't
> + * send notification for such devices and we solve the button
> + * case emulating on compositor side. */
> + if (device->is_mt)
> + break;
> +
> /* Treat BTN_TOUCH from devices that only have BTN_TOUCH as
> * BTN_LEFT */
> e->code = BTN_LEFT;
> @@ -107,6 +116,73 @@ evdev_process_key(struct evdev_input_device *device,
> }
> }
>
> +/*
> + * evdev_notify_touch - notify the compositor about non-motion touch events
> + */
> +static void
> +evdev_notify_touch(struct evdev_input_device *device, int time,
> + struct evdev_motion_accumulator *accum)
> +{
> + if (device->slot_mt < 0)
> + return;
> +
> + if (device->slot_close) {
> + notify_touch(&device->master->base.input_device, time,
> + device->slot_mt, 0, 0, WL_INPUT_DEVICE_TOUCH_UP);
> + device->slot_close = 0;
> + } else if (device->slot_open) {
> + notify_touch(&device->master->base.input_device, time,
> + device->slot_mt, 0, 0,
> + WL_INPUT_DEVICE_TOUCH_DOWN);
> + device->slot_open = 0;
> + }
> +}
> +
> +static void
> +evdev_process_touch(struct evdev_input_device *device,
> + struct input_event *e, int time,
> + struct evdev_motion_accumulator *accum)
> +{
> + switch (e->code) {
> + case ABS_MT_SLOT:
> + device->slot_mt = e->value;
> + break;
> + case ABS_MT_TRACKING_ID:
> + if (e->value >= 0)
> + device->slot_open = 1;
> + else
> + device->slot_close = 1;
> +
> + evdev_notify_touch(device, time, accum);
> + }
> + return;
> +}
> +
> +static void
> +evdev_process_touch_motion(struct evdev_input_device *device,
> + struct input_event *e, int time,
> + struct evdev_motion_accumulator *accum)
> +{
> + const int screen_width = device->output->current->width;
> + const int screen_height = device->output->current->height;
> +
> + switch (e->code) {
> + case ABS_MT_POSITION_X:
> + accum->mt_x = (e->value - device->abs.min_x) * screen_width /
> + (device->abs.max_x - device->abs.min_x) +
> + device->output->x;
> + accum->type |= EVDEV_ABSOLUTE_MT_MOTION;
> + break;
> + case ABS_MT_POSITION_Y:
> + accum->mt_y = (e->value - device->abs.min_y) * screen_height /
> + (device->abs.max_y - device->abs.min_y) +
> + device->output->y;
> + accum->type |= EVDEV_ABSOLUTE_MT_MOTION;
> + break;
> + }
> + return;
> +}
Can we just fold these three functions into one evdev_process_touch()
with a switch over e->code? Also to get coordinates on the touch_down
event, we need to treat the first ABS_MT_POSITION_X/Y as touch down
(which it is), so I suggest adding a new accumulator flag:
EVDEV_ABSOLUTE_MT_DOWN and set that when we receive positive tracking
ID. Then in flush we check for that flag and send the down event if
it's set otherwise if the MT_MOTION is set we send motion, and in
either case we clear both. We also need an accumulator flag for touch
up, that is, when we get a negative tracking ID for a slot. We won't
need the slot_open and slot_close flags then.
> static inline void
> evdev_process_absolute_motion(struct evdev_input_device *device,
> struct input_event *e, struct evdev_motion_accumulator *accum)
> @@ -119,13 +195,13 @@ evdev_process_absolute_motion(struct evdev_input_device *device,
> accum->x = (e->value - device->abs.min_x) * screen_width /
> (device->abs.max_x - device->abs.min_x) +
> device->output->x;
> - accum->type = EVDEV_ABSOLUTE_MOTION;
> + accum->type |= EVDEV_ABSOLUTE_MOTION;
> break;
> case ABS_Y:
> accum->y = (e->value - device->abs.min_y) * screen_height /
> (device->abs.max_y - device->abs.min_y) +
> device->output->y;
> - accum->type = EVDEV_ABSOLUTE_MOTION;
> + accum->type |= EVDEV_ABSOLUTE_MOTION;
> break;
> }
> }
> @@ -148,7 +224,7 @@ evdev_process_absolute_motion_touchpad(struct evdev_input_device *device,
> (device->abs.max_x - device->abs.min_x);
> }
> device->abs.old_x = e->value;
> - accum->type = EVDEV_RELATIVE_MOTION;
> + accum->type |= EVDEV_RELATIVE_MOTION;
> break;
> case ABS_Y:
> e->value -= device->abs.min_y;
> @@ -161,7 +237,7 @@ evdev_process_absolute_motion_touchpad(struct evdev_input_device *device,
> (device->abs.max_y - device->abs.min_y);
> }
> device->abs.old_y = e->value;
> - accum->type = EVDEV_RELATIVE_MOTION;
> + accum->type |= EVDEV_RELATIVE_MOTION;
> break;
> }
> }
> @@ -173,15 +249,32 @@ evdev_process_relative_motion(struct input_event *e,
> switch (e->code) {
> case REL_X:
> accum->dx += e->value;
> - accum->type = EVDEV_RELATIVE_MOTION;
> + accum->type |= EVDEV_RELATIVE_MOTION;
> break;
> case REL_Y:
> accum->dy += e->value;
> - accum->type = EVDEV_RELATIVE_MOTION;
> + accum->type |= EVDEV_RELATIVE_MOTION;
> break;
> }
> }
>
> +static inline void
> +evdev_process_absolute(struct evdev_input_device *device,
> + struct input_event *e, int time,
> + struct evdev_motion_accumulator *accum)
> +{
> + if (device->is_touchpad)
> + evdev_process_absolute_motion_touchpad(device, e, accum);
> + else {
use else if (device->is_mt) { instead to avoid indentation
> + if (device->is_mt) {
> + evdev_process_touch(device, e, time, accum);
> + evdev_process_touch_motion(device, e, time, accum);
This becomes just evdev_process_touch_mt().
> + }
> + else
else on the same line as { (and I normally use {}'s in both branches
of an if-statement if one of them requires it...)
> + evdev_process_absolute_motion(device, e, accum);
> + }
> +}
> +
> static int
> is_motion_event(struct input_event *e)
> {
> @@ -196,6 +289,8 @@ is_motion_event(struct input_event *e)
> switch (e->code) {
> case ABS_X:
> case ABS_Y:
> + case ABS_MT_POSITION_X:
> + case ABS_MT_POSITION_Y:
> return 1;
> }
> }
> @@ -213,24 +308,37 @@ evdev_reset_accum(struct wl_input_device *device,
> * through the bytestream whereas the other could be omitted. For
> * this, we have to save the old value that will be forwarded without
> * modifications to the compositor. */
> - accum->x = device->x;
> - accum->y = device->y;
> + accum->mt_x = accum->x = device->x;
> + accum->mt_y = accum->y = device->y;
> }
>
> static void
> evdev_flush_motion(struct wl_input_device *device, uint32_t time,
> - struct evdev_motion_accumulator *accum)
> + struct evdev_motion_accumulator *accum, int slot_mt)
> {
> + int save_types;
> +
> if (!accum->type)
> return;
>
> - if (accum->type == EVDEV_RELATIVE_MOTION)
> + if (accum->type & EVDEV_RELATIVE_MOTION) {
> notify_motion(device, time,
> device->x + accum->dx, device->y + accum->dy);
> - if (accum->type == EVDEV_ABSOLUTE_MOTION)
> + accum->type &= ~EVDEV_RELATIVE_MOTION;
> + }
> + if (accum->type & EVDEV_ABSOLUTE_MT_MOTION) {
> + notify_touch(device, time, slot_mt, accum->mt_x, accum->mt_y,
> + WL_INPUT_DEVICE_TOUCH_MOTION);
> + accum->type &= ~EVDEV_ABSOLUTE_MT_MOTION;
> + }
> + if (accum->type & EVDEV_ABSOLUTE_MOTION) {
> notify_motion(device, time, accum->x, accum->y);
> + accum->type &= ~EVDEV_ABSOLUTE_MOTION;
> + }
>
> + save_types = accum->type;
> evdev_reset_accum(device, accum);
> + accum->type = save_types;
I think we can just set dx and dy to 0 in the EVDEV_RELATIVE_MOTION
case and skip save_types and the call to evdev_reset_accum() instead.
> }
>
> static int
> @@ -265,18 +373,13 @@ evdev_input_device_data(int fd, uint32_t mask, void *data)
> * events and send as a bunch */
> if (!is_motion_event(e))
> evdev_flush_motion(&device->master->base.input_device,
> - time, &accumulator);
> + time, &accumulator, device->slot_mt);
> switch (e->type) {
> case EV_REL:
> evdev_process_relative_motion(e, &accumulator);
> break;
> case EV_ABS:
> - if (device->is_touchpad)
> - evdev_process_absolute_motion_touchpad(device,
> - e, &accumulator);
> - else
> - evdev_process_absolute_motion(device, e,
> - &accumulator);
> + evdev_process_absolute(device, e, time, &accumulator);
> break;
> case EV_KEY:
> evdev_process_key(device, e, time);
> @@ -284,7 +387,8 @@ evdev_input_device_data(int fd, uint32_t mask, void *data)
> }
> }
>
> - evdev_flush_motion(&device->master->base.input_device, time, &accumulator);
> + evdev_flush_motion(&device->master->base.input_device, time,
> + &accumulator, device->slot_mt);
>
> return 1;
> }
> @@ -328,6 +432,10 @@ evdev_configure_device(struct evdev_input_device *device)
> device->abs.min_y = absinfo.minimum;
> device->abs.max_y = absinfo.maximum;
> }
> + if (TEST_BIT(abs_bits, ABS_MT_SLOT)) {
> + device->is_mt = 1;
> + device->slot_mt = 0;
> + }
> }
> if (TEST_BIT(ev_bits, EV_KEY)) {
> has_key = 1;
> @@ -365,7 +473,11 @@ evdev_input_device_create(struct evdev_input *master,
>
> device->master = master;
> device->is_touchpad = 0;
> + device->is_mt = 0;
> device->devnode = strdup(path);
> + device->slot_mt = -1;
> + device->slot_open = 0;
> + device->slot_close = 0;
>
> device->fd = open(path, O_RDONLY);
> if (device->fd < 0)
> --
> 1.7.5.4
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
More information about the wayland-devel
mailing list