From a6cdd84853df31b8d2beffcbf7fbc5e51dbd74b5 Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Mon, 20 Sep 2010 15:03:06 +1000 Subject: 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 --- Xi/exevents.c | 423 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Xi/extinit.c | 65 ++++++++ Xi/xiallowev.c | 38 ++++- Xi/xipassivegrab.c | 11 +- Xi/xiquerydevice.c | 125 ++++++++++++++++ Xi/xiquerydevice.h | 2 + Xi/xiselectev.c | 21 +++ 7 files changed, 677 insertions(+), 8 deletions(-) (limited to 'Xi') 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, ¶m, &mask); break; + case XIGrabtypeTouchBegin: + status = GrabTouch(client, dev, mod_dev, stuff->detail, + ¶m, &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; -- cgit v1.2.3