[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