[RFC XI 2.1 - xserver 8/9] Input: Add initial multitouch support from Xi 2.1

Chase Douglas chase.douglas at canonical.com
Fri Nov 12 14:35:09 PST 2010


Xi 2.1 adds TouchClasses to devices, as well as TouchBegin, TouchMotion
and TouchEnd events, to allow support for multiple touchpoints on a
single device. This is missing support for any type of grabbing.

Based on an initial patch by Daniel Stone.

Signed-off-by: Daniel Stone <daniel at fooishbar.org>
Signed-off-by: Chase Douglas <chase.douglas at canonical.com>
---
 Xi/exevents.c                  |  107 ++++++++++++++++++++++++++++++++++++
 Xi/extinit.c                   |    3 +
 Xi/xiquerydevice.c             |   95 ++++++++++++++++++++++++++++++++
 Xi/xiquerydevice.h             |    3 +
 Xi/xiselectev.c                |   31 +++++++++++
 configure.ac                   |    2 +-
 dix/devices.c                  |  100 ++++++++++++++++++++++++++++++++++
 dix/eventconvert.c             |   12 ++++
 dix/events.c                   |   16 ++----
 dix/getevents.c                |  117 ++++++++++++++++++++++++++++++++++++++++
 dix/inpututils.c               |   45 +++++++++++++++
 hw/xfree86/common/xf86Xinput.c |   20 +++++++
 hw/xfree86/common/xf86Xinput.h |    3 +
 include/eventstr.h             |    3 +
 include/exevents.h             |    8 +++
 include/input.h                |   16 ++++++
 include/inputstr.h             |  111 +++++++++++++++++++++++--------------
 include/protocol-versions.h    |    2 +-
 mi/mieq.c                      |    6 ++
 19 files changed, 645 insertions(+), 55 deletions(-)

diff --git a/Xi/exevents.c b/Xi/exevents.c
index 0cd2246..bec4ef7 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -77,6 +77,7 @@ SOFTWARE.
 #include "xiquerydevice.h" /* For List*Info */
 #include "eventconvert.h"
 #include "eventstr.h"
+#include "xserver-properties.h"
 
 #include <X11/extensions/XKBproto.h>
 #include "xkbsrv.h"
@@ -923,6 +924,74 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
     }
 }
 
+static void
+ProcessTouchEvent(DeviceEvent *ev, DeviceIntPtr device)
+{
+    TouchClassPtr t = device->touch;
+    Window child;
+    SpritePtr sprite;
+    xEvent *xi2;
+    Mask filter;
+    int err, touch, i;
+
+    if (!t)
+        return;
+
+    touch = FindTouchPoint(device, ev->valuators.data[t->id_axis]);
+    if (touch < 0)
+    {
+        DebugF("[Xi] %s: Received touch event for inactive touchpoint %d\n",
+               device->name, ev->valuators.data[t->id_axis]);
+        return;
+    }
+
+    err = EventToXI2((InternalEvent *) ev, &xi2);
+    if (err != Success)
+    {
+        ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchBegin (%d)\n",
+               device->name, err);
+        return;
+    }
+
+    /* In direct touch mode, we focus immediately under the touchpoint, so
+     * we need to build a window trace; in Relative, we just use the sprite. */
+    if (device->touch->mode == XIDirectTouch) {
+        sprite = &device->touch->sprite[touch];
+        if (sprite->spriteTraceGood <= 0)
+            XYToWindow(sprite, ev->valuators.data[t->x_axis],
+                       ev->valuators.data[t->y_axis]);
+    } else
+        sprite = device->spriteInfo->sprite;
+
+    child = sprite->spriteTrace[sprite->spriteTraceGood - 1]->drawable.id;
+    for (i = 0; i < sprite->spriteTraceGood; i++)
+    {
+        WindowPtr win = sprite->spriteTrace[i];
+        int mask;
+        int deliveries;
+
+        mask = EventIsDeliverable(device, (InternalEvent *)ev, win);
+        if (!(mask & XI2_MASK))
+            continue;
+        
+        filter = GetEventFilter(device, xi2);
+        FixUpEventFromWindow(sprite, xi2, win, child, FALSE);
+        deliveries = DeliverEventsToWindow(device, win, xi2, 1, filter,
+                                           NullGrab);
+        if (deliveries > 0)
+            /* FIXME: mark event as "not-for-you-yet" for further deliveries */
+            ;
+    }
+
+    if (ev->type == ET_TouchEnd)
+    {
+        FinishTouchPoint(device, ev->valuators.data[t->id_axis]);
+        sprite->spriteTraceGood = 0;
+    }
+
+    free(xi2);
+}
+
 /**
  * Main device event processing function.
  * Called from when processing the events from the event queue.
@@ -952,6 +1021,12 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
     {
         ProcessRawEvent(&ev->raw_event, device);
         return;
+    } else if (ev->any.type == ET_TouchBegin ||
+               ev->any.type == ET_TouchMotion ||
+               ev->any.type == ET_TouchEnd)
+    {
+        ProcessTouchEvent(&ev->device_event, device);
+        return;
     }
 
     if (IsPointerDevice(device))
@@ -1150,6 +1225,38 @@ InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval, int
         dev->proximity->in_proximity = FALSE;
 }
 
+void
+InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
+                            int maxval, int resolution)
+{
+    TouchAxisInfoPtr ax;
+
+    if (!dev || !dev->touch || minval > maxval)
+        return;
+    if (axnum >= dev->touch->numAxes)
+        return;
+
+    ax = dev->touch->axes + axnum;
+
+    ax->min_value = minval;
+    ax->max_value = maxval;
+    ax->resolution = resolution;
+    ax->label = label;
+
+    if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_X))
+        dev->touch->x_axis = axnum;
+    else if (ax->label == XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_POSITION_Y))
+        dev->touch->y_axis = axnum;
+    else if (ax->label ==
+             XIGetKnownProperty(AXIS_LABEL_PROP_ABS_MT_TRACKING_ID)) {
+        int i;
+
+        dev->touch->id_axis = axnum;
+        for (i = 0; i < dev->touch->numTouches; i++)
+            dev->touch->last.valuators[i][axnum] = -1;
+    }
+}
+
 static void
 FixDeviceStateNotify(DeviceIntPtr dev, deviceStateNotify * ev, KeyClassPtr k,
 		     ButtonClassPtr b, ValuatorClassPtr v, int first)
diff --git a/Xi/extinit.c b/Xi/extinit.c
index eda4efb..d70a6a9 100644
--- a/Xi/extinit.c
+++ b/Xi/extinit.c
@@ -880,6 +880,9 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
         case XI_KeyRelease:
         case XI_ButtonPress:
         case XI_ButtonRelease:
+        case XI_TouchBegin:
+        case XI_TouchMotion:
+        case XI_TouchEnd:
             SDeviceEvent((xXIDeviceEvent*)from, (xXIDeviceEvent*)to);
             break;
         case XI_RawMotion:
diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index fdd2c05..421ac6d 100644
--- a/Xi/xiquerydevice.c
+++ b/Xi/xiquerydevice.c
@@ -232,6 +232,12 @@ SizeDeviceClasses(DeviceIntPtr dev)
     if (dev->valuator)
         len += sizeof(xXIValuatorInfo) * dev->valuator->numAxes;
 
+    if (dev->touch)
+    {
+        len += sizeof(xXITouchInfo);
+        len += sizeof(xXITouchValuatorInfo) * dev->touch->numAxes;
+    }
+
     return len;
 }
 
@@ -373,6 +379,73 @@ SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
     swaps(&info->sourceid, n);
 }
 
+/**
+ * List multitouch information
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchInfo(DeviceIntPtr dev, xXITouchInfo *touch)
+{
+    touch->type = XITouchClass;
+    touch->length = sizeof(xXITouchInfo) >> 2;
+    touch->sourceid = dev->id;
+    touch->mode = dev->touch->mode;
+    touch->num_touches = dev->touch->numTouches;
+
+    return touch->length << 2;
+}
+
+static void
+SwapTouchInfo(DeviceIntPtr dev, xXITouchInfo* touch)
+{
+    char n;
+    swaps(&touch->type, n);
+    swaps(&touch->length, n);
+    swaps(&touch->sourceid, n);
+}
+
+/**
+ * List multitouch axis information
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
+                      int axisnumber)
+{
+    TouchClassPtr t = dev->touch;
+
+    val->type = XITouchValuatorClass;
+    val->length = sizeof(xXITouchValuatorInfo) >> 2;
+    val->sourceid = dev->id;
+    val->number = axisnumber;
+    val->label = t->axes[axisnumber].label;
+    val->min.integral = t->axes[axisnumber].min_value;
+    val->min.frac = 0;
+    val->max.integral = t->axes[axisnumber].max_value;
+    val->max.frac = 0;
+    val->resolution = t->axes[axisnumber].resolution;
+
+    return val->length << 2;
+}
+
+static void
+SwapTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val)
+{
+    char n;
+    swaps(&val->type, n);
+    swaps(&val->length, n);
+    swaps(&val->sourceid, n);
+    swaps(&val->number, n);
+    swapl(&val->label, n);
+    swapl(&val->min.integral, n);
+    swapl(&val->min.frac, n);
+    swapl(&val->max.integral, n);
+    swapl(&val->max.frac, n);
+    swapl(&val->resolution, n);
+}
+
 int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
 {
     DeviceIntPtr master = dev->u.master;
@@ -462,6 +535,22 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
         total_len += len;
     }
 
+    if (dev->touch)
+    {
+        (*nclasses)++;
+        len = ListTouchInfo(dev, (xXITouchInfo*)any);
+        any += len;
+        total_len += len;
+
+        for (i = 0; i < dev->touch->numAxes; i++)
+        {
+            (*nclasses)++;
+            len = ListTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any, i);
+            any += len;
+            total_len += len;
+        }
+    }
+
     return total_len;
 }
 
@@ -489,6 +578,12 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
             case XIValuatorClass:
                 SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
                 break;
+            case XITouchClass:
+                SwapTouchInfo(dev, (xXITouchInfo*)any);
+                break;
+            case XITouchValuatorClass:
+                SwapTouchValuatorInfo(dev, (xXITouchValuatorInfo*)any);
+                break;
         }
 
         any += len * 4;
diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
index 02f0659..59326f7 100644
--- a/Xi/xiquerydevice.h
+++ b/Xi/xiquerydevice.h
@@ -44,4 +44,7 @@ int ListButtonInfo(DeviceIntPtr dev, xXIButtonInfo* info, Bool reportState);
 int ListKeyInfo(DeviceIntPtr dev, xXIKeyInfo* info);
 int ListValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info,
 		     int axisnumber, Bool reportState);
+int ListTouchInfo(DeviceIntPtr dev, xXITouchInfo* info);
+int ListTouchValuatorInfo(DeviceIntPtr dev, xXITouchValuatorInfo* val,
+                          int axisnumber);
 #endif /* QUERYDEV_H */
diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 7aa3f0a..32937fb 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -141,6 +141,37 @@ ProcXISelectEvents(ClientPtr client)
                 return BadValue;
         }
 
+        if (evmask->mask_len >= 1)
+        {
+            unsigned char *bits = (unsigned char*)&evmask[1];
+
+            /* All three touch events must be selected at once */
+            if ((BitIsOn(bits, XI_TouchBegin) ||
+                 BitIsOn(bits, XI_TouchMotion) ||
+                 BitIsOn(bits, XI_TouchEnd)) &&
+                (!BitIsOn(bits, XI_TouchBegin) ||
+                 !BitIsOn(bits, XI_TouchMotion) ||
+                 !BitIsOn(bits, XI_TouchEnd)))
+                return BadValue;
+
+            /* All master devices doesn't make sense for touch events */
+            if (BitIsOn(bits, XI_TouchBegin) &&
+                evmask->deviceid == XIAllMasterDevices)
+                return BadValue;
+
+            /* Only one client per window may select for touch begin events */
+            if (BitIsOn(bits, XI_TouchBegin))
+            {
+                OtherInputMasks *inputMasks = wOtherInputMasks(win);
+                if (inputMasks &&
+                    (BitIsOn(inputMasks->xi2mask[evmask->deviceid],
+                             XI_TouchBegin) ||
+                     BitIsOn(inputMasks->xi2mask[XIAllDevices],
+                             XI_TouchBegin)))
+                    return BadValue;
+            }
+        }
+
         if (XICheckInvalidMaskBits((unsigned char*)&evmask[1],
                                    evmask->mask_len * 4) != Success)
             return BadValue;
diff --git a/configure.ac b/configure.ac
index 77cad54..207205d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -794,7 +794,7 @@ WINDOWSWMPROTO="windowswmproto"
 APPLEWMPROTO="applewmproto >= 1.4"
 
 dnl Core modules for most extensions, et al.
-SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.2.99.3] [renderproto >= 0.11] [xextproto >= 7.0.99.3] [inputproto >= 1.9.99.902] [kbproto >= 1.0.3] fontsproto"
+SDK_REQUIRED_MODULES="[xproto >= 7.0.17] [randrproto >= 1.2.99.3] [renderproto >= 0.11] [xextproto >= 7.0.99.3] [inputproto >= 2.1] [kbproto >= 1.0.3] fontsproto"
 # Make SDK_REQUIRED_MODULES available for inclusion in xorg-server.pc
 AC_SUBST(SDK_REQUIRED_MODULES)
 
diff --git a/dix/devices.c b/dix/devices.c
index 67b7cde..490ec0b 100644
--- a/dix/devices.c
+++ b/dix/devices.c
@@ -754,6 +754,17 @@ FreeDeviceClass(int type, pointer *class)
                 free((*v));
                 break;
             }
+        case XITouchClass:
+            {
+                TouchClassPtr *t = (TouchClassPtr*)class;
+                int i;
+
+                for (i = 0; i < (*t)->numTouches; i++)
+                    free((*t)->sprite[i].spriteTrace);
+
+                free((*t));
+                break;
+            }
         case FocusClass:
             {
                 FocusClassPtr *f = (FocusClassPtr*)class;
@@ -862,6 +873,7 @@ FreeAllDeviceClasses(ClassesPtr classes)
 
     FreeDeviceClass(KeyClass, (pointer)&classes->key);
     FreeDeviceClass(ValuatorClass, (pointer)&classes->valuator);
+    FreeDeviceClass(XITouchClass, (pointer)&classes->touch);
     FreeDeviceClass(ButtonClass, (pointer)&classes->button);
     FreeDeviceClass(FocusClass, (pointer)&classes->focus);
     FreeDeviceClass(ProximityClass, (pointer)&classes->proximity);
@@ -1543,6 +1555,94 @@ InitPointerDeviceStruct(DevicePtr device, CARD8 *map, int numButtons, Atom* btn_
 	   InitPtrFeedbackClassDeviceStruct(dev, controlProc));
 }
 
+/**
+ * Sets up multitouch capabilities on @device.
+ *
+ * @max_touches The maximum number of simultaneous touches, or 0 for unlimited.
+ * @mode The mode of the touch device (XIDirectTouch or XIDependentTouch).
+ * @numAxes The number of valuator axes for the touch device
+ */
+Bool
+InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches,
+                           unsigned int mode, unsigned int numAxes)
+{
+    TouchClassPtr touch;
+    int *valuators;
+    float *remainder;
+    int i;
+
+    if (device->touch)
+        return FALSE;
+
+    if ((mode != XIDirectTouch && mode != XIDependentTouch) ||
+        max_touches == 0 || numAxes <= 4)
+        return FALSE;
+
+    if (numAxes > MAX_VALUATORS)
+    {
+        LogMessage(X_WARNING,
+                   "Device '%s' has %d axes, only using first %d.\n",
+                   device->name, numAxes, MAX_VALUATORS);
+        numAxes = MAX_VALUATORS;
+    }
+    
+
+    touch = calloc(1,
+                   sizeof(TouchClassRec) +
+                   numAxes * sizeof(TouchAxisInfo) +
+                   max_touches * sizeof(SpriteRec) +
+                   max_touches * sizeof(int *) +
+                   max_touches * numAxes * sizeof(int) +
+                   max_touches * sizeof(float *) +
+                   max_touches * numAxes * sizeof(float));
+    if (!touch)
+        return FALSE;
+
+    touch->axes = (TouchAxisInfoPtr)(touch + 1);
+    touch->sprite = (SpriteRec *)(touch->axes + numAxes);
+    touch->last.valuators = (int **)(touch->sprite + max_touches);
+
+    valuators = (int *)(touch->last.valuators + max_touches);
+    for (i = 0; i < max_touches; i++)
+    {
+        touch->last.valuators[i] = valuators;
+        valuators += numAxes;
+    }
+
+    touch->last.remainder = (float **)valuators;
+    remainder = (float *)(touch->last.remainder + max_touches);
+    for (i = 0; i < max_touches; i++)
+    {
+        touch->last.remainder[i] = remainder;
+        remainder += numAxes;
+    }
+
+    for (i = 0; i < max_touches; i++)
+    {
+        SpritePtr sprite = &touch->sprite[i];
+
+        sprite->spriteTrace = (WindowPtr *)calloc(1, 32*sizeof(WindowPtr));
+        if (!sprite->spriteTrace)
+            FatalError("Failed to allocate touch spriteTrace");
+        sprite->spriteTraceSize = 32;
+        sprite->spriteTrace[0] = screenInfo.screens[0]->root;
+        sprite->hot.pScreen = screenInfo.screens[0];
+        sprite->hotPhys.pScreen = screenInfo.screens[0];
+    }
+
+    touch->sourceid = device->id;
+    touch->numAxes = numAxes;
+    touch->numTouches = max_touches;
+    touch->mode = mode;
+    touch->x_axis = -1;
+    touch->y_axis = -1;
+    touch->id_axis = -1;
+
+    device->touch = touch;
+
+    return TRUE;
+}
+
 /*
  * Check if the given buffer contains elements between low (inclusive) and
  * high (inclusive) only.
diff --git a/dix/eventconvert.c b/dix/eventconvert.c
index 991298e..b931065 100644
--- a/dix/eventconvert.c
+++ b/dix/eventconvert.c
@@ -130,6 +130,9 @@ EventToCore(InternalEvent *event, xEvent *core)
         case ET_RawButtonPress:
         case ET_RawButtonRelease:
         case ET_RawMotion:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             return BadMatch;
         default:
             /* XXX: */
@@ -175,6 +178,9 @@ EventToXI(InternalEvent *ev, xEvent **xi, int *count)
         case ET_RawButtonPress:
         case ET_RawButtonRelease:
         case ET_RawMotion:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             *count = 0;
             *xi = NULL;
             return BadMatch;
@@ -216,6 +222,9 @@ EventToXI2(InternalEvent *ev, xEvent **xi)
         case ET_ButtonRelease:
         case ET_KeyPress:
         case ET_KeyRelease:
+        case ET_TouchBegin:
+        case ET_TouchMotion:
+        case ET_TouchEnd:
             return eventToDeviceEvent(&ev->device_event, xi);
         case ET_ProximityIn:
         case ET_ProximityOut:
@@ -724,6 +733,9 @@ GetXI2Type(InternalEvent *event)
         case ET_RawMotion:      xi2type = XI_RawMotion;        break;
         case ET_FocusIn:        xi2type = XI_FocusIn;          break;
         case ET_FocusOut:       xi2type = XI_FocusOut;         break;
+        case ET_TouchBegin:     xi2type = XI_TouchBegin;       break;
+        case ET_TouchEnd:       xi2type = XI_TouchEnd;         break;
+        case ET_TouchMotion:    xi2type = XI_TouchMotion;      break;
         default:
             break;
     }
diff --git a/dix/events.c b/dix/events.c
index 9c56bb5..dc1727e 100644
--- a/dix/events.c
+++ b/dix/events.c
@@ -334,12 +334,6 @@ IsMaster(DeviceIntPtr dev)
     return dev->type == MASTER_POINTER || dev->type == MASTER_KEYBOARD;
 }
 
-static WindowPtr XYToWindow(
-    SpritePtr pSprite,
-    int x,
-    int y
-);
-
 /**
  * Max event opcode.
  */
@@ -447,7 +441,7 @@ GetWindowXI2Mask(DeviceIntPtr dev, WindowPtr win, xEvent* ev)
             (inputMasks->xi2mask[XIAllMasterDevices][evtype/8] && IsMaster(dev)));
 }
 
-static Mask
+Mask
 GetEventMask(DeviceIntPtr dev, xEvent *event, InputClients* other)
 {
     /* XI2 filters are only ever 8 bit, so let's return a 8 bit mask */
@@ -1277,7 +1271,6 @@ ComputeFreezes(void)
 {
     DeviceIntPtr replayDev = syncEvents.replayDev;
     int i;
-    SpritePtr pSprite = replayDev->spriteInfo->sprite;
     WindowPtr w;
     GrabPtr grab;
     DeviceIntPtr dev;
@@ -1291,6 +1284,7 @@ ComputeFreezes(void)
     if (replayDev)
     {
         DeviceEvent* event = replayDev->deviceGrab.sync.event;
+        SpritePtr pSprite = replayDev->spriteInfo->sprite;
 
 	syncEvents.replayDev = (DeviceIntPtr)NULL;
 
@@ -2571,7 +2565,7 @@ PointInBorderSize(WindowPtr pWin, int x, int y)
  *
  * @returns the window at the given coordinates.
  */
-static WindowPtr
+WindowPtr
 XYToWindow(SpritePtr pSprite, int x, int y)
 {
     WindowPtr  pWin;
@@ -3440,9 +3434,9 @@ CheckPassiveGrabsOnWindow(
         tempGrab.detail.exact = event->detail.key;
         if (!match)
         {
-            tempGrab.type = GetXIType((InternalEvent*)event);
             tempGrab.grabtype = GRABTYPE_XI;
-            if (GrabMatchesSecond(&tempGrab, grab, FALSE))
+            if ((tempGrab.type = GetXIType((InternalEvent*)event)) &&
+                (GrabMatchesSecond(&tempGrab, grab, FALSE)))
                 match = XI_MATCH;
         }
 
diff --git a/dix/getevents.c b/dix/getevents.c
index 8ebd63b..a785a0c 100644
--- a/dix/getevents.c
+++ b/dix/getevents.c
@@ -47,6 +47,7 @@
 #include "eventstr.h"
 #include "eventconvert.h"
 #include "inpututils.h"
+#include "windowstr.h"
 
 #include <X11/extensions/XKBproto.h>
 #include "xkbsrv.h"
@@ -1274,6 +1275,122 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, const Valuato
 }
 
 /**
+ * Get events for a touch. Generates a TouchBegin event if end is not set and
+ * the touch id is not active. Generates a TouchMotion event if end is not set
+ * and the touch id is active. Generates a TouchEnd event if end is set and the
+ * touch id is active.
+ *
+ * events is not NULL-terminated; the return value is the number of events.
+ * The DDX is responsible for allocating the event structure in the first
+ * place via GetMaximumEventsNum(), and for freeing it.
+ */
+int
+GetTouchEvents(EventList *events, DeviceIntPtr pDev,
+               const ValuatorMask *mask_in, Bool end)
+{
+    int x, y;
+    float x_frac = 0.0, y_frac = 0.0;
+    DeviceEvent *event;
+    ValuatorMask mask;
+    int touch;
+    TouchClassPtr t = pDev->touch;
+    ScreenPtr scr = pDev->spriteInfo->sprite->hotPhys.pScreen;
+    CARD32 ms = GetTimeInMillis();
+
+    /* refuse events from disabled devices */
+    if (!pDev->enabled)
+        return 0;
+
+    /* Sanity check. */
+    if (!t)
+        return 0;
+
+    /* These axes must exist */
+    if (t->x_axis < 0 || t->y_axis < 0 || t->id_axis < 0) {
+        return 0;
+    }
+
+    /* Touch ID must exist */
+    if (!valuator_mask_isset(mask_in, t->id_axis))
+        return 0;
+
+    touch = FindTouchPoint(pDev, valuator_mask_get(mask_in, t->id_axis));
+
+    /* Touch must exist or must be starting and have both X and Y */
+    if (touch < 0 && (end || !valuator_mask_isset(mask_in, t->x_axis) ||
+                      !valuator_mask_isset(mask_in, t->y_axis)))
+        return 0;
+
+    valuator_mask_copy(&mask, mask_in);
+
+    /* Get our screen event co-ordinates (root_x/root_y/event_x/event_y):
+     * these come from the touchpoint in Absolute mode, or the sprite in
+     * Relative. */
+    if (t->mode == XIDirectTouch) {
+        int scaled;
+
+        if (valuator_mask_isset(&mask, t->x_axis))
+        {
+            scaled = rescaleValuatorAxis(valuator_mask_get(&mask, t->x_axis),
+                                         0.0, &x_frac,
+                                         (AxisInfoPtr)(t->axes + t->x_axis),
+                                         NULL, scr->width);
+            valuator_mask_set(&mask, t->x_axis, scaled);
+            x = scaled;
+        } else {
+            x = t->last.valuators[touch][t->x_axis];
+            x_frac = t->last.remainder[touch][t->x_axis];
+        }
+
+        if (valuator_mask_isset(&mask, t->y_axis))
+        {
+            scaled = rescaleValuatorAxis(valuator_mask_get(&mask, t->y_axis),
+                                         0.0, &y_frac,
+                                         (AxisInfoPtr)(t->axes + t->y_axis),
+                                         NULL, scr->height);
+            valuator_mask_set(&mask, t->y_axis, scaled);
+            y = scaled;
+        } else {
+            y = t->last.valuators[touch][t->y_axis];
+            y_frac = t->last.remainder[touch][t->y_axis];
+        }
+    }
+    else {
+        x = pDev->spriteInfo->sprite->hotPhys.x;
+        y = pDev->spriteInfo->sprite->hotPhys.y;
+    }
+
+    event = (DeviceEvent *)events->event;
+    init_event(pDev, event, ms);
+    event->root = scr->root->drawable.id;
+    event->root_x = x;
+    event->root_y = y;
+    event->root_x_frac = x_frac;
+    event->root_y_frac = y_frac;
+
+    set_valuators(pDev, event, &mask);
+
+    if (touch < 0) {
+        touch = CreateTouchPoint(pDev, valuator_mask_get(&mask, t->id_axis));
+        if (touch < 0)
+            return 0;
+
+        event->type = ET_TouchBegin;
+    }
+    else if (end)
+        event->type = ET_TouchEnd;
+    else
+        event->type = ET_TouchMotion;
+
+    t->last.valuators[touch][t->x_axis] = x;
+    t->last.remainder[touch][t->x_axis] = x_frac;
+    t->last.valuators[touch][t->y_axis] = y;
+    t->last.remainder[touch][t->y_axis] = y_frac;
+
+    return 1;
+}
+
+/**
  * Synthesize a single motion event for the core pointer.
  *
  * Used in cursor functions, e.g. when cursor confinement changes, and we need
diff --git a/dix/inpututils.c b/dix/inpututils.c
index 3d96ec7..ab8d9ce 100644
--- a/dix/inpututils.c
+++ b/dix/inpututils.c
@@ -546,3 +546,48 @@ CountBits(const uint8_t *mask, int len)
 
     return ret;
 }
+
+int
+FindTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int i;
+
+    if (!dev->touch)
+        return -1;
+
+    for (i = 0; i < dev->touch->numTouches; i++)
+        if (dev->touch->last.valuators[i][dev->touch->id_axis] == (int)touchid)
+            return i;
+
+    return -1;
+}
+
+int
+CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int touch;
+
+    if (!dev->touch)
+        return -1;
+
+    if (FindTouchPoint(dev, touchid) >= 0)
+        return -1;
+
+    touch = FindTouchPoint(dev, -1);
+    if (touch < 0)
+        return -1;
+
+    dev->touch->last.valuators[touch][dev->touch->id_axis] = touchid;
+    return touch;
+}
+
+void
+FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid)
+{
+    int touch = FindTouchPoint(dev, touchid);
+
+    if (touch < 0)
+        return;
+
+    dev->touch->last.valuators[touch][dev->touch->id_axis] = -1;
+}
diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c
index b910cf8..1ca9dfb 100644
--- a/hw/xfree86/common/xf86Xinput.c
+++ b/hw/xfree86/common/xf86Xinput.c
@@ -1334,6 +1334,16 @@ xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 			   max_res, mode);
 }
 
+void
+xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
+                                int minval, int maxval, int resolution)
+{
+    if (!dev || !dev->touch)
+        return;
+
+    InitTouchValuatorAxisStruct(dev, axnum, label, minval, maxval, resolution);
+}
+
 /*
  * Set the valuator values to be in synch with dix/event.c
  * DefineInitialRootWindow().
@@ -1385,4 +1395,14 @@ xf86EnableDevice(DeviceIntPtr dev)
     EnableDevice(dev, TRUE);
 }
 
+void
+xf86PostTouchEvent(DeviceIntPtr dev, const ValuatorMask *mask, int end)
+{
+    int i, nevents;
+
+    nevents = GetTouchEvents(xf86Events, dev, mask, end);
+    for (i = 0; i < nevents; i++)
+        mieqEnqueue(dev, (InternalEvent *)((xf86Events + i)->event));
+}
+
 /* end of xf86Xinput.c */
diff --git a/hw/xfree86/common/xf86Xinput.h b/hw/xfree86/common/xf86Xinput.h
index b310eaf..8ceb96d 100644
--- a/hw/xfree86/common/xf86Xinput.h
+++ b/hw/xfree86/common/xf86Xinput.h
@@ -141,6 +141,7 @@ extern _X_EXPORT void xf86PostKeyEventP(DeviceIntPtr device, unsigned int key_co
 		       const int *valuators);
 extern _X_EXPORT void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code,
                            int is_down);
+extern _X_EXPORT void xf86PostTouchEvent(DeviceIntPtr dev, const ValuatorMask *mask, int end);
 extern _X_EXPORT InputInfoPtr xf86FirstLocalDevice(void);
 extern _X_EXPORT int xf86ScaleAxis(int Cx, int Sxhigh, int Sxlow, int Rxhigh, int Rxlow);
 extern _X_EXPORT void xf86XInputSetScreen(InputInfoPtr pInfo, int screen_number, int x, int y);
@@ -148,6 +149,8 @@ extern _X_EXPORT void xf86ProcessCommonOptions(InputInfoPtr pInfo, pointer optio
 extern _X_EXPORT void xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label, int minval,
 				int maxval, int resolution, int min_res,
 				int max_res, int mode);
+extern _X_EXPORT void xf86InitTouchValuatorAxisStruct(DeviceIntPtr dev, int axnum, Atom label,
+				int minval, int maxval, int resolution);
 extern _X_EXPORT void xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum);
 extern _X_EXPORT void xf86AddEnabledDevice(InputInfoPtr pInfo);
 extern _X_EXPORT void xf86RemoveEnabledDevice(InputInfoPtr pInfo);
diff --git a/include/eventstr.h b/include/eventstr.h
index 433227e..61c4c24 100644
--- a/include/eventstr.h
+++ b/include/eventstr.h
@@ -65,6 +65,9 @@ enum EventType {
     ET_RawButtonRelease,
     ET_RawMotion,
     ET_XQuartz,
+    ET_TouchBegin,
+    ET_TouchEnd,
+    ET_TouchMotion,
     ET_Internal = 0xFF /* First byte */
 };
 
diff --git a/include/exevents.h b/include/exevents.h
index bfee385..3961c68 100644
--- a/include/exevents.h
+++ b/include/exevents.h
@@ -51,6 +51,14 @@ extern _X_EXPORT void InitValuatorAxisStruct(
 	int                    /* max_res */,
 	int                    /* mode */);
 
+extern _X_EXPORT void InitTouchValuatorAxisStruct(
+	DeviceIntPtr           /* dev */,
+	int                    /* axnum */,
+	Atom                   /* label */,
+	int                    /* minval */,
+	int                    /* maxval */,
+	int                    /* resolution */);
+
 /* Input device properties */
 extern _X_EXPORT void XIDeleteAllDeviceProperties(
         DeviceIntPtr            /* device */
diff --git a/include/input.h b/include/input.h
index c1db544..36c176c 100644
--- a/include/input.h
+++ b/include/input.h
@@ -314,6 +314,12 @@ extern _X_EXPORT Bool InitAbsoluteClassDeviceStruct(
 extern _X_EXPORT Bool InitFocusClassDeviceStruct(
     DeviceIntPtr /*device*/);
 
+extern _X_EXPORT Bool InitTouchClassDeviceStruct(
+    DeviceIntPtr /*device*/,
+    unsigned int /*max_touches*/,
+    unsigned int /*mode*/,
+    unsigned int /*numAxes*/);
+
 typedef void (*BellProcPtr)(
     int /*percent*/,
     DeviceIntPtr /*device*/,
@@ -463,6 +469,12 @@ extern int GetKeyboardValuatorEvents(
     int key_code,
     const ValuatorMask *mask);
 
+extern int GetTouchEvents(
+    EventListPtr events,
+    DeviceIntPtr pDev,
+    const ValuatorMask *mask,
+    int end);
+
 extern int GetProximityEvents(
     EventListPtr events,
     DeviceIntPtr pDev,
@@ -524,8 +536,12 @@ extern DeviceIntPtr GetXTestDevice(DeviceIntPtr master);
 extern void SendDevicePresenceEvent(int deviceid, int type);
 extern _X_EXPORT InputAttributes *DuplicateInputAttributes(InputAttributes *attrs);
 extern _X_EXPORT void FreeInputAttributes(InputAttributes *attrs);
+extern int CreateTouchPoint(DeviceIntPtr dev, unsigned int touchid);
+extern int FindTouchPoint(DeviceIntPtr dev, unsigned int touchid);
+extern void FinishTouchPoint(DeviceIntPtr dev, unsigned int touchid);
 
 /* misc event helpers */
+extern Mask GetEventMask(DeviceIntPtr dev, xEvent* ev, InputClientsPtr clients);
 extern Mask GetEventFilter(DeviceIntPtr dev, xEvent *event);
 extern Mask GetWindowXI2Mask(DeviceIntPtr dev, WindowPtr win, xEvent* ev);
 void FixUpEventFromWindow(SpritePtr pSprite,
diff --git a/include/inputstr.h b/include/inputstr.h
index b3c60b2..cb0ba32 100644
--- a/include/inputstr.h
+++ b/include/inputstr.h
@@ -49,6 +49,8 @@ SOFTWARE.
 #ifndef INPUTSTRUCT_H
 #define INPUTSTRUCT_H
 
+#include <X11/extensions/XI2proto.h>
+
 #include <pixman.h>
 #include "input.h"
 #include "window.h"
@@ -71,7 +73,7 @@ extern _X_EXPORT int CountBits(const uint8_t *mask, int len);
  * events to the protocol, the server will not support these events until
  * this number here is bumped.
  */
-#define XI2LASTEVENT    17 /* XI_RawMotion */
+#define XI2LASTEVENT    XI_TouchMotion
 #define XI2MASKSIZE     ((XI2LASTEVENT + 7)/8) /* no of bits for masks */
 
 /**
@@ -203,6 +205,47 @@ typedef struct _GrabRec {
     unsigned char       xi2mask[EMASKSIZE][XI2MASKSIZE];
 } GrabRec;
 
+/**
+ * Sprite information for a device.
+ */
+typedef struct _SpriteRec {
+    CursorPtr	current;
+    BoxRec	hotLimits;	/* logical constraints of hot spot */
+    Bool	confined;	/* confined to screen */
+    RegionPtr	hotShape;	/* additional logical shape constraint */
+    BoxRec	physLimits;	/* physical constraints of hot spot */
+    WindowPtr	win;		/* window of logical position */
+    HotSpot	hot;		/* logical pointer position */
+    HotSpot	hotPhys;	/* physical pointer position */
+#ifdef PANORAMIX
+    ScreenPtr	screen;		/* all others are in Screen 0 coordinates */
+    RegionRec   Reg1;	        /* Region 1 for confining motion */
+    RegionRec   Reg2;		/* Region 2 for confining virtual motion */
+    WindowPtr   windows[MAXSCREENS];
+    WindowPtr	confineWin;	/* confine window */ 
+#endif
+    /* The window trace information is used at dix/events.c to avoid having
+     * to compute all the windows between the root and the current pointer
+     * window each time a button or key goes down. The grabs on each of those
+     * windows must be checked.
+     * spriteTraces should only be used at dix/events.c! */
+    WindowPtr *spriteTrace;
+    int spriteTraceSize;
+    int spriteTraceGood;
+
+    /* Due to delays between event generation and event processing, it is
+     * possible that the pointer has crossed screen boundaries between the
+     * time in which it begins generating events and the time when
+     * those events are processed.
+     *
+     * pEnqueueScreen: screen the pointer was on when the event was generated
+     * pDequeueScreen: screen the pointer was on when the event is processed
+     */
+    ScreenPtr pEnqueueScreen;
+    ScreenPtr pDequeueScreen;
+
+} SpriteRec;
+
 typedef struct _KeyClassRec {
     int			sourceid;
     CARD8		down[DOWN_LENGTH];
@@ -243,6 +286,29 @@ typedef struct _ValuatorClassRec {
     ValuatorAccelerationRec	accelScheme;
 } ValuatorClassRec, *ValuatorClassPtr;
 
+typedef struct _TouchAxisInfo {
+    int		resolution;
+    int		min_value;
+    int		max_value;
+    Atom	label;
+} TouchAxisInfo, *TouchAxisInfoPtr;
+
+typedef struct _TouchClassRec {
+    int                   sourceid;
+    TouchAxisInfoPtr      axes;
+    unsigned short        numAxes;
+    unsigned short        numTouches;
+    SpriteRec             *sprite;
+    struct {
+        int               **valuators;
+        float             **remainder;
+    } last;
+    CARD8                 mode;
+    int                   x_axis;
+    int                   y_axis;
+    int                   id_axis;
+} TouchClassRec, *TouchClassPtr;
+
 typedef struct _ButtonClassRec {
     int			sourceid;
     CARD8		numButtons;
@@ -347,6 +413,7 @@ typedef struct _LedFeedbackClassRec {
 typedef struct _ClassesRec {
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr	touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
@@ -360,47 +427,6 @@ typedef struct _ClassesRec {
 } ClassesRec;
 
 
-/**
- * Sprite information for a device.
- */
-typedef struct _SpriteRec {
-    CursorPtr	current;
-    BoxRec	hotLimits;	/* logical constraints of hot spot */
-    Bool	confined;	/* confined to screen */
-    RegionPtr	hotShape;	/* additional logical shape constraint */
-    BoxRec	physLimits;	/* physical constraints of hot spot */
-    WindowPtr	win;		/* window of logical position */
-    HotSpot	hot;		/* logical pointer position */
-    HotSpot	hotPhys;	/* physical pointer position */
-#ifdef PANORAMIX
-    ScreenPtr	screen;		/* all others are in Screen 0 coordinates */
-    RegionRec   Reg1;	        /* Region 1 for confining motion */
-    RegionRec   Reg2;		/* Region 2 for confining virtual motion */
-    WindowPtr   windows[MAXSCREENS];
-    WindowPtr	confineWin;	/* confine window */ 
-#endif
-    /* The window trace information is used at dix/events.c to avoid having
-     * to compute all the windows between the root and the current pointer
-     * window each time a button or key goes down. The grabs on each of those
-     * windows must be checked.
-     * spriteTraces should only be used at dix/events.c! */
-    WindowPtr *spriteTrace;
-    int spriteTraceSize;
-    int spriteTraceGood;
-
-    /* Due to delays between event generation and event processing, it is
-     * possible that the pointer has crossed screen boundaries between the
-     * time in which it begins generating events and the time when
-     * those events are processed.
-     *
-     * pEnqueueScreen: screen the pointer was on when the event was generated
-     * pDequeueScreen: screen the pointer was on when the event is processed
-     */
-    ScreenPtr pEnqueueScreen;
-    ScreenPtr pDequeueScreen;
-
-} SpriteRec;
-
 /* Device properties */
 typedef struct _XIPropertyValue
 {
@@ -512,6 +538,7 @@ typedef struct _DeviceIntRec {
     int			id;
     KeyClassPtr		key;
     ValuatorClassPtr	valuator;
+    TouchClassPtr	touch;
     ButtonClassPtr	button;
     FocusClassPtr	focus;
     ProximityClassPtr	proximity;
diff --git a/include/protocol-versions.h b/include/protocol-versions.h
index c674465..f21d632 100644
--- a/include/protocol-versions.h
+++ b/include/protocol-versions.h
@@ -127,7 +127,7 @@
 
 /* X Input */
 #define SERVER_XI_MAJOR_VERSION			2
-#define SERVER_XI_MINOR_VERSION			0
+#define SERVER_XI_MINOR_VERSION			1
 
 /* XKB */
 #define SERVER_XKB_MAJOR_VERSION		1
diff --git a/mi/mieq.c b/mi/mieq.c
index d1441e2..f176ec0 100644
--- a/mi/mieq.c
+++ b/mi/mieq.c
@@ -269,6 +269,9 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event)
         case ET_ProximityOut:
         case ET_Hierarchy:
         case ET_DeviceChanged:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             event->device_event.deviceid = dev->id;
             break;
 #if XFreeXDGA
@@ -381,6 +384,9 @@ mieqProcessDeviceEvent(DeviceIntPtr dev,
         case ET_KeyRelease:
         case ET_ButtonPress:
         case ET_ButtonRelease:
+        case ET_TouchBegin:
+        case ET_TouchEnd:
+        case ET_TouchMotion:
             if (dev && screen && screen != DequeueScreen(dev) && !handler) {
                 DequeueScreen(dev) = screen;
                 x = event->device_event.root_x;
-- 
1.7.1



More information about the xorg-devel mailing list