[PATCH evdev 2/3] Add the new valuators for multitouch (rev2)

Peter Hutterer peter.hutterer at who-t.net
Mon May 31 18:35:30 PDT 2010


On Sun, May 30, 2010 at 03:08:58PM +0200, Benjamin Tissoires wrote:
> The step one in implementing multitouch in evdev is to report all the
> touches in different valuators.
> This patch detects multitouch devices and creates the extra valuators
> required for multitouch.
> 
> Bonus point: this patch also sort the multitouch valuators to have
> ABS_MT_POSISTION_X and ABS_MT_POSISTION_Y at their first position.
            ^^ typos, but at least they're consistent ;)

> 
> Note that I currently assume that all definitions above ABS_MT_TOUCH_MAJOR
> and below ABS_MAX are MT-related.
> 
> Signed-off-by: Benjamin Tissoires <tissoire at cena.fr>
> ---
>  src/evdev.c |  177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  src/evdev.h |    6 ++-
>  2 files changed, 181 insertions(+), 2 deletions(-)
> 
> diff --git a/src/evdev.c b/src/evdev.c
> index 0cf1d3e..10fe81b 100644
> --- a/src/evdev.c
> +++ b/src/evdev.c
> @@ -339,6 +339,8 @@ EvdevQueueButtonClicks(InputInfoPtr pInfo, int button, int count)
>  #define ABS_X_VALUE 0x1
>  #define ABS_Y_VALUE 0x2
>  #define ABS_VALUE   0x4
> +#define ABS_MT_X_VALUE   0x8
> +#define ABS_MT_Y_VALUE   0x16
>  /**
>   * Take the valuators and process them accordingly.
>   */
> @@ -543,6 +545,10 @@ EvdevProcessAbsoluteMotionEvent(InputInfoPtr pInfo, struct input_event *ev)
>          pEvdev->abs |= ABS_X_VALUE;
>      else if (ev->code == ABS_Y)
>          pEvdev->abs |= ABS_Y_VALUE;
> +    else if (ev->code == ABS_MT_POSITION_X)
> +        pEvdev->abs |= ABS_MT_X_VALUE;
> +    else if (ev->code == ABS_MT_POSITION_Y)
> +        pEvdev->abs |= ABS_MT_Y_VALUE;
>      else
>          pEvdev->abs |= ABS_VALUE;
>  }
> @@ -704,6 +710,8 @@ EvdevProcessEvent(InputInfoPtr pInfo, struct input_event *ev)
>  #undef ABS_X_VALUE
>  #undef ABS_Y_VALUE
>  #undef ABS_VALUE
> +#undef ABS_MT_X_VALUE
> +#undef ABS_MT_Y_VALUE
>  
>  /* just a magic number to reduce the number of reads */
>  #define NUM_EVENTS 16
> @@ -1132,6 +1140,154 @@ EvdevAddKeyClass(DeviceIntPtr device)
>      return Success;
>  }
>  
> +/**
> + * Count the number of mt-related valuators.
> + * assume that mt values are in the range
> + * ABS_MT_TOUCH_MAJOR..ABS_MAX
> + */
> +static int
> +EvdevMTCountValuators(EvdevPtr pEvdev)
> +{
> +    int axis;
> +
> +    pEvdev->mt_num_valuators = 0;
> +
> +    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
> +        return 0;
> +
> +    for (axis = ABS_MT_TOUCH_MAJOR; axis <= ABS_MAX; axis++) {

As Henrik pointed out this is a problematic assumption. Any reason we can't
just take the currently highest known MT define and use it? we'll simply
update when a new kernel comes out with new defines.

> +        if (!TestBit(axis, pEvdev->abs_bitmask))
> +            continue;
> +        pEvdev->mt_num_valuators++;

this is misusing continue, you might as well just do a if (TestBit))
num_valuators++.

> +    }
> +
> +    return pEvdev->mt_num_valuators;
> +}
> +
> +/**
> + * This function counts the number of valuators the device has.
> + * In case of multitouch device, it adds to this number a set of
> + * valuators to handle more than one touch.
> + *
> + * For instance, two touches can be received as:
> + *
> + * Valuator 0: x0
> + * Valuator 1: y0
> + * Valuator 2: pressure0
> + * Valuator 3: x1
> + * Valuator 4: y1
> + * Valuator 5: pressure1

I think it'd be good to use other numbers than starting with 0. we're pretty
much guaranteed that the device has other axes as valuators 0, 1 ...
> + */
> +static int
> +EvdevMTAddExtraValuators(EvdevPtr pEvdev, int total_num_axes)
> +{
> +    int num_axes = 0;
> +    int mt_num_valuators = 0;
> +
> +    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
> +        return total_num_axes;
> +
> +    mt_num_valuators = EvdevMTCountValuators(pEvdev);
> +
> +    /* substract the mt-valuators to total_num_axes
> +     * to have only the non-mt valuators */
> +    num_axes = total_num_axes - mt_num_valuators;
> +
> +    /* count the maximum number of touchpoints the device can support */
> +    pEvdev->mt_max_touchpoints = (MAX_VALUATORS - num_axes)
> +                                                / mt_num_valuators;
> +
> +    /* check if the device tells the number of touchpoints
> +     * it can support. */
> +    if (TestBit(ABS_MT_TRACKING_ID, pEvdev->abs_bitmask)) {
> +        int max_id = pEvdev->absinfo[ABS_MT_TRACKING_ID].maximum;
> +        if (max_id < pEvdev->mt_max_touchpoints)
> +            pEvdev->mt_max_touchpoints = max_id;
> +    }
> +
> +    num_axes += pEvdev->mt_max_touchpoints * mt_num_valuators;
> +
> +    return num_axes;
> +}
> +
> +/**
> + * As the first declared mt valuator is ABS_MT_TOUCH_MAJOR, it is then
                   ^^ this should be "defined", I think
> + * the first mt-related valuator.
> + * This function puts ABS_MT_POSITION_X and ABS_MT_POSITION_Y at places
> + * 0 and 1 in the set of multitouch valuators.
> + */
> +static void
> +EvdevMTSortValuators(EvdevPtr pEvdev)
> +{
> +    int axis, tmp_axis_value;
> +    int first_axis = 0;
> +    int second_axis = 0;
> +
> +    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
> +        return;
> +
> +    /* find the first and second mt axes */
> +    for (axis = ABS_MT_TOUCH_MAJOR; axis <= ABS_MAX; axis++) {

same assumption here again, might be best to abstract this with an evdev
specific define for highest ABS_MT supported.

> +        if (pEvdev->axis_map[axis] == -1)
> +            continue;
> +
> +        if (!first_axis)
> +            first_axis = axis;
> +        else if (!second_axis) {
> +            second_axis = axis;
> +            break;
> +        }
> +    }
> +
> +    /* do the actual swap */
> +    tmp_axis_value = pEvdev->axis_map[first_axis];
> +    pEvdev->axis_map[first_axis] = pEvdev->axis_map[ABS_MT_POSITION_X];
> +    pEvdev->axis_map[ABS_MT_POSITION_X] = tmp_axis_value;
> +
> +    tmp_axis_value = pEvdev->axis_map[second_axis];
> +    pEvdev->axis_map[second_axis] = pEvdev->axis_map[ABS_MT_POSITION_Y];
> +    pEvdev->axis_map[ABS_MT_POSITION_Y] = tmp_axis_value;
> +}
> +
> +/**
> + * As multitouch devices contains extra axes to enable multitouch,
> + * they are not initialized in the first init pass. This function
> + * does the init for those extra valuators.
> + */
> +static void
> +EvdevMTInitValuators(DeviceIntPtr device, Atom *atoms)
> +{
> +    InputInfoPtr pInfo;
> +    EvdevPtr pEvdev;
> +    int axis, j;
> +
> +    pInfo = device->public.devicePrivate;
> +    pEvdev = pInfo->private;
> +
> +    if (!(pEvdev->flags & EVDEV_MULTITOUCH))
> +        return;
> +
> +    /* Here j starts at 1 as one set of mt-valuators has already been
> +     * registered */
> +    for (j = 1; j < pEvdev->mt_max_touchpoints; j++) {
> +        for (axis = ABS_MT_TOUCH_MAJOR; axis <= ABS_MAX; axis++) {
> +            int axnum = pEvdev->axis_map[axis];
> +            int real_axnum = axnum + j * pEvdev->mt_num_valuators;
> +            if (axnum == -1)
> +                continue;
> +            xf86InitValuatorAxisStruct(device, real_axnum,
> +    #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7

no indentation for #ifdef's please.

> +                                       atoms[axnum],
> +    #endif
> +                                       pEvdev->absinfo[axis].minimum,
> +                                       pEvdev->absinfo[axis].maximum,
> +                                       10000, 0, 10000);

use the kernel resolution here please (see 7bbbce9a834).


> +            xf86InitValuatorDefaults(device, real_axnum);
> +            pEvdev->old_vals[real_axnum] = -1;
> +        }
> +    }
> +}
> +
>  static int
>  EvdevAddAbsClass(DeviceIntPtr device)
>  {
> @@ -1149,6 +1305,9 @@ EvdevAddAbsClass(DeviceIntPtr device)
>      num_axes = CountBits(pEvdev->abs_bitmask, NLONGS(ABS_MAX));
>      if (num_axes < 1)
>          return !Success;
> +
> +    num_axes = EvdevMTAddExtraValuators(pEvdev, num_axes);
> +
>      pEvdev->num_vals = num_axes;
>      memset(pEvdev->vals, 0, num_axes * sizeof(int));
>      memset(pEvdev->old_vals, -1, num_axes * sizeof(int));
> @@ -1162,6 +1321,8 @@ EvdevAddAbsClass(DeviceIntPtr device)
>          i++;
>      }
>  
> +    EvdevMTSortValuators(pEvdev);
> +
>      EvdevInitAxesLabels(pEvdev, pEvdev->num_vals, atoms);
>  
>      if (!InitValuatorClassDeviceStruct(device, num_axes,
> @@ -1200,6 +1361,8 @@ EvdevAddAbsClass(DeviceIntPtr device)
>  
>      free(atoms);
>  
> +    EvdevMTInitValuators(device, atoms);
> +
>      if (!InitPtrFeedbackClassDeviceStruct(device, EvdevPtrCtrlProc))
>          return !Success;
>  
> @@ -1503,7 +1666,7 @@ EvdevInit(DeviceIntPtr device)
>  
>      if (pEvdev->flags & (EVDEV_UNIGNORE_RELATIVE | EVDEV_UNIGNORE_ABSOLUTE))
>          EvdevInitAnyClass(device, pEvdev);
> -    else if (pEvdev->flags & (EVDEV_TOUCHPAD | EVDEV_TOUCHSCREEN | EVDEV_TABLET))
> +    else if (pEvdev->flags & (EVDEV_TOUCHPAD | EVDEV_TOUCHSCREEN | EVDEV_TABLET | EVDEV_MULTITOUCH))
>          EvdevInitTouchDevice(device, pEvdev);
>      else if (pEvdev->flags & EVDEV_RELATIVE_EVENTS)
>          EvdevInitRelClass(device, pEvdev);
> @@ -1875,6 +2038,15 @@ EvdevProbe(InputInfoPtr pInfo)
>          xf86Msg(X_PROBED, "%s: Found absolute axes\n", pInfo->name);
>          pEvdev->flags |= EVDEV_ABSOLUTE_EVENTS;
>  
> +        if ((TestBit(ABS_MT_POSITION_X, pEvdev->abs_bitmask) &&
> +                        TestBit(ABS_MT_POSITION_Y, pEvdev->abs_bitmask))) {

align the two TestBits please.

> +            xf86Msg(X_INFO, "%s: Found absolute multitouch device.\n", pInfo->name);
> +            pEvdev->flags |= EVDEV_MULTITOUCH;
> +            if (!pEvdev->num_buttons) {
> +                pEvdev->num_buttons = 7; /* LMR + scroll wheels */
> +                pEvdev->flags |= EVDEV_BUTTON_EVENTS;
> +            }
> +        }
>          if ((TestBit(ABS_X, pEvdev->abs_bitmask) &&
>               TestBit(ABS_Y, pEvdev->abs_bitmask))) {
>              xf86Msg(X_PROBED, "%s: Found x and y absolute axes\n", pInfo->name);
> @@ -1947,6 +2119,9 @@ EvdevProbe(InputInfoPtr pInfo)
>          } else if (pEvdev->flags & EVDEV_TOUCHSCREEN) {
>              xf86Msg(X_INFO, "%s: Configuring as touchscreen\n", pInfo->name);
>              pInfo->type_name = XI_TOUCHSCREEN;
> +	} else if (pEvdev->flags & EVDEV_MULTITOUCH) {
> +	    xf86Msg(X_INFO, "%s: Configuring as multitouch device\n", pInfo->name);
> +	    pInfo->type_name = "MULTITOUCHDEVICE";

this should probably be a define, so we can add it to XI in due time.

Cheers,
  Peter

>  	} else {
>  	    xf86Msg(X_INFO, "%s: Configuring as mouse\n", pInfo->name);
>  	    pInfo->type_name = XI_MOUSE;
> diff --git a/src/evdev.h b/src/evdev.h
> index 8c89f83..852f06c 100644
> --- a/src/evdev.h
> +++ b/src/evdev.h
> @@ -71,6 +71,7 @@
>  #define EVDEV_UNIGNORE_ABSOLUTE (1 << 9) /* explicitly unignore abs axes */
>  #define EVDEV_UNIGNORE_RELATIVE (1 << 10) /* explicitly unignore rel axes */
>  #define EVDEV_RELATIVE_MODE	(1 << 11) /* Force relative events for devices with absolute axes */
> +#define EVDEV_MULTITOUCH	(1 << 12) /* device looks like a multi-touch screen? */
>  
>  #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
>  #define HAVE_PROPERTIES 1
> @@ -80,7 +81,6 @@
>  #define MAX_VALUATORS 36
>  #endif
>  
> -
>  #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) < 5
>  typedef struct {
>      char *rules;
> @@ -193,6 +193,10 @@ typedef struct {
>      /* Event queue used to defer keyboard/button events until EV_SYN time. */
>      int                     num_queue;
>      EventQueueRec           queue[EVDEV_MAXQUEUE];
> +
> +    unsigned int mt_num_valuators;
> +    unsigned int mt_max_touchpoints; /* the number of simultaneous touchpoints
> +                                      * the device can support */
>  } EvdevRec, *EvdevPtr;
>  
>  /* Event posting functions */
> -- 
> 1.7.0.1
> 


More information about the xorg-devel mailing list