summaryrefslogtreecommitdiff
path: root/Xi
diff options
context:
space:
mode:
authorDaniel Stone <daniel@fooishbar.org>2010-09-20 15:03:06 +1000
committerChase Douglas <chase.douglas@ubuntu.com>2010-10-13 21:42:27 +0200
commita6cdd84853df31b8d2beffcbf7fbc5e51dbd74b5 (patch)
tree4147c8c3b56826ca841aa3f780bdf31e80ae3838 /Xi
parentf7d671b89a6c4e00f1333960b56d18f69548628e (diff)
Input: Add initial multitouch support from Xi 2.1
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 a full implementation of the Xi 2.1 additions. Signed-off-by: Daniel Stone <daniel@fooishbar.org>
Diffstat (limited to 'Xi')
-rw-r--r--Xi/exevents.c423
-rw-r--r--Xi/extinit.c65
-rw-r--r--Xi/xiallowev.c38
-rw-r--r--Xi/xipassivegrab.c11
-rw-r--r--Xi/xiquerydevice.c125
-rw-r--r--Xi/xiquerydevice.h2
-rw-r--r--Xi/xiselectev.c21
7 files changed, 677 insertions, 8 deletions
diff --git a/Xi/exevents.c b/Xi/exevents.c
index 1f02cb0d9..038b7dcea 100644
--- a/Xi/exevents.c
+++ b/Xi/exevents.c
@@ -87,6 +87,7 @@ SOFTWARE.
Mod3Mask | Mod4Mask | Mod5Mask )
#define AllButtonsMask ( \
Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask )
+#define RootWindow(dev) dev->spriteInfo->sprite->spriteTrace[0]
Bool ShouldFreeInputMasks(WindowPtr /* pWin */ ,
Bool /* ignoreSelectedEvents */
@@ -572,6 +573,49 @@ DeepCopyPointerClasses(DeviceIntPtr from, DeviceIntPtr to)
to->valuator = NULL;
}
+ if (from->touch)
+ {
+ TouchClassPtr t;
+ int i;
+
+ if (!to->touch)
+ {
+ classes = to->unused_classes;
+ to->touch = classes->touch;
+ if (to->touch)
+ classes->touch = NULL;
+ }
+
+ to->touch = realloc(to->touch,
+ sizeof(*to->touch) +
+ (from->touch->num_touches *
+ sizeof(*from->touch->touches)));
+ t = to->touch;
+ if (!t)
+ FatalError("[Xi] no memory for class shift.\n");
+
+ t->min_x = from->touch->min_x;
+ t->max_x = from->touch->max_x;
+ t->min_y = from->touch->min_y;
+ t->max_y = from->touch->max_y;
+ t->min_touch_width = from->touch->min_touch_width;
+ t->max_touch_width = from->touch->max_touch_width;
+ t->max_touches = from->touch->max_touches;
+ t->num_touches = from->touch->num_touches;
+ t->touches = (TouchInfoPtr) &t[1];
+ memcpy(t->touches, from->touch->touches,
+ t->num_touches * sizeof(*t->touches));
+ for (i = 0; i < t->num_touches; i++)
+ t->touches[i].sourceid = from->id;
+ }
+ else if (to->touch && !from->touch)
+ {
+ ClassesPtr classes;
+ classes = to->unused_classes;
+ classes->touch = to->touch;
+ to->touch = NULL;
+ }
+
if (from->button)
{
if (!to->button)
@@ -931,6 +975,340 @@ ProcessRawEvent(RawDeviceEvent *ev, DeviceIntPtr device)
}
}
+static Bool
+CheckTouchQueueSize(TouchInfoPtr touch)
+{
+ InternalEvent *tmp;
+
+ if (touch->num_frozen_events < touch->size_frozen_events)
+ return TRUE;
+
+ touch->size_frozen_events += 10;
+ tmp = realloc(touch->frozen_events,
+ touch->size_frozen_events * sizeof(InternalEvent));
+ if (!tmp)
+ {
+ touch->size_frozen_events -= 10;
+ return FALSE;
+ }
+ touch->frozen_events = tmp;
+
+ return TRUE;
+}
+
+static void
+ProcessTouchMotionEvent(TouchMotionEvent *ev, DeviceIntPtr device)
+{
+ TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+ xEvent *xi2;
+ int ret;
+ WindowPtr win;
+
+ if (!touch)
+ {
+ DebugF("[Xi] %s: Received TouchMotion event for dead touchpoint %d\n",
+ device->name, ev->touchid);
+ return;
+ }
+
+ /* If the touch is currently frozen, add it to the queue if possible or
+ * drop the event if not, then get out. */
+ if (touch->frozen)
+ {
+ if (CheckTouchQueueSize(touch))
+ touch->frozen_events[touch->num_frozen_events++] =
+ *(InternalEvent*)ev;
+ return;
+ }
+
+ if (ev->mask & XITouchXMask)
+ touch->last_x = ev->x;
+ if (ev->mask & XITouchYMask)
+ touch->last_y = ev->y;
+ if (ev->mask & XITouchTouchSizeMask)
+ {
+ touch->last_touch_major = ev->touch_width_major;
+ touch->last_touch_minor = ev->touch_width_minor;
+ }
+ if (ev->mask & XITouchToolSizeMask)
+ {
+ touch->last_tool_major = ev->tool_width_major;
+ touch->last_tool_minor = ev->tool_width_minor;
+ }
+ if (ev->mask & XITouchOrientationMask)
+ touch->last_orientation = ev->orientation;
+
+ ret = EventToXI2((InternalEvent *) ev, &xi2);
+ if (ret != Success)
+ {
+ ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchMotionEvent (%d)\n",
+ device->name, ret);
+ return;
+ }
+
+ if (dixLookupWindow(&win, touch->win, clients[touch->client_id],
+ DixReadAccess) == Success)
+ FixUpEventFromWindow(device, xi2, win, None, TRUE);
+
+ TryClientEvents(clients[touch->client_id], device, xi2, 1,
+ 0, NoEventMask, NULL);
+
+ free(xi2);
+}
+
+static void
+ProcessTouchEndEvent(TouchStateEvent *ev, DeviceIntPtr device)
+{
+ TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+ xEvent *xi2;
+ int err;
+
+ if (!touch)
+ {
+ DebugF("[Xi] %s: Received TouchFini for dead touchpoint %d\n",
+ device->name, ev->touchid);
+ return;
+ }
+
+ /* If the touch is currently frozen, add it to the queue; if we can't
+ * enlarge the queue to fit TouchEnd, then just stomp a motion event. */
+ if (touch->frozen)
+ {
+ if (CheckTouchQueueSize(touch))
+ touch->num_frozen_events++;
+ touch->frozen_events[touch->num_frozen_events] = *(InternalEvent*)ev;
+ return;
+ }
+
+ err = EventToXI2((InternalEvent *) ev, &xi2);
+ if (err != Success)
+ {
+ ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchEnd (%d)\n",
+ device->name, err);
+ return;
+ }
+
+ TryClientEvents(clients[touch->client_id], device, xi2, 1,
+ 0, NoEventMask, NULL);
+ FinishTouchPoint(device, ev->touchid);
+}
+
+static void
+ProcessTouchQueue(TouchInfoPtr touch, DeviceIntPtr device)
+{
+ int j;
+
+ /* The first queued event is this TouchBegin, so skip it. */
+ for (j = 1; j < touch->num_frozen_events; j++)
+ {
+ if (touch->frozen_events[j].any.type == ET_TouchMotion)
+ ProcessTouchMotionEvent((TouchMotionEvent*)&touch->frozen_events[j],
+ device);
+ else if (touch->frozen_events[j].any.type == ET_TouchEnd)
+ ProcessTouchEndEvent((TouchStateEvent*)&touch->frozen_events[j],
+ device);
+ else
+ FatalError("[Xi] %s: Unknown event type %d in touch queue\n",
+ device->name, touch->frozen_events[j].any.type);
+ }
+ touch->num_frozen_events = 0;
+}
+
+static void
+ProcessTouchBeginEvent(TouchStateEvent *ev, DeviceIntPtr device)
+{
+ TouchInfoPtr touch = FindTouchPoint(device, ev->touchid);
+ WindowPtr win;
+ SpriteRec tsprite;
+ SpritePtr sprite;
+ ClientPtr client;
+ GrabRec tempGrab;
+ Window child = None;
+ xEvent *xi2;
+ int i = 0, err;
+
+ if (!touch)
+ {
+ DebugF("[Xi] %s: Received TouchBegin event for dead touchpoint %d\n",
+ device->name, ev->touchid);
+ return;
+ }
+
+ err = EventToXI2((InternalEvent *) ev, &xi2);
+ if (err != Success)
+ {
+ ErrorF("[Xi] %s: XI2 conversion failed in ProcessTouchBegin (%d)\n",
+ device->name, err);
+ return;
+ }
+
+ /* In Absolute focus_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->focus_mode == Absolute)
+ {
+ /* Fake sprite for XYToWindow. */
+ memset(&tsprite, 0, sizeof(tsprite));
+ sprite = &tsprite;
+ tsprite.spriteTraceSize = 10;
+ tsprite.spriteTrace = calloc(tsprite.spriteTraceSize,sizeof(WindowPtr));
+ if (!tsprite.spriteTrace)
+ goto out;
+ tsprite.spriteTrace[0] = RootWindow(device);
+ XYToWindow(sprite, ev->root_x, ev->root_y);
+ } else
+ {
+ sprite = device->spriteInfo->sprite;
+ }
+
+ tempGrab.type = GetXI2Type((InternalEvent *) ev);
+ tempGrab.grabtype = GRABTYPE_XI2;
+ tempGrab.device = device;
+ tempGrab.detail.exact = ev->tool;
+ tempGrab.detail.pMask = NULL;
+ tempGrab.modifiersDetail.pMask = NULL;
+ tempGrab.next = NULL;
+
+ /* If we've already got win, then it means we're replaying the touch
+ * stream, so find it in the trace and then restart delivery from the next
+ * child. */
+ if (touch->win != None)
+ {
+ for (; i < sprite->spriteTraceGood; i++)
+ {
+ if (sprite->spriteTrace[i]->drawable.id == touch->win)
+ {
+ i++;
+ break;
+ }
+ }
+ }
+ /* Now walk the window tree downwards looking for (more) grabs. */
+ for (; i < sprite->spriteTraceGood; i++)
+ {
+ GrabPtr grab;
+
+ win = sprite->spriteTrace[i];
+ for (grab = wPassiveGrabs(win); grab; grab = grab->next)
+ {
+ DeviceIntPtr gdev;
+ XkbSrvInfoPtr xkbi = NULL;
+
+ if (grab->grabtype != GRABTYPE_XI2 || grab->type != XI_TouchBegin)
+ continue;
+
+ if (!IsMaster(grab->device) && device->u.master)
+ gdev = GetMaster(device, MASTER_KEYBOARD);
+ else
+ gdev = grab->modifierDevice;
+ if (gdev && gdev->key)
+ xkbi = gdev->key->xkbInfo;
+
+ tempGrab.window = win;
+ tempGrab.modifierDevice = grab->modifierDevice;
+ tempGrab.modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0;
+
+ if (GrabMatchesSecond(&tempGrab, grab, FALSE))
+ {
+ client = rClient(grab);
+ FixUpEventFromWindow(device, xi2, grab->window, None, TRUE);
+ err = TryClientEvents(client, device, xi2, 1, 0, NoEventMask,
+ grab);
+ if (!err)
+ continue; /* Couldn't deliver, keep trying */
+
+ /* At this point, we've delivered our grabbed event; if it's
+ * a Sync grab, then freeze the device, else replay anything
+ * that might be left in the queue. */
+ touch->client_id = client->index;
+ touch->win = win->drawable.id;
+
+ if (grab->keyboardMode == GrabModeSync)
+ {
+ touch->frozen = 1;
+ touch->num_frozen_events = 1;
+ if (!CheckTouchQueueSize(touch))
+ FatalError("[Xi] %s: Couldn't allocate frozen touch events\n",
+ device->name);
+ touch->frozen_events[0] = *(InternalEvent*)ev;
+ } else
+ {
+ ProcessTouchQueue(touch, device);
+ }
+ goto out;
+ }
+ }
+ }
+
+ /* If we get this far, we've gone through all the grabs and are now
+ * attempting normal window delivery. */
+ win = sprite->spriteTrace[sprite->spriteTraceGood - 1];
+ while (win)
+ {
+ OtherInputMasks *masks = wOtherInputMasks(win);
+
+ if (masks && GetWindowXI2Mask(device, win, xi2)) {
+ InputClients *iclients = masks->inputClients;
+ Mask mask = GetEventMask(device, xi2, iclients);
+ Mask filter = GetEventFilter(device, xi2);
+
+ client = rClient(iclients);
+ FixUpEventFromWindow(device, xi2, win, child, FALSE);
+ if (XaceHook(XACE_RECEIVE_ACCESS, client, win, xi2, 1) == Success)
+ {
+ err = TryClientEvents(client, device, xi2, 1, mask, filter,
+ NULL);
+ if (err)
+ {
+ touch->client_id = client->index;
+ touch->win = win->drawable.id;
+ goto out;
+ }
+ }
+
+ }
+
+ child = win->drawable.id;
+ win = win->parent;
+ }
+
+out:
+ free(xi2);
+}
+
+void
+AllowTouches(ClientPtr client, TimeStamp time, DeviceIntPtr device,
+ int mode, uint32_t touchid)
+{
+ TouchInfoPtr touch = FindTouchPoint(device, touchid);
+
+ if (!touch)
+ {
+ DebugF("[Xi] %s: Received AllowEvents for dead touchpoint %d\n",
+ device->name, touchid);
+ return;
+ }
+
+ if (!touch->frozen || touch->num_frozen_events == 0)
+ return;
+
+ if (touch->frozen_events[0].any.type != ET_TouchBegin)
+ {
+ ErrorF("[Xi] %s: Corrupted touchstream queue, type %d at head\n",
+ device->name, touch->frozen_events[0].any.type);
+ return;
+ }
+
+ /* Either replay the TouchBegin down towards the next window, or flush the
+ * TouchMotion/TouchEnd queue out to the grabbing client, depending on the
+ * mode. */
+ touch->frozen = 0;
+ if (mode == XIReplayTouch)
+ ProcessTouchBeginEvent((TouchStateEvent *) &touch->frozen_events[0],
+ device);
+ else
+ ProcessTouchQueue(touch, device);
+}
+
/**
* Main device event processing function.
* Called from when processing the events from the event queue.
@@ -960,6 +1338,18 @@ ProcessOtherEvent(InternalEvent *ev, DeviceIntPtr device)
{
ProcessRawEvent(&ev->raw_event, device);
return;
+ } else if (ev->any.type == ET_TouchBegin)
+ {
+ ProcessTouchBeginEvent(&ev->touch_state_event, device);
+ return;
+ } else if (ev->any.type == ET_TouchEnd)
+ {
+ ProcessTouchEndEvent(&ev->touch_state_event, device);
+ return;
+ } else if (ev->any.type == ET_TouchMotion)
+ {
+ ProcessTouchMotionEvent(&ev->touch_motion_event, device);
+ return;
}
if (IsPointerDevice(device))
@@ -1563,6 +1953,39 @@ GrabWindow(ClientPtr client, DeviceIntPtr dev, int type,
return AddPassiveGrabToList(client, grab);
}
+/* TouchBegin grab */
+int
+GrabTouch(ClientPtr client, DeviceIntPtr dev, DeviceIntPtr mod_dev, int tool,
+ GrabParameters *param, GrabMask *mask)
+{
+ WindowPtr pWin;
+ GrabPtr grab;
+ Mask access_mode = DixGrabAccess;
+ int rc;
+
+ rc = CheckGrabValues(client, param);
+ if (rc != Success)
+ return rc;
+
+ rc = dixLookupWindow(&pWin, param->grabWindow, client, DixSetAttrAccess);
+ if (rc != Success)
+ return rc;
+ if (param->this_device_mode == GrabModeSync)
+ access_mode |= DixFreezeAccess;
+ rc = XaceHook(XACE_DEVICE_ACCESS, client, dev, access_mode);
+ if (rc != Success)
+ return rc;
+
+ /* FIXME: tool gets truncated to 8 bits here */
+ grab = CreateGrab(client->index, dev, mod_dev, pWin, GRABTYPE_XI2,
+ mask, param, XI_TouchBegin, tool, NULL, NULL);
+
+ if (!grab)
+ return BadAlloc;
+
+ return AddPassiveGrabToList(client, grab);
+}
+
int
SelectForWindow(DeviceIntPtr dev, WindowPtr pWin, ClientPtr client,
Mask mask, Mask exclusivemasks)
diff --git a/Xi/extinit.c b/Xi/extinit.c
index 7edadeaf2..556c06042 100644
--- a/Xi/extinit.c
+++ b/Xi/extinit.c
@@ -812,6 +812,62 @@ static void SXIPropertyEvent(xXIPropertyEvent *from, xXIPropertyEvent *to)
swapl(&to->property, n);
}
+static void SXITouchStateEvent(xXITouchStateEvent *from,
+ xXITouchStateEvent *to)
+{
+ char n;
+
+ *to = *from;
+ swaps(&to->sequenceNumber, n);
+ swapl(&to->length, n);
+ swaps(&to->evtype, n);
+ swaps(&to->deviceid, n);
+ swapl(&to->time, n);
+ swapl(&to->touchid, n);
+ swapl(&to->tool, n);
+ swaps(&to->sourceid, n);
+ swapl(&to->root, n);
+ swapl(&to->event, n);
+ swapl(&to->child, n);
+}
+
+static void SXITouchMotionEvent(xXITouchMotionEvent *from,
+ xXITouchMotionEvent *to)
+{
+ char n;
+
+ *to = *from;
+ swaps(&to->sequenceNumber, n);
+ swapl(&to->length, n);
+ swaps(&to->evtype, n);
+ swaps(&to->deviceid, n);
+ swapl(&to->time, n);
+ swaps(&to->sourceid, n);
+ swaps(&to->mask, n);
+ swapl(&to->touchid, n);
+ swapl(&to->root, n);
+ swapl(&to->event, n);
+ swapl(&to->child, n);
+ swapl(&to->root_x, n);
+ swapl(&to->root_y, n);
+ swapl(&to->event_x, n);
+ swapl(&to->event_y, n);
+ swapl(&to->x.integral, n);
+ swapl(&to->x.frac, n);
+ swapl(&to->y.integral, n);
+ swapl(&to->y.frac, n);
+ swapl(&to->touch_width_major.integral, n);
+ swapl(&to->touch_width_major.frac, n);
+ swapl(&to->touch_width_minor.integral, n);
+ swapl(&to->touch_width_minor.frac, n);
+ swapl(&to->tool_width_major.integral, n);
+ swapl(&to->tool_width_major.frac, n);
+ swapl(&to->tool_width_minor.integral, n);
+ swapl(&to->tool_width_minor.frac, n);
+ swaps(&to->orientation, n);
+ swaps(&to->flags, n);
+}
+
static void SRawEvent(xXIRawEvent *from, xXIRawEvent *to)
{
char n;
@@ -889,6 +945,15 @@ XI2EventSwap(xGenericEvent *from, xGenericEvent *to)
case XI_RawButtonRelease:
SRawEvent((xXIRawEvent*)from, (xXIRawEvent*)to);
break;
+ case XI_TouchBegin:
+ case XI_TouchEnd:
+ SXITouchStateEvent((xXITouchStateEvent*)from,
+ (xXITouchStateEvent*)to);
+ break;
+ case XI_TouchMotion:
+ SXITouchMotionEvent((xXITouchMotionEvent*)from,
+ (xXITouchMotionEvent*)to);
+ break;
default:
ErrorF("[Xi] Unknown event type to swap. This is a bug.\n");
break;
diff --git a/Xi/xiallowev.c b/Xi/xiallowev.c
index 3077e1a44..5de43ff72 100644
--- a/Xi/xiallowev.c
+++ b/Xi/xiallowev.c
@@ -44,6 +44,7 @@
int
SProcXIAllowEvents(ClientPtr client)
{
+ xXIAllowEventsDetailReq *req;
char n;
REQUEST(xXIAllowEventsReq);
@@ -52,6 +53,13 @@ SProcXIAllowEvents(ClientPtr client)
swaps(&stuff->deviceid, n);
swapl(&stuff->time, n);
+ /* See comment in ProcXIAllowEvents. */
+ if ((stuff->length << 2) == sizeof(xXIAllowEventsDetailReq))
+ {
+ req = (xXIAllowEventsDetailReq *) stuff;
+ swapl(&req->detail, n);
+ }
+
return ProcXIAllowEvents(client);
}
@@ -61,17 +69,31 @@ ProcXIAllowEvents(ClientPtr client)
TimeStamp time;
DeviceIntPtr dev;
int ret = Success;
+ xXIAllowEventsDetailReq req;
- REQUEST(xXIAllowEventsReq);
- REQUEST_SIZE_MATCH(xXIAllowEventsReq);
+ /* XI 2.1 adds a detail field to the request to support touch events,
+ * so we need to cater for both versions of the request. */
+ if ((client->req_len << 2) == sizeof(xXIAllowEventsDetailReq))
+ {
+ req = *(xXIAllowEventsDetailReq*)client->requestBuffer;
+ }
+ else if ((client->req_len << 2) == sizeof(xXIAllowEventsReq))
+ {
+ memcpy(&req, client->requestBuffer, sizeof(xXIAllowEventsReq));
+ req.detail = 0;
+ }
+ else
+ {
+ return BadLength;
+ }
- ret = dixLookupDevice(&dev, stuff->deviceid, client, DixGetAttrAccess);
+ ret = dixLookupDevice(&dev, req.deviceid, client, DixGetAttrAccess);
if (ret != Success)
return ret;
- time = ClientTimeToServerTime(stuff->time);
+ time = ClientTimeToServerTime(req.time);
- switch (stuff->mode) {
+ switch (req.mode) {
case XIReplayDevice:
AllowSome(client, time, dev, NOT_GRABBED);
break;
@@ -93,8 +115,12 @@ ProcXIAllowEvents(ClientPtr client)
if (IsMaster(dev))
AllowSome(client, time, dev, THAWED_BOTH);
break;
+ case XIAcceptTouch:
+ case XIReplayTouch:
+ AllowTouches(client, time, dev, req.mode, req.detail);
+ break;
default:
- client->errorValue = stuff->mode;
+ client->errorValue = req.mode;
ret = BadValue;
}
diff --git a/Xi/xipassivegrab.c b/Xi/xipassivegrab.c
index 296614510..5bafe539e 100644
--- a/Xi/xipassivegrab.c
+++ b/Xi/xipassivegrab.c
@@ -105,7 +105,8 @@ ProcXIPassiveGrabDevice(ClientPtr client)
if (stuff->grab_type != XIGrabtypeButton &&
stuff->grab_type != XIGrabtypeKeycode &&
stuff->grab_type != XIGrabtypeEnter &&
- stuff->grab_type != XIGrabtypeFocusIn)
+ stuff->grab_type != XIGrabtypeFocusIn &&
+ stuff->grab_type != XIGrabtypeTouchBegin)
{
client->errorValue = stuff->grab_type;
return BadValue;
@@ -185,6 +186,10 @@ ProcXIPassiveGrabDevice(ClientPtr client)
status = GrabWindow(client, dev, stuff->grab_type,
&param, &mask);
break;
+ case XIGrabtypeTouchBegin:
+ status = GrabTouch(client, dev, mod_dev, stuff->detail,
+ &param, &mask);
+ break;
}
if (status != GrabSuccess)
@@ -263,7 +268,8 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
if (stuff->grab_type != XIGrabtypeButton &&
stuff->grab_type != XIGrabtypeKeycode &&
stuff->grab_type != XIGrabtypeEnter &&
- stuff->grab_type != XIGrabtypeFocusIn)
+ stuff->grab_type != XIGrabtypeFocusIn &&
+ stuff->grab_type != XIGrabtypeTouchBegin)
{
client->errorValue = stuff->grab_type;
return BadValue;
@@ -294,6 +300,7 @@ ProcXIPassiveUngrabDevice(ClientPtr client)
case XIGrabtypeKeycode: tempGrab.type = XI_KeyPress; break;
case XIGrabtypeEnter: tempGrab.type = XI_Enter; break;
case XIGrabtypeFocusIn: tempGrab.type = XI_FocusIn; break;
+ case XIGrabtypeTouchBegin: tempGrab.type = XI_TouchBegin; break;
}
tempGrab.grabtype = GRABTYPE_XI2;
tempGrab.modifierDevice = mod_dev;
diff --git a/Xi/xiquerydevice.c b/Xi/xiquerydevice.c
index 303c8b27d..f913d2f5e 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(xXITouchClassInfo);
+ len += dev->touch->num_touches * sizeof(xXITouchInfo);
+ }
+
return len;
}
@@ -373,6 +379,114 @@ SwapValuatorInfo(DeviceIntPtr dev, xXIValuatorInfo* info)
swaps(&info->sourceid, n);
}
+/**
+ * List information for the given touchpoint.
+ *
+ * @return The number of bytes written into info.
+ */
+int
+ListTouchInfo(DeviceIntPtr dev, xXITouchClassInfo* info, int touchnum,
+ Bool reportState)
+{
+ TouchClassPtr t = dev->touch;
+ xXITouchInfo *tinfo;
+ int i;
+
+ info->type = XITouchClass;
+ info->length = sizeof(xXITouchClassInfo) >> 2;
+ info->length += (t->num_touches * sizeof(xXITouchInfo)) >> 2;
+ info->sourceid = dev->id;
+ info->mode = t->xy_mode;
+ info->min_x = t->min_x;
+ info->max_x = t->max_x;
+ info->min_y = t->min_y;
+ info->max_y = t->max_y;
+ info->min_touch_width = t->min_touch_width;
+ info->max_touch_width = t->max_touch_width;
+ info->max_touches = t->max_touches;
+ info->num_touches = t->num_touches;
+
+ tinfo = (xXITouchInfo *) &info[1];
+ for (i = 0; i < t->num_touches; i++)
+ {
+ tinfo->length = sizeof(xXITouchInfo) >> 2;
+ tinfo->touchid = t->touches[i].touchid;
+ tinfo->tool = t->touches[i].tool;
+
+ if (reportState)
+ {
+ tinfo->x = t->touches[i].last_x;
+ tinfo->y = t->touches[i].last_y;
+ tinfo->touch_major = t->touches[i].last_touch_major;
+ tinfo->touch_minor = t->touches[i].last_touch_minor;
+ tinfo->tool_major = t->touches[i].last_tool_major;
+ tinfo->tool_minor = t->touches[i].last_tool_minor;
+ tinfo->orientation = t->touches[i].last_orientation;
+ }
+ else {
+ tinfo->x = info->min_x;
+ tinfo->y = info->min_y;
+ tinfo->touch_major = info->min_touch_width;
+ tinfo->touch_minor = info->min_touch_width;
+ tinfo->tool_major = info->min_touch_width;
+ tinfo->tool_minor = info->min_touch_width;
+ tinfo->orientation = 0;
+ }
+
+ tinfo++;
+ }
+
+ return info->length << 2;
+}
+
+static void
+SwapTouchInfo(DeviceIntPtr dev, xXITouchClassInfo* info)
+{
+ xXITouchInfo *tinfo;
+ int num_touches = info->num_touches;
+ int i;
+ char n;
+
+ swaps(&info->type, n);
+ swaps(&info->length, n);
+ swaps(&info->sourceid, n);
+ swapl(&info->num_touches, n);
+ swapl(&info->max_touches, n);
+ swapl(&info->min_x.integral, n);
+ swapl(&info->min_x.frac, n);
+ swapl(&info->max_x.integral, n);
+ swapl(&info->max_x.frac, n);
+ swapl(&info->min_y.integral, n);
+ swapl(&info->min_y.frac, n);
+ swapl(&info->max_y.integral, n);
+ swapl(&info->max_y.frac, n);
+ swapl(&info->min_touch_width.integral, n);
+ swapl(&info->min_touch_width.frac, n);
+ swapl(&info->max_touch_width.integral, n);
+ swapl(&info->max_touch_width.frac, n);
+
+ tinfo = (xXITouchInfo *) &info[1];
+ for (i = 0; i < num_touches; i++) {
+ swaps(&tinfo->length, n);
+ swapl(&tinfo->touchid, n);
+ swapl(&tinfo->tool, n);
+ swapl(&tinfo->x.integral, n);
+ swapl(&tinfo->x.frac, n);
+ swapl(&tinfo->y.integral, n);
+ swapl(&tinfo->y.frac, n);
+ swapl(&tinfo->touch_major.integral, n);
+ swapl(&tinfo->touch_major.frac, n);
+ swapl(&tinfo->touch_minor.integral, n);
+ swapl(&tinfo->touch_minor.frac, n);
+ swapl(&tinfo->tool_major.integral, n);
+ swapl(&tinfo->tool_major.frac, n);
+ swapl(&tinfo->tool_minor.integral, n);
+ swapl(&tinfo->tool_minor.frac, n);
+ swaps(&tinfo->orientation, n);
+ tinfo++;
+ }
+}
+
int GetDeviceUse(DeviceIntPtr dev, uint16_t *attachment)
{
DeviceIntPtr master = dev->u.master;
@@ -462,6 +576,14 @@ ListDeviceClasses(ClientPtr client, DeviceIntPtr dev,
total_len += len;
}
+ if (dev->touch)
+ {
+ (*nclasses)++;
+ len = ListTouchInfo(dev, (xXITouchClassInfo*)any, i, rc == Success);
+ any += len;
+ total_len += len;
+ }
+
return total_len;
}
@@ -489,6 +611,9 @@ SwapDeviceInfo(DeviceIntPtr dev, xXIDeviceInfo* info)
case XIValuatorClass:
SwapValuatorInfo(dev, (xXIValuatorInfo*)any);
break;
+ case XITouchClass:
+ SwapTouchInfo(dev, (xXITouchClassInfo*)any);
+ break;
}
any += len * 4;
diff --git a/Xi/xiquerydevice.h b/Xi/xiquerydevice.h
index 02f06591e..9700ca74f 100644
--- a/Xi/xiquerydevice.h
+++ b/Xi/xiquerydevice.h
@@ -44,4 +44,6 @@ 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, xXITouchClassInfo* info,
+ int axisnumber, Bool reportState);
#endif /* QUERYDEV_H */
diff --git a/Xi/xiselectev.c b/Xi/xiselectev.c
index 7aa3f0ab6..e64b898d2 100644
--- a/Xi/xiselectev.c
+++ b/Xi/xiselectev.c
@@ -141,6 +141,27 @@ ProcXISelectEvents(ClientPtr client)
return BadValue;
}
+ /* Only one client per window may select for touch begin events;
+ * touch motion and end events may not be selected for. */
+ if (evmask->mask_len >= 1)
+ {
+ unsigned char *bits = (unsigned char*)&evmask[1];
+ if (BitIsOn(bits, XI_TouchBegin))
+ {
+ OtherInputMasks *inputMasks = wOtherInputMasks(win);
+ if (inputMasks &&
+ (BitIsOn(inputMasks->xi2mask[evmask->deviceid],
+ XI_TouchBegin) ||
+ BitIsOn(inputMasks->xi2mask[XIAllDevices],
+ XI_TouchBegin) ||
+ BitIsOn(inputMasks->xi2mask[XIAllMasterDevices],
+ XI_TouchBegin)))
+ return BadValue;
+ }
+ if (BitIsOn(bits, XI_TouchMotion) || BitIsOn(bits, XI_TouchEnd))
+ return BadValue;
+ }
+
if (XICheckInvalidMaskBits((unsigned char*)&evmask[1],
evmask->mask_len * 4) != Success)
return BadValue;