diff options
author | Daniel Stone <daniel@fooishbar.org> | 2010-09-20 15:03:06 +1000 |
---|---|---|
committer | Chase Douglas <chase.douglas@ubuntu.com> | 2010-10-13 21:42:27 +0200 |
commit | a6cdd84853df31b8d2beffcbf7fbc5e51dbd74b5 (patch) | |
tree | 4147c8c3b56826ca841aa3f780bdf31e80ae3838 | |
parent | f7d671b89a6c4e00f1333960b56d18f69548628e (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>
-rw-r--r-- | Xext/geext.c | 2 | ||||
-rw-r--r-- | Xext/security.c | 2 | ||||
-rw-r--r-- | Xi/exevents.c | 423 | ||||
-rw-r--r-- | Xi/extinit.c | 65 | ||||
-rw-r--r-- | Xi/xiallowev.c | 38 | ||||
-rw-r--r-- | Xi/xipassivegrab.c | 11 | ||||
-rw-r--r-- | Xi/xiquerydevice.c | 125 | ||||
-rw-r--r-- | Xi/xiquerydevice.h | 2 | ||||
-rw-r--r-- | Xi/xiselectev.c | 21 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | dix/devices.c | 56 | ||||
-rw-r--r-- | dix/eventconvert.c | 67 | ||||
-rw-r--r-- | dix/events.c | 42 | ||||
-rw-r--r-- | dix/getevents.c | 154 | ||||
-rw-r--r-- | dix/inpututils.c | 65 | ||||
-rw-r--r-- | hw/xfree86/common/xf86Xinput.c | 26 | ||||
-rw-r--r-- | hw/xfree86/common/xf86Xinput.h | 14 | ||||
-rw-r--r-- | include/dix.h | 7 | ||||
-rw-r--r-- | include/events.h | 2 | ||||
-rw-r--r-- | include/eventstr.h | 42 | ||||
-rw-r--r-- | include/exevents.h | 9 | ||||
-rw-r--r-- | include/input.h | 40 | ||||
-rw-r--r-- | include/inputstr.h | 39 | ||||
-rw-r--r-- | include/protocol-versions.h | 2 | ||||
-rw-r--r-- | mi/mieq.c | 10 |
25 files changed, 1239 insertions, 27 deletions
diff --git a/Xext/geext.c b/Xext/geext.c index 8319c9291..b37c1a0bd 100644 --- a/Xext/geext.c +++ b/Xext/geext.c @@ -33,8 +33,6 @@ #include "geext.h" #include "protocol-versions.h" -#define rClient(obj) (clients[CLIENT_ID((obj)->resource)]) - DevPrivateKeyRec GEClientPrivateKeyRec; int RT_GECLIENT = 0; diff --git a/Xext/security.c b/Xext/security.c index 7eb95de74..8673880d1 100644 --- a/Xext/security.c +++ b/Xext/security.c @@ -154,8 +154,6 @@ SecurityLookupRequestName(ClientPtr client) } -#define rClient(obj) (clients[CLIENT_ID((obj)->resource)]) - /* SecurityDeleteAuthorization * * Arguments: 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; diff --git a/configure.ac b/configure.ac index c0bf98f30..5f6befeda 100644 --- a/configure.ac +++ b/configure.ac @@ -790,7 +790,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 7a7bbdf79..f102aff06 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -752,6 +752,12 @@ FreeDeviceClass(int type, pointer *class) free((*v)); break; } + case XITouchClass: + { + TouchClassPtr *t = (TouchClassPtr*)class; + free((*t)); + break; + } case FocusClass: { FocusClassPtr *f = (FocusClassPtr*)class; @@ -860,6 +866,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); @@ -1550,6 +1557,55 @@ 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. + * @min_x The minimum value for the x axis. + * @max_x The maximum value for the x axis. + * @min_y The minimum value for the y axis. + * @max_y The maximum value for the y axis. + * @min_touch_width The minimum value for touch/tool width. + * @max_touch_width The maximum value for touch/tool width. + */ +Bool +InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches, + unsigned int xy_mode, unsigned int focus_mode, + double min_x, double max_x, + double min_y, double max_y, + double min_touch_width, double max_touch_width) +{ + if (device->touch) + return FALSE; + + if (xy_mode != Absolute && xy_mode != Relative) + return FALSE; + if (focus_mode != Absolute && focus_mode != Relative) + return FALSE; + if (max_touches == 0) + return FALSE; + if (max_x < min_x || max_y < min_y || max_touch_width < min_touch_width) + return FALSE; + + device->touch = calloc(1, sizeof(*device->touch) + + max_touches * sizeof(*device->touch->touches)); + if (!device->touch) + return FALSE; + device->touch->touches = (TouchInfoPtr) &device->touch[1]; + device->touch->max_touches = max_touches; + device->touch->xy_mode = xy_mode; + device->touch->focus_mode = focus_mode; + /* FIXME: don't throw away float */ + device->touch->min_x.integral = min_x; + device->touch->max_x.integral = max_x; + device->touch->min_y.integral = min_y; + device->touch->max_y.integral = max_y; + device->touch->min_touch_width.integral = min_touch_width; + device->touch->max_touch_width.integral = max_touch_width; + + 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 0f747c1a0..5461ad737 100644 --- a/dix/eventconvert.c +++ b/dix/eventconvert.c @@ -55,6 +55,8 @@ static int eventToKeyButtonPointer(DeviceEvent *ev, xEvent **xi, int *count); static int eventToDeviceChanged(DeviceChangedEvent *ev, xEvent **dcce); static int eventToDeviceEvent(DeviceEvent *ev, xEvent **xi); static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi); +static int eventToTouchStateEvent(TouchStateEvent *ev, xEvent **xi); +static int eventToTouchMotionEvent(TouchMotionEvent *ev, xEvent **xi); /* Do not use, read comments below */ BOOL EventIsKeyRepeat(xEvent *event); @@ -139,6 +141,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: */ @@ -184,6 +189,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; @@ -238,6 +246,11 @@ EventToXI2(InternalEvent *ev, xEvent **xi) case ET_RawButtonRelease: case ET_RawMotion: return eventToRawEvent(&ev->raw_event, xi); + case ET_TouchBegin: + case ET_TouchEnd: + return eventToTouchStateEvent(&ev->touch_state_event, xi); + case ET_TouchMotion: + return eventToTouchMotionEvent(&ev->touch_motion_event, xi); default: break; } @@ -650,6 +663,57 @@ eventToRawEvent(RawDeviceEvent *ev, xEvent **xi) return Success; } +static int +eventToTouchStateEvent(TouchStateEvent *ev, xEvent **xi) +{ + xXITouchStateEvent* state; + int len = sizeof(xXITouchStateEvent); + + *xi = calloc(1, len); + state = (xXITouchStateEvent*)*xi; + state->type = GenericEvent; + state->extension = IReqCode; + state->length = bytes_to_int32(len - sizeof(xEvent)); + state->evtype = GetXI2Type((InternalEvent*)ev); + state->deviceid = ev->deviceid; + state->time = ev->time; + state->sourceid = ev->sourceid; + state->touchid = ev->touchid; + state->tool = ev->tool; + + return Success; +} + +static int +eventToTouchMotionEvent(TouchMotionEvent *ev, xEvent **xi) +{ + xXITouchMotionEvent* motion; + int len = sizeof(xXITouchMotionEvent); + + *xi = calloc(1, len); + motion = (xXITouchMotionEvent*)*xi; + motion->type = GenericEvent; + motion->extension = IReqCode; + motion->length = bytes_to_int32(len - sizeof(xEvent)); + motion->evtype = GetXI2Type((InternalEvent*)ev); + motion->deviceid = ev->deviceid; + motion->time = ev->time; + motion->sourceid = ev->sourceid; + motion->touchid = ev->touchid; + motion->mask = ev->mask; + motion->root_x = ev->root_x; + motion->root_y = ev->root_y; + motion->x = ev->x; + motion->y = ev->y; + motion->touch_width_major = ev->touch_width_minor; + motion->touch_width_minor = ev->touch_width_minor; + motion->tool_width_major = ev->tool_width_major; + motion->tool_width_minor = ev->tool_width_minor; + motion->orientation = ev->orientation; + + return Success; +} + /** * Return the corresponding core type for the given event or 0 if no core * equivalent exists. @@ -721,6 +785,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 987abc203..34af7aeb6 100644 --- a/dix/events.c +++ b/dix/events.c @@ -195,8 +195,6 @@ typedef const char *string; #define XE_KBPTR (xE->u.keyButtonPointer) -#define rClient(obj) (clients[CLIENT_ID((obj)->resource)]) - CallbackListPtr EventCallback; CallbackListPtr DeviceEventCallback; @@ -334,12 +332,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 +439,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 */ @@ -2247,6 +2239,32 @@ FixUpEventFromWindow( event->evtype == XI_PropertyEvent) return; + if (event->evtype == XI_TouchBegin || event->evtype == XI_TouchEnd) + { + xXITouchStateEvent* state = (xXITouchStateEvent*)xE; + state->root = pSprite->spriteTrace[0]->drawable.id; + state->event = pWin->drawable.id; + state->child = child; + return; + } else if (event->evtype == XI_TouchMotion) + { + xXITouchMotionEvent* motion = (xXITouchMotionEvent*)xE; + motion->root = pSprite->spriteTrace[0]->drawable.id; + motion->event = pWin->drawable.id; + if (pSprite->hot.pScreen == pWin->drawable.pScreen) + { + motion->event_x = motion->root_x - FP1616(pWin->drawable.x, 0); + motion->event_y = motion->root_y - FP1616(pWin->drawable.y, 0); + motion->child = child; + } else + { + motion->event_x = 0; + motion->event_y = 0; + motion->child = None; + } + return; + } + event->root = pSprite->spriteTrace[0]->drawable.id; event->event = pWin->drawable.id; if (pSprite->hot.pScreen == pWin->drawable.pScreen) @@ -2567,7 +2585,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; @@ -3433,9 +3451,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 552debb37..4deecdd49 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -46,6 +46,7 @@ #include "mipointer.h" #include "eventstr.h" #include "eventconvert.h" +#include "windowstr.h" #include <X11/extensions/XKBproto.h> #include "xkbsrv.h" @@ -1251,6 +1252,159 @@ GetProximityEvents(EventList *events, DeviceIntPtr pDev, int type, } /** + * Get events for a touch; always generates a TouchMotion event, and may + * generate a TouchBegin if this is the first touch of the sequence. + * + * 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, uint32_t touchid, + uint32_t tool, uint16_t mask, double x, double y, + double touch_width_major, double touch_width_minor, + double tool_width_major, double tool_width_minor, + unsigned int orientation) +{ + int num_events = 0; + uint32_t time = GetTimeInMillis(); + int16_t rx, ry; + float rx_frac = 0.0, ry_frac = 0.0; + TouchMotionEvent *motion; + TouchStateEvent *state; + TouchInfoPtr touch = FindTouchPoint(pDev, touchid); + ScreenPtr screen = pDev->spriteInfo->sprite->hotPhys.pScreen; + AxisInfo axis; + + /* refuse events from disabled devices */ + if (!pDev->enabled) + return 0; + + /* Sanity checks. */ + if (!pDev->touch) + return 0; + if ((mask & XITouchOrientationMask) && orientation >= 360) + return 0; + + /* 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 (pDev->touch->focus_mode == Absolute) { + if (mask & XITouchXMask) { + axis.min_value = pDev->touch->min_x.integral; + axis.max_value = pDev->touch->max_x.integral; + rx = rescaleValuatorAxis(x, 0.0, &rx_frac, &axis, NULL, + screen->width); + } + if (mask & XITouchYMask) { + axis.min_value = pDev->touch->min_y.integral; + axis.max_value = pDev->touch->max_y.integral; + ry = rescaleValuatorAxis(y, 0.0, &ry_frac, &axis, NULL, + screen->height); + } + } + else { + if (mask & XITouchXMask) + rx = pDev->spriteInfo->sprite->hotPhys.x; + if (mask & XITouchYMask) + ry = pDev->spriteInfo->sprite->hotPhys.y; + } + + if (!touch) { + /* We need at least x and y co-ordinates to start a touch, to know + * where to deliver the TouchBegin. */ + if (!(mask & XITouchXMask) || !(mask & XITouchYMask)) + return 0; + + touch = CreateTouchPoint(pDev, touchid, tool); + if (!touch) + return 0; + + state = (TouchStateEvent *) events->event; + events++; + num_events++; + memset(state, 0, sizeof(TouchStateEvent)); + state->header = ET_Internal; + state->type = ET_TouchBegin; + state->length = sizeof(TouchStateEvent); + state->time = time; + state->deviceid = pDev->id; + state->sourceid = pDev->id; + state->touchid = touchid; + state->tool = tool; + state->root = screen->root->drawable.id; + state->root_x = rx; + state->root_y = ry; + } + + motion = (TouchMotionEvent *) events->event; + num_events++; + memset(motion, 0, sizeof(TouchMotionEvent)); + motion->header = ET_Internal; + motion->type = ET_TouchMotion; + motion->length = sizeof(TouchMotionEvent); + motion->time = time; + motion->deviceid = pDev->id; + motion->sourceid = pDev->id; + motion->root = screen->root->drawable.id; + motion->touchid = touchid; + motion->mask = mask; + + /* FIXME: don't throw away float */ + if (mask & XITouchXMask) { + motion->x.integral = x; + motion->root_x = FP1616(rx, rx_frac); + } + if (mask & XITouchYMask) { + motion->y.integral = y; + motion->root_y = FP1616(ry, ry_frac); + } + if (mask & XITouchTouchSizeMask) { + motion->touch_width_major.integral = touch_width_major; + motion->touch_width_minor.integral = touch_width_minor; + } + if (mask & XITouchToolSizeMask) { + motion->tool_width_major.integral = tool_width_major; + motion->tool_width_minor.integral = tool_width_minor; + } + if (mask & XITouchOrientationMask) { + motion->orientation = orientation; + } + + return num_events; +} + +int +GetTouchFinishEvents(EventList *events, DeviceIntPtr pDev, uint32_t touchid) +{ + TouchStateEvent *event; + TouchInfoPtr touch = FindTouchPoint(pDev, touchid); + + /* refuse events from disabled devices */ + if (!pDev->enabled) + return 0; + + if (!touch) { + DebugF("[Xi] %s: Got touch finish event for dead touchpoint %d\n", + pDev->name, touchid); + return 0; + } + + event = (TouchStateEvent *) events->event; + memset(event, 0, sizeof(TouchStateEvent)); + event->header = ET_Internal; + event->type = ET_TouchEnd; + event->length = sizeof(TouchStateEvent); + event->time = GetTimeInMillis(); + event->deviceid = pDev->id; + event->sourceid = pDev->id; + event->touchid = touchid; + event->tool = touch->tool; + + 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 5037f4c54..8b264fe7c 100644 --- a/dix/inpututils.c +++ b/dix/inpututils.c @@ -451,3 +451,68 @@ CountBits(uint8_t *mask, int len) return ret; } + +TouchInfoPtr +FindTouchPoint(DeviceIntPtr dev, uint32_t touchid) +{ + int i; + + if (!dev->touch) + return NULL; + + for (i = 0; i < dev->touch->max_touches; i++) + if (dev->touch->touches[i].touchid == touchid) + return &dev->touch->touches[i]; + + return NULL; +} + +TouchInfoPtr +CreateTouchPoint(DeviceIntPtr dev, uint32_t touchid, uint32_t tool) +{ + int i; + + if (!dev->touch || dev->touch->num_touches == dev->touch->max_touches) + return NULL; + + if (FindTouchPoint(dev, touchid)) + return NULL; + + for (i = 0; i < dev->touch->max_touches; i++) + { + if (!dev->touch->touches[i].touchid) + { + dev->touch->num_touches++; + memset(&dev->touch->touches[i], 0, sizeof(TouchInfoRec)); + dev->touch->touches[i].touchid = touchid; + dev->touch->touches[i].tool = tool; + return &dev->touch->touches[i]; + } + } + + return NULL; +} + +void +FinishTouchPoint(DeviceIntPtr dev, uint32_t touchid) +{ + TouchInfoPtr touch = FindTouchPoint(dev, touchid); + + if (!touch) + return; + + touch->touchid = 0; + touch->client_id = 0; + touch->frozen = 0; + touch->num_frozen_events = 0; + touch->win = None; + touch->tool = 0; + touch->last_x = dev->touch->min_x; + touch->last_y = dev->touch->min_y; + touch->last_touch_major = dev->touch->min_touch_width; + touch->last_touch_minor = dev->touch->min_touch_width; + touch->last_tool_major = dev->touch->min_touch_width; + touch->last_tool_minor = dev->touch->min_touch_width; + touch->last_orientation = 0; + dev->touch->num_touches--; +} diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c index 8fe56f1b5..004ba1247 100644 --- a/hw/xfree86/common/xf86Xinput.c +++ b/hw/xfree86/common/xf86Xinput.c @@ -1414,4 +1414,30 @@ xf86EnableDevice(DeviceIntPtr dev) EnableDevice(dev, TRUE); } +void +xf86PostTouchMotion(DeviceIntPtr dev, uint32_t touchid, uint32_t tool, + uint16_t mask, double x, double y, + double touch_major, double touch_minor, + double tool_major, double tool_minor, + unsigned int orientation) +{ + int i, nevents; + + nevents = GetTouchEvents(xf86Events, dev, touchid, tool, mask, x, y, + touch_major, touch_minor, tool_major, tool_minor, + orientation); + for (i = 0; i < nevents; i++) + mieqEnqueue(dev, (InternalEvent *)((xf86Events + i)->event)); +} + +void +xf86FiniTouchPoint(DeviceIntPtr dev, uint32_t touchid) +{ + int i, nevents; + + nevents = GetTouchFinishEvents(xf86Events, dev, touchid); + 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 732f7f8a9..73c566036 100644 --- a/hw/xfree86/common/xf86Xinput.h +++ b/hw/xfree86/common/xf86Xinput.h @@ -206,6 +206,20 @@ extern _X_EXPORT void xf86EnableDevice(DeviceIntPtr dev); /* not exported */ int xf86NewInputDevice(IDevPtr idev, DeviceIntPtr *pdev, BOOL is_auto); +/* Multitouch API. */ +extern _X_EXPORT void xf86PostTouchMotion(DeviceIntPtr dev, + uint32_t touchid, + uint32_t tool, + uint16_t mask, + double x, + double y, + double touch_major, + double touch_minor, + double width_major, + double width_minor, + unsigned int orientation); +extern _X_EXPORT void xf86FiniTouchPoint(DeviceIntPtr dev, uint32_t touchid); + /* xf86Helper.c */ extern _X_EXPORT void xf86AddInputDriver(InputDriverPtr driver, pointer module, int flags); extern _X_EXPORT void xf86DeleteInputDriver(int drvIndex); diff --git a/include/dix.h b/include/dix.h index 7485e8ed4..dba8af3ca 100644 --- a/include/dix.h +++ b/include/dix.h @@ -369,6 +369,13 @@ extern void AllowSome( DeviceIntPtr /* thisDev */, int /* newState */); +extern void AllowTouches( + ClientPtr /* client */, + TimeStamp /* time */, + DeviceIntPtr /* thisDev */, + int /* mode */, + uint32_t /* touchid */); + extern void ReleaseActiveGrabs( ClientPtr client); diff --git a/include/events.h b/include/events.h index 375173adc..9ceceda24 100644 --- a/include/events.h +++ b/include/events.h @@ -26,6 +26,8 @@ #define EVENTS_H typedef struct _DeviceEvent DeviceEvent; typedef struct _DeviceChangedEvent DeviceChangedEvent; +typedef struct _TouchStateEvent TouchStateEvent; +typedef struct _TouchMotionEvent TouchMotionEvent; #if XFreeXDGA typedef struct _DGAEvent DGAEvent; #endif diff --git a/include/eventstr.h b/include/eventstr.h index 433227e6e..da11d6a44 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 */ }; @@ -209,6 +212,43 @@ struct _RawDeviceEvent } valuators; }; +struct _TouchStateEvent +{ + unsigned char header; /**< Always ET_Internal */ + enum EventType type; /**< ET_TouchBegin or ET_TouchEnd */ + int length; /**< Length in bytes */ + Time time; /**< Time in ms */ + int deviceid; /**< Device to post this event for */ + int sourceid; /**< The physical source device */ + uint32_t touchid; /**< Unique identifier for this touch */ + uint32_t tool; /**< Identifier of the physical tool */ + Window root; /**< Root window for events */ + FP1616 root_x; /**< x in screen co-ordinates */ + FP1616 root_y; /**< y in screen co-ordinates */ +}; + +struct _TouchMotionEvent +{ + unsigned char header; /**< Always ET_Internal */ + enum EventType type; /**< ET_TouchMotion */ + int length; /**< Length in bytes */ + Time time; /**< Time in ms */ + int deviceid; /**< Device to post this event for */ + int sourceid; /**< The physical source device */ + uint32_t touchid; /**< Unique identifier for this touch */ + uint16_t mask; /**< Components present in this event */ + Window root; /**< Root window event is relative to */ + FP1616 root_x; /**< x in screen co-ordinates */ + FP1616 root_y; /**< y in screen co-ordinates */ + FP3232 x; + FP3232 y; + FP3232 touch_width_major; + FP3232 touch_width_minor; + FP3232 tool_width_major; + FP3232 tool_width_minor; + uint16_t orientation; +}; + #ifdef XQUARTZ #define XQUARTZ_EVENT_MAXARGS 5 struct _XQuartzEvent { @@ -234,6 +274,8 @@ union _InternalEvent { } any; DeviceEvent device_event; DeviceChangedEvent changed_event; + TouchStateEvent touch_state_event; + TouchMotionEvent touch_motion_event; #if XFreeXDGA DGAEvent dga_event; #endif diff --git a/include/exevents.h b/include/exevents.h index 39e1c70fe..48212ad67 100644 --- a/include/exevents.h +++ b/include/exevents.h @@ -202,6 +202,15 @@ GrabWindow( GrabMask* /* eventMask */); extern int +GrabTouch( + ClientPtr /* client */, + DeviceIntPtr /* dev */, + DeviceIntPtr /* mod_dev */, + int /* tool */, + GrabParameters* /* param */, + GrabMask* /* eventMask */); + +extern int SelectForWindow( DeviceIntPtr /* dev */, WindowPtr /* pWin */, diff --git a/include/input.h b/include/input.h index dbdc863c4..fa9b7c38c 100644 --- a/include/input.h +++ b/include/input.h @@ -105,6 +105,9 @@ typedef struct _DeviceIntRec *DeviceIntPtr; typedef struct _ClassesRec *ClassesPtr; typedef struct _SpriteRec *SpritePtr; typedef union _GrabMask GrabMask; +typedef struct _TouchInfoRec *TouchInfoPtr; + +#define rClient(obj) (clients[CLIENT_ID((obj)->resource)]) typedef struct _EventList { xEvent* event; @@ -323,6 +326,18 @@ 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 /*xy_mode*/, + unsigned int /*focus_mode*/, + double /*min_x*/, + double /*max_x*/, + double /*min_y*/, + double /*max_y*/, + double /*min_touch_width*/, + double /*max_touch_width*/); + typedef void (*BellProcPtr)( int /*percent*/, DeviceIntPtr /*device*/, @@ -474,6 +489,25 @@ extern int GetKeyboardValuatorEvents( ValuatorMask *mask, int *valuators); +extern int GetTouchEvents( + EventListPtr events, + DeviceIntPtr pDev, + uint32_t touchid, + uint32_t tool, + uint16_t mask, + double x, + double y, + double touch_width_major, + double touch_width_minor, + double tool_width_major, + double tool_width_minor, + unsigned int orientation); + +extern int GetTouchFinishEvents( + EventListPtr events, + DeviceIntPtr pDev, + uint32_t touchid); + extern int GetProximityEvents( EventListPtr events, DeviceIntPtr pDev, @@ -536,8 +570,13 @@ 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 TouchInfoPtr CreateTouchPoint(DeviceIntPtr dev, uint32_t touchid, + uint32_t tool); +extern TouchInfoPtr FindTouchPoint(DeviceIntPtr dev, uint32_t touchid); +extern void FinishTouchPoint(DeviceIntPtr dev, uint32_t 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, @@ -545,6 +584,7 @@ void FixUpEventFromWindow(SpritePtr pSprite, WindowPtr pWin, Window child, Bool calcChild); +extern WindowPtr XYToWindow(SpritePtr pSprite, int x, int y); /* Implemented by the DDX. */ extern _X_EXPORT int NewInputDeviceRequest( diff --git a/include/inputstr.h b/include/inputstr.h index e1cf06ad5..544f3cd2d 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" @@ -70,7 +72,7 @@ SOFTWARE. * 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 */ /** @@ -242,6 +244,39 @@ typedef struct _ValuatorClassRec { ValuatorAccelerationRec accelScheme; } ValuatorClassRec, *ValuatorClassPtr; +typedef struct _TouchInfoRec { + uint32_t touchid; /* 0 if currently unused */ + int sourceid; + int client_id; + Window win; + int frozen; + int size_frozen_events; + int num_frozen_events; + InternalEvent *frozen_events; + uint32_t tool; + FP3232 last_x; + FP3232 last_y; + FP3232 last_touch_major; + FP3232 last_touch_minor; + FP3232 last_tool_major; + FP3232 last_tool_minor; + uint16_t last_orientation; +} TouchInfoRec; + +typedef struct _TouchClassRec { + FP3232 min_x; + FP3232 max_x; + FP3232 min_y; + FP3232 max_y; + FP3232 min_touch_width; + FP3232 max_touch_width; + unsigned int xy_mode; + unsigned int focus_mode; + unsigned int max_touches; + unsigned int num_touches; + TouchInfoPtr touches; +} TouchClassRec, *TouchClassPtr; + typedef struct _ButtonClassRec { int sourceid; CARD8 numButtons; @@ -346,6 +381,7 @@ typedef struct _LedFeedbackClassRec { typedef struct _ClassesRec { KeyClassPtr key; ValuatorClassPtr valuator; + TouchClassPtr touch; ButtonClassPtr button; FocusClassPtr focus; ProximityClassPtr proximity; @@ -511,6 +547,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 c67446548..f21d63298 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 @@ -282,6 +282,13 @@ ChangeDeviceID(DeviceIntPtr dev, InternalEvent* event) case ET_RawMotion: event->raw_event.deviceid = dev->id; break; + case ET_TouchBegin: + case ET_TouchEnd: + event->touch_state_event.deviceid = dev->id; + break; + case ET_TouchMotion: + event->touch_motion_event.deviceid = dev->id; + break; default: ErrorF("[mi] Unknown event type (%d), cannot change id.\n", event->any.type); @@ -388,6 +395,9 @@ mieqProcessDeviceEvent(DeviceIntPtr dev, NewCurrentScreen (dev, DequeueScreen(dev), x, y); } break; + case ET_TouchMotion: + /* XXX FIXME: Add screen-crossing support here */ + break; default: break; } |