diff options
Diffstat (limited to 'xorg-server/dix')
-rw-r--r-- | xorg-server/dix/Makefile.am | 1 | ||||
-rw-r--r-- | xorg-server/dix/devices.c | 87 | ||||
-rw-r--r-- | xorg-server/dix/eventconvert.c | 63 | ||||
-rw-r--r-- | xorg-server/dix/events.c | 69 | ||||
-rw-r--r-- | xorg-server/dix/getevents.c | 352 | ||||
-rw-r--r-- | xorg-server/dix/grabs.c | 3 | ||||
-rw-r--r-- | xorg-server/dix/inpututils.c | 7 | ||||
-rw-r--r-- | xorg-server/dix/touch.c | 982 | ||||
-rw-r--r-- | xorg-server/dix/window.c | 9 |
9 files changed, 1535 insertions, 38 deletions
diff --git a/xorg-server/dix/Makefile.am b/xorg-server/dix/Makefile.am index f5af619e3..b7358aa72 100644 --- a/xorg-server/dix/Makefile.am +++ b/xorg-server/dix/Makefile.am @@ -39,6 +39,7 @@ libdix_la_SOURCES = \ swaprep.c \ swapreq.c \ tables.c \ + touch.c \ window.c EXTRA_DIST = buildatoms BuiltInAtoms Xserver.d Xserver-dtrace.h.in diff --git a/xorg-server/dix/devices.c b/xorg-server/dix/devices.c index e79ea5996..f01857e77 100644 --- a/xorg-server/dix/devices.c +++ b/xorg-server/dix/devices.c @@ -267,6 +267,7 @@ AddInputDevice(ClientPtr client, DeviceProc deviceProc, Bool autoStart) return (DeviceIntPtr)NULL; dev->last.scroll = NULL; + dev->last.touches = NULL; dev->id = devid; dev->public.processInputProc = ProcessOtherEvent; dev->public.realInputProc = ProcessOtherEvent; @@ -765,6 +766,21 @@ FreeDeviceClass(int type, pointer *class) free((*v)); break; } + case XITouchClass: + { + TouchClassPtr *t = (TouchClassPtr*)class; + int i; + + for (i = 0; i < (*t)->num_touches; i++) + { + free((*t)->touches[i].sprite.spriteTrace); + free((*t)->touches[i].listeners); + free((*t)->touches[i].valuators); + } + + free((*t)); + break; + } case FocusClass: { FocusClassPtr *f = (FocusClassPtr*)class; @@ -873,6 +889,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); @@ -952,6 +969,9 @@ CloseDevice(DeviceIntPtr dev) free(dev->deviceGrab.sync.event); free(dev->config_info); /* Allocated in xf86ActivateDevice. */ free(dev->last.scroll); + for (j = 0; j < dev->last.num_touches; j++) + free(dev->last.touches[j].valuators); + free(dev->last.touches); dev->config_info = NULL; dixFreeObjectWithPrivates(dev, PRIVATE_DEVICE); } @@ -1427,7 +1447,6 @@ InitPtrFeedbackClassDeviceStruct(DeviceIntPtr dev, PtrCtrlProcPtr controlProc) return TRUE; } - static LedCtrl defaultLedControl = { DEFAULT_LEDS, DEFAULT_LEDS_MASK, 0}; @@ -1550,6 +1569,72 @@ InitPointerDeviceStruct(DevicePtr device, CARD8 *map, int numButtons, Atom* btn_ InitPtrFeedbackClassDeviceStruct(dev, controlProc)); } +/** + * Sets up multitouch capabilities on @device. + * + * @max_touches The maximum number of simultaneous touches, or 0 for unlimited. + * @mode The mode of the touch device (XIDirectTouch or XIDependentTouch). + * @num_axes The number of touch valuator axes. + */ +Bool +InitTouchClassDeviceStruct(DeviceIntPtr device, unsigned int max_touches, + unsigned int mode, unsigned int num_axes) +{ + TouchClassPtr touch; + int i; + + if (device->touch || !device->valuator) + return FALSE; + + /* Check the mode is valid, and at least X and Y axes. */ + if (mode != XIDirectTouch && mode != XIDependentTouch) + return FALSE; + if (num_axes < 2) + return FALSE; + + if (num_axes > MAX_VALUATORS) + { + LogMessage(X_WARNING, + "Device '%s' has %d touch axes, only using first %d.\n", + device->name, num_axes, MAX_VALUATORS); + num_axes = MAX_VALUATORS; + } + + touch = calloc(1, sizeof(*touch)); + if (!touch) + return FALSE; + + touch->max_touches = max_touches; + if (max_touches == 0) + max_touches = 5; /* arbitrary number plucked out of the air */ + touch->touches = calloc(max_touches, sizeof(*touch->touches)); + if (!touch->touches) + goto err; + touch->num_touches = max_touches; + for (i = 0; i < max_touches; i++) + TouchInitTouchPoint(touch, device->valuator, i); + + touch->mode = mode; + touch->sourceid = device->id; + + device->touch = touch; + device->last.touches = calloc(max_touches, sizeof(*device->last.touches)); + device->last.num_touches = touch->num_touches; + for (i = 0; i < touch->num_touches; i++) + TouchInitDDXTouchPoint(device, &device->last.touches[i]); + + return TRUE; + +err: + for (i = 0; i < touch->num_touches; i++) + TouchFreeTouchPoint(device, i); + + free(touch->touches); + free(touch); + + return FALSE; +} + /* * Check if the given buffer contains elements between low (inclusive) and * high (inclusive) only. diff --git a/xorg-server/dix/eventconvert.c b/xorg-server/dix/eventconvert.c index 5caae0551..84c7f5f1d 100644 --- a/xorg-server/dix/eventconvert.c +++ b/xorg-server/dix/eventconvert.c @@ -58,6 +58,7 @@ 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 eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi); /* Do not use, read comments below */ BOOL EventIsKeyRepeat(xEvent *event); @@ -158,6 +159,13 @@ EventToCore(InternalEvent *event, xEvent **core_out, int *count_out) case ET_RawButtonPress: case ET_RawButtonRelease: case ET_RawMotion: + case ET_RawTouchBegin: + case ET_RawTouchUpdate: + case ET_RawTouchEnd: + case ET_TouchBegin: + case ET_TouchUpdate: + case ET_TouchEnd: + case ET_TouchOwnership: ret = BadMatch; break; default: @@ -208,6 +216,13 @@ EventToXI(InternalEvent *ev, xEvent **xi, int *count) case ET_RawButtonPress: case ET_RawButtonRelease: case ET_RawMotion: + case ET_RawTouchBegin: + case ET_RawTouchUpdate: + case ET_RawTouchEnd: + case ET_TouchBegin: + case ET_TouchUpdate: + case ET_TouchEnd: + case ET_TouchOwnership: *count = 0; *xi = NULL; return BadMatch; @@ -249,7 +264,12 @@ EventToXI2(InternalEvent *ev, xEvent **xi) case ET_ButtonRelease: case ET_KeyPress: case ET_KeyRelease: + case ET_TouchBegin: + case ET_TouchUpdate: + case ET_TouchEnd: return eventToDeviceEvent(&ev->device_event, xi); + case ET_TouchOwnership: + return eventToTouchOwnershipEvent(&ev->touch_ownership_event, xi); case ET_ProximityIn: case ET_ProximityOut: *xi = NULL; @@ -261,6 +281,9 @@ EventToXI2(InternalEvent *ev, xEvent **xi) case ET_RawButtonPress: case ET_RawButtonRelease: case ET_RawMotion: + case ET_RawTouchBegin: + case ET_RawTouchUpdate: + case ET_RawTouchEnd: return eventToRawEvent(&ev->raw_event, xi); default: break; @@ -650,7 +673,11 @@ eventToDeviceEvent(DeviceEvent *ev, xEvent **xi) xde->evtype = GetXI2Type(ev->type); xde->time = ev->time; xde->length = bytes_to_int32(len - sizeof(xEvent)); - xde->detail = ev->detail.button; + if (IsTouchEvent((InternalEvent*)ev)) + xde->detail = ev->touchid; + else + xde->detail = ev->detail.button; + xde->root = ev->root; xde->buttons_len = btlen; xde->valuators_len = vallen; @@ -659,7 +686,11 @@ eventToDeviceEvent(DeviceEvent *ev, xEvent **xi) xde->root_x = FP1616(ev->root_x, ev->root_x_frac); xde->root_y = FP1616(ev->root_y, ev->root_y_frac); - xde->flags = ev->flags; + if (ev->type == ET_TouchUpdate) + xde->flags |= (ev->flags & TOUCH_PENDING_END) ? XITouchPendingEnd : 0; + else + xde->flags = ev->flags; + if (ev->key_repeat) xde->flags |= XIKeyRepeat; @@ -696,6 +727,27 @@ eventToDeviceEvent(DeviceEvent *ev, xEvent **xi) } static int +eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi) +{ + int len = sizeof(xXITouchOwnershipEvent); + xXITouchOwnershipEvent *xtoe; + + *xi = calloc(1, len); + xtoe = (xXITouchOwnershipEvent*)*xi; + xtoe->type = GenericEvent; + xtoe->extension = IReqCode; + xtoe->length = bytes_to_int32(len - sizeof(xEvent)); + xtoe->evtype = GetXI2Type(ev->type); + xtoe->deviceid = ev->deviceid; + xtoe->time = ev->time; + xtoe->sourceid = ev->sourceid; + xtoe->touchid = ev->touchid; + xtoe->flags = 0; /* we don't have wire flags for ownership yet */ + + return Success; +} + +static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi) { xXIRawEvent* raw; @@ -810,8 +862,15 @@ GetXI2Type(enum EventType type) case ET_RawButtonPress: xi2type = XI_RawButtonPress; break; case ET_RawButtonRelease: xi2type = XI_RawButtonRelease; break; case ET_RawMotion: xi2type = XI_RawMotion; break; + case ET_RawTouchBegin: xi2type = XI_RawTouchBegin; break; + case ET_RawTouchUpdate: xi2type = XI_RawTouchUpdate; break; + case ET_RawTouchEnd: xi2type = XI_RawTouchEnd; 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_TouchUpdate: xi2type = XI_TouchUpdate; break; + case ET_TouchOwnership: xi2type = XI_TouchOwnership; break; default: break; } diff --git a/xorg-server/dix/events.c b/xorg-server/dix/events.c index 42f7c1b06..9c15ad429 100644 --- a/xorg-server/dix/events.c +++ b/xorg-server/dix/events.c @@ -152,6 +152,7 @@ typedef const char *string; #include "eventstr.h" #include "enterleave.h" #include "eventconvert.h" +#include "mi.h" /* Extension events type numbering starts at EXTENSION_EVENT_BASE. */ #define NoSuchEvent 0x80000000 /* so doesn't match NoEventMask */ @@ -1118,13 +1119,14 @@ NoticeEventTime(InternalEvent *ev) void EnqueueEvent(InternalEvent *ev, DeviceIntPtr device) { - QdEventPtr tail; + QdEventPtr tail = NULL; QdEventPtr qe; SpritePtr pSprite = device->spriteInfo->sprite; int eventlen; DeviceEvent *event = &ev->device_event; - tail = list_last_entry(&syncEvents.pending, QdEventRec, next); + if (!list_is_empty(&syncEvents.pending)) + tail = list_last_entry(&syncEvents.pending, QdEventRec, next); NoticeTime((InternalEvent*)event); @@ -1311,7 +1313,17 @@ ComputeFreezes(void) event->root_x, event->root_y); if (!CheckDeviceGrabs(replayDev, event, syncEvents.replayWin)) { - if (replayDev->focus && !IsPointerEvent((InternalEvent*)event)) + if (IsTouchEvent((InternalEvent*)event)) + { + InternalEvent *events = InitEventList(GetMaximumEventsNum()); + int i, nev; + TouchPointInfoPtr ti = TouchFindByClientID(replayDev, event->touchid); + BUG_WARN(!ti); + nev = GetTouchOwnershipEvents(events, replayDev, ti, XIRejectTouch, ti->listeners[0].listener, 0); + for (i = 0; i < nev; i++) + mieqProcessDeviceEvent(replayDev, events + i, NULL); + ProcessInputEvents(); + } else if (replayDev->focus && !IsPointerEvent((InternalEvent*)event)) DeliverFocusedEvent(replayDev, (InternalEvent*)event, w); else DeliverDeviceEvents(w, (InternalEvent*)event, NullGrab, @@ -1516,6 +1528,8 @@ DeactivatePointerGrab(DeviceIntPtr mouse) Bool wasImplicit = (mouse->deviceGrab.fromPassiveGrab && mouse->deviceGrab.implicitGrab); + TouchRemovePointerGrab(mouse); + mouse->valuator->motionHintWindow = NullWindow; mouse->deviceGrab.grab = NullGrab; mouse->deviceGrab.sync.state = NOT_GRABBED; @@ -2466,6 +2480,9 @@ FixUpEventFromWindow( case XI_RawButtonPress: case XI_RawButtonRelease: case XI_RawMotion: + case XI_RawTouchBegin: + case XI_RawTouchUpdate: + case XI_RawTouchEnd: case XI_DeviceChanged: case XI_HierarchyChanged: case XI_PropertyEvent: @@ -2476,6 +2493,13 @@ FixUpEventFromWindow( event->root = RootWindow(pSprite)->drawable.id; event->event = pWin->drawable.id; + + if (evtype == XI_TouchOwnership) + { + event->child = child; + return; + } + if (pSprite->hot.pScreen == pWin->drawable.pScreen) { event->event_x = event->root_x - FP1616(pWin->drawable.x, 0); @@ -2984,6 +3008,9 @@ CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev) case ET_ButtonPress: case ET_ButtonRelease: case ET_Motion: + case ET_TouchBegin: + case ET_TouchUpdate: + case ET_TouchEnd: break; default: /* all other events return FALSE */ @@ -3636,11 +3663,15 @@ BorderSizeNotEmpty(DeviceIntPtr pDev, WindowPtr pWin) * @param device The device of the event to check. * @param grab The grab to check. * @param event The current device event. + * @param real_event The original event, in case of touch emulation. The + * real event is the one stored in the sync queue. * * @return Whether the grab has been activated. */ Bool -ActivatePassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event) +ActivatePassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event, + InternalEvent *real_event) + { SpritePtr pSprite = device->spriteInfo->sprite; GrabInfoPtr grabinfo = &device->deviceGrab; @@ -3712,7 +3743,7 @@ ActivatePassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event) if (grabinfo->sync.state == FROZEN_NO_EVENT) grabinfo->sync.state = FROZEN_WITH_EVENT; - *grabinfo->sync.event = event->device_event; + *grabinfo->sync.event = real_event->device_event; free(xE); return TRUE; @@ -3818,6 +3849,7 @@ CheckPassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event, DeviceIntPtr gdev; XkbSrvInfoPtr xkbi = NULL; enum MatchFlags match = 0; + int emulated_type = 0; gdev = grab->modifierDevice; if (grab->grabtype == CORE) @@ -3839,13 +3871,26 @@ CheckPassiveGrab(DeviceIntPtr device, GrabPtr grab, InternalEvent *event, tempGrab->modifiersDetail.exact = xkbi ? xkbi->state.grab_mods : 0; /* Check for XI2 and XI grabs first */ - match = MatchForType(grab, tempGrab, XI2, GetXI2Type(event->any.type)); + match = MatchForType(grab, tempGrab, XI2, event->any.type); + + if (!match && IsTouchEvent(event) && (event->device_event.flags & TOUCH_POINTER_EMULATED)) + { + emulated_type = TouchGetPointerEventType(event); + match = MatchForType(grab, tempGrab, XI2, emulated_type); + } if (!match) - match = MatchForType(grab, tempGrab, XI, GetXIType(event->any.type)); + match = MatchForType(grab, tempGrab, XI, event->any.type); + + if (!match && emulated_type) + match = MatchForType(grab, tempGrab, XI, emulated_type); if (!match && checkCore) - match = MatchForType(grab, tempGrab, CORE, GetCoreType(event->any.type)); + { + match = MatchForType(grab, tempGrab, CORE, event->any.type); + if (!match && emulated_type) + match = MatchForType(grab, tempGrab, CORE, emulated_type); + } if (!match || (grab->confineTo && (!grab->confineTo->realized || @@ -3919,6 +3964,8 @@ CheckPassiveGrabsOnWindow( break; case ET_ButtonPress: case ET_ButtonRelease: + case ET_TouchBegin: + case ET_TouchEnd: tempGrab->detail.exact = event->device_event.detail.button; break; default: @@ -3936,7 +3983,7 @@ CheckPassiveGrabsOnWindow( if (!CheckPassiveGrab(device, grab, event, checkCore, tempGrab)) continue; - if (activate && !ActivatePassiveGrab(device, grab, event)) + if (activate && !ActivatePassiveGrab(device, grab, event, event)) continue; break; @@ -4151,8 +4198,8 @@ DeliverOneGrabbedEvent(InternalEvent *event, DeviceIntPtr dev, enum InputLevel l if (rc == Success) { int evtype = xi2_get_type(xE); - mask = xi2mask_isset(grab->xi2mask, dev, evtype); - filter = 1; + mask = GetXI2MaskByte(grab->xi2mask, dev, evtype); + filter = GetEventFilter(dev, xE); } break; case XI: diff --git a/xorg-server/dix/getevents.c b/xorg-server/dix/getevents.c index c843568b3..cdb00ccb4 100644 --- a/xorg-server/dix/getevents.c +++ b/xorg-server/dix/getevents.c @@ -49,6 +49,7 @@ #include "eventconvert.h" #include "inpututils.h" #include "mi.h" +#include "windowstr.h" #include <X11/extensions/XKBproto.h> #include "xkbsrv.h" @@ -171,12 +172,43 @@ key_autorepeats(DeviceIntPtr pDev, int key_code) } static void +init_event(DeviceIntPtr dev, DeviceEvent* event, Time ms) +{ + memset(event, 0, sizeof(DeviceEvent)); + event->header = ET_Internal; + event->length = sizeof(DeviceEvent); + event->time = ms; + event->deviceid = dev->id; + event->sourceid = dev->id; +} + +static void +init_touch_ownership(DeviceIntPtr dev, TouchOwnershipEvent *event, Time ms) +{ + memset(event, 0, sizeof(TouchOwnershipEvent)); + event->header = ET_Internal; + event->type = ET_TouchOwnership; + event->length = sizeof(TouchOwnershipEvent); + event->time = ms; + event->deviceid = dev->id; +} + +static void init_raw(DeviceIntPtr dev, RawDeviceEvent *event, Time ms, int type, int detail) { memset(event, 0, sizeof(RawDeviceEvent)); event->header = ET_Internal; event->length = sizeof(RawDeviceEvent); - event->type = ET_RawKeyPress - ET_KeyPress + type; + switch(type) { + case MotionNotify: event->type = ET_RawMotion; break; + case ButtonPress: event->type = ET_RawButtonPress; break; + case ButtonRelease: event->type = ET_RawButtonRelease; break; + case KeyPress: event->type = ET_RawKeyPress; break; + case KeyRelease: event->type = ET_RawKeyRelease; break; + case XI_TouchBegin: event->type = ET_RawTouchBegin; break; + case XI_TouchUpdate: event->type = ET_RawTouchUpdate; break; + case XI_TouchEnd: event->type = ET_RawTouchEnd; break; + } event->time = ms; event->deviceid = dev->id; event->sourceid = dev->id; @@ -801,36 +833,28 @@ scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) /** - * If we have HW cursors, this actually moves the visible sprite. If not, we - * just do all the screen crossing, etc. + * Scale from (absolute) device to screen coordinates here, * - * We scale from device to screen coordinates here, call - * miPointerSetPosition() and then scale back into device coordinates (if - * needed). miPSP will change x/y if the screen was crossed. - * - * The coordinates provided are always absolute. The parameter mode - * specifies whether it was relative or absolute movement that landed us at - * those coordinates. see fill_pointer_events for information on coordinate - * systems. + * The coordinates provided are always absolute. see fill_pointer_events for + * information on coordinate systems. * * @param dev The device to be moved. - * @param mode Movement mode (Absolute or Relative) - * @param[in,out] mask Mask of axis values for this event, returns the - * per-screen device coordinates after confinement + * @param mask Mask of axis values for this event * @param[out] devx x desktop-wide coordinate in device coordinate system * @param[out] devy y desktop-wide coordinate in device coordinate system * @param[out] screenx x coordinate in desktop coordinate system * @param[out] screeny y coordinate in desktop coordinate system */ static ScreenPtr -positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, - double *devx, double *devy, - double *screenx, double *screeny) +scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask, + double *devx, double *devy, + double *screenx, double *screeny) { - double x, y; - double tmpx, tmpy; ScreenPtr scr = miPointerGetScreen(dev); + double x, y; + BUG_WARN(!dev->valuator); + BUG_WARN(dev->valuator->numAxes < 2); if (!dev->valuator || dev->valuator->numAxes < 2) return scr; @@ -849,11 +873,48 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL, screenInfo.y, screenInfo.height); - tmpx = *screenx; - tmpy = *screeny; *devx = x; *devy = y; + return scr; +} + +/** + * If we have HW cursors, this actually moves the visible sprite. If not, we + * just do all the screen crossing, etc. + * + * We use the screen coordinates here, call miPointerSetPosition() and then + * scale back into device coordinates (if needed). miPSP will change x/y if + * the screen was crossed. + * + * The coordinates provided are always absolute. The parameter mode + * specifies whether it was relative or absolute movement that landed us at + * those coordinates. see fill_pointer_events for information on coordinate + * systems. + * + * @param dev The device to be moved. + * @param mode Movement mode (Absolute or Relative) + * @param[out] mask Mask of axis values for this event, returns the + * per-screen device coordinates after confinement + * @param[in,out] devx x desktop-wide coordinate in device coordinate system + * @param[in,out] devy y desktop-wide coordinate in device coordinate system + * @param[in,out] screenx x coordinate in desktop coordinate system + * @param[in,out] screeny y coordinate in desktop coordinate system + */ +static ScreenPtr +positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, + double *devx, double *devy, + double *screenx, double *screeny) +{ + ScreenPtr scr = miPointerGetScreen(dev); + double tmpx, tmpy; + + if (!dev->valuator || dev->valuator->numAxes < 2) + return scr; + + tmpx = *screenx; + tmpy = *screeny; + /* miPointerSetPosition takes care of crossing screens for us, as well as * clipping to the current screen. Coordinates returned are in desktop * coord system */ @@ -873,11 +934,13 @@ positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask, /* Recalculate the per-screen device coordinates */ if (valuator_mask_isset(mask, 0)) { + double x; x = rescaleValuatorAxis(*screenx - scr->x, NULL, dev->valuator->axes + 0, 0, scr->width); valuator_mask_set_double(mask, 0, x); } if (valuator_mask_isset(mask, 1)) { + double y; y = rescaleValuatorAxis(*screeny - scr->y, NULL, dev->valuator->axes + 1, 0, scr->height); valuator_mask_set_double(mask, 1, y); @@ -1272,6 +1335,7 @@ fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, if ((flags & POINTER_NORAW) == 0) set_raw_valuators(raw, &mask, raw->valuators.data); + scale_to_desktop(pDev, &mask, &devx, &devy, &screenx, &screeny); scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, &mask, &devx, &devy, &screenx, &screeny); @@ -1611,6 +1675,252 @@ GetProximityEvents(InternalEvent *events, DeviceIntPtr pDev, int type, const Val return num_events; } +int +GetTouchOwnershipEvents(InternalEvent *events, DeviceIntPtr pDev, + TouchPointInfoPtr ti, uint8_t reason, XID resource, + uint32_t flags) +{ + TouchClassPtr t = pDev->touch; + TouchOwnershipEvent *event; + CARD32 ms = GetTimeInMillis(); + + if (!pDev->enabled || !t || !ti) + return 0; + + event = &events->touch_ownership_event; + init_touch_ownership(pDev, event, ms); + + event->touchid = ti->client_id; + event->sourceid = ti->sourceid; + event->resource = resource; + event->flags = flags; + event->reason = reason; + + return 1; +} + +/** + * Generate internal events representing this touch event and enqueue them + * on the event queue. + * + * This function is not reentrant. Disable signals before calling. + * + * @param device The device to generate the event for + * @param type Event type, one of XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd + * @param touchid Touch point ID + * @param flags Event modification flags + * @param mask Valuator mask for valuators present for this event. + */ +void +QueueTouchEvents(DeviceIntPtr device, int type, + uint32_t ddx_touchid, int flags, const ValuatorMask *mask) +{ + int nevents; + + nevents = GetTouchEvents(InputEventList, device, ddx_touchid, type, flags, mask); + queueEventList(device, InputEventList, nevents); +} + +/** + * Get events for a touch. Generates a TouchBegin event if end is not set and + * the touch id is not active. Generates a TouchUpdate event if end is not set + * and the touch id is active. Generates a TouchEnd event if end is set and the + * touch id is active. + * + * events is not NULL-terminated; the return value is the number of events. + * The DDX is responsible for allocating the event structure in the first + * place via GetMaximumEventsNum(), and for freeing it. + * + * @param[out] events The list of events generated + * @param dev The device to generate the events for + * @param ddx_touchid The touch ID as assigned by the DDX + * @param type XI_TouchBegin, XI_TouchUpdate or XI_TouchEnd + * @param flags Event flags + * @param mask_in Valuator information for this event + */ +int +GetTouchEvents(InternalEvent *events, DeviceIntPtr dev, uint32_t ddx_touchid, + uint16_t type, uint32_t flags, const ValuatorMask *mask_in) +{ + ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen; + TouchClassPtr t = dev->touch; + ValuatorClassPtr v = dev->valuator; + DeviceEvent *event; + CARD32 ms = GetTimeInMillis(); + ValuatorMask mask; + double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */ + double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */ + int i; + int num_events = 0; + RawDeviceEvent *raw; + union touch { + TouchPointInfoPtr dix_ti; + DDXTouchPointInfoPtr ti; + } touchpoint; + int need_rawevent = TRUE; + Bool emulate_pointer = FALSE; + int client_id = 0; + + if (!dev->enabled || !t || !v) + return 0; + + /* Find and/or create the DDX touch info */ + + if (flags & TOUCH_CLIENT_ID) /* A DIX-submitted TouchEnd */ + { + touchpoint.dix_ti = TouchFindByClientID(dev, ddx_touchid); + BUG_WARN(!touchpoint.dix_ti); + + if (!touchpoint.dix_ti) + return 0; + + if (!mask_in || + !valuator_mask_isset(mask_in, 0) || + !valuator_mask_isset(mask_in, 1)) + { + ErrorF("[dix] dix-submitted events must have x/y valuator information.\n"); + return 0; + } + + need_rawevent = FALSE; + client_id = touchpoint.dix_ti->client_id; + } else /* a DDX-submitted touch */ + { + touchpoint.ti = TouchFindByDDXID(dev, ddx_touchid, (type == XI_TouchBegin)); + if (!touchpoint.ti) + { + ErrorF("[dix] %s: unable to %s touch point %x\n", dev->name, + type == XI_TouchBegin ? "begin" : "find", ddx_touchid); + return 0; + } + client_id = touchpoint.ti->client_id; + } + + if (!(flags & TOUCH_CLIENT_ID)) + emulate_pointer = touchpoint.ti->emulate_pointer; + else + emulate_pointer = !!(flags & TOUCH_POINTER_EMULATED); + + if (!IsMaster(dev)) + events = UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT, &num_events); + + valuator_mask_copy(&mask, mask_in); + + if (need_rawevent) + { + raw = &events->raw_event; + events++; + num_events++; + init_raw(dev, raw, ms, type, client_id); + set_raw_valuators(raw, &mask, raw->valuators.data_raw); + } + + event = &events->device_event; + num_events++; + + init_event(dev, event, ms); + /* if submitted for master device, get the sourceid from there */ + if (flags & TOUCH_CLIENT_ID) + { + event->sourceid = touchpoint.dix_ti->sourceid; + /* TOUCH_CLIENT_ID implies norawevent */ + } + + switch (type) { + case XI_TouchBegin: + event->type = ET_TouchBegin; + /* If we're starting a touch, we must have x & y co-ordinates. */ + if (!mask_in || + !valuator_mask_isset(mask_in, 0) || + !valuator_mask_isset(mask_in, 1)) + { + ErrorF("%s: Attempted to start touch without x/y (driver bug)\n", + dev->name); + return 0; + } + break; + case XI_TouchUpdate: + event->type = ET_TouchUpdate; + if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0) + { + ErrorF("%s: TouchUpdate with no valuators? Driver bug\n", + dev->name); + } + break; + case XI_TouchEnd: + event->type = ET_TouchEnd; + /* We can end the DDX touch here, since we don't use the active + * field below */ + if (!(flags & TOUCH_CLIENT_ID)) + TouchEndDDXTouch(dev, touchpoint.ti); + break; + default: + return 0; + } + if (!(flags & TOUCH_CLIENT_ID)) + { + if (!valuator_mask_isset(&mask, 0)) + valuator_mask_set_double(&mask, 0, valuator_mask_get_double(touchpoint.ti->valuators, 0)); + if (!valuator_mask_isset(&mask, 1)) + valuator_mask_set_double(&mask, 1, valuator_mask_get_double(touchpoint.ti->valuators, 1)); + } + + /* Get our screen event co-ordinates (root_x/root_y/event_x/event_y): + * these come from the touchpoint in Absolute mode, or the sprite in + * Relative. */ + if (t->mode == XIDirectTouch) { + transformAbsolute(dev, &mask); + + if (!(flags & TOUCH_CLIENT_ID)) { + for (i = 0; i < valuator_mask_size(&mask); i++) { + double val; + if (valuator_mask_fetch_double(&mask, i, &val)) + valuator_mask_set_double(touchpoint.ti->valuators, i, val); + } + } + + clipAbsolute(dev, &mask); + } + else { + screenx = dev->spriteInfo->sprite->hotPhys.x; + screeny = dev->spriteInfo->sprite->hotPhys.y; + } + if (need_rawevent) + set_raw_valuators(raw, &mask, raw->valuators.data); + + scr = scale_to_desktop(dev, &mask, &devx, &devy, &screenx, &screeny); + if (emulate_pointer) + scr = positionSprite(dev, Absolute, &mask, + &devx, &devy, &screenx, &screeny); + + /* see fill_pointer_events for coordinate systems */ + updateHistory(dev, &mask, ms); + clipValuators(dev, &mask); + storeLastValuators(dev, &mask, 0, 1, devx, devy); + + event->root = scr->root->drawable.id; + + event_set_root_coordinates(event, screenx, screeny); + event->touchid = client_id; + event->flags = flags; + + if (emulate_pointer) + { + event->flags |= TOUCH_POINTER_EMULATED; + event->detail.button = 1; + } + + set_valuators(dev, event, &mask); + for (i = 0; i < v->numAxes; i++) + { + if (valuator_mask_isset(&mask, i)) + v->axisVal[i] = valuator_mask_get(&mask, i); + } + + return num_events; +} + + /** * Synthesize a single motion event for the core pointer. * diff --git a/xorg-server/dix/grabs.c b/xorg-server/dix/grabs.c index da014dfc3..701470c83 100644 --- a/xorg-server/dix/grabs.c +++ b/xorg-server/dix/grabs.c @@ -266,6 +266,9 @@ CreateGrab( void FreeGrab(GrabPtr pGrab) { + if (pGrab->grabtype == XI2 && pGrab->type == XI_TouchBegin) + TouchListenerGone(pGrab->resource); + free(pGrab->modifiersDetail.pMask); free(pGrab->detail.pMask); diff --git a/xorg-server/dix/inpututils.c b/xorg-server/dix/inpututils.c index 8cd4d5921..d279c1d75 100644 --- a/xorg-server/dix/inpututils.c +++ b/xorg-server/dix/inpututils.c @@ -663,6 +663,8 @@ int event_get_corestate(DeviceIntPtr mouse, DeviceIntPtr kbd) /* core state needs to be assembled BEFORE the device is updated. */ corestate = (kbd && kbd->key) ? XkbStateFieldFromRec(&kbd->key->xkbInfo->state) : 0; corestate |= (mouse && mouse->button) ? (mouse->button->state) : 0; + corestate |= (mouse && mouse->touch) ? (mouse->touch->state) : 0; + return corestate; } @@ -672,7 +674,10 @@ void event_set_state(DeviceIntPtr mouse, DeviceIntPtr kbd, DeviceEvent *event) for (i = 0; mouse && mouse->button && i < mouse->button->numButtons; i++) if (BitIsOn(mouse->button->down, i)) - SetBit(event->buttons, i); + SetBit(event->buttons, mouse->button->map[i]); + + if (mouse && mouse->touch && mouse->touch->buttonsDown > 0) + SetBit(event->buttons, mouse->button->map[1]); if (kbd && kbd->key) { diff --git a/xorg-server/dix/touch.c b/xorg-server/dix/touch.c new file mode 100644 index 000000000..db0bf334a --- /dev/null +++ b/xorg-server/dix/touch.c @@ -0,0 +1,982 @@ +/* + * Copyright © 2011 Collabra Ltd. + * Copyright © 2011 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone <daniel@fooishbar.org> + */ + +#ifdef HAVE_DIX_CONFIG_H +#include <dix-config.h> +#endif + +#include "inputstr.h" +#include "scrnintstr.h" +#include "dixgrabs.h" + +#include "eventstr.h" +#include "exevents.h" +#include "inpututils.h" +#include "eventconvert.h" +#include "windowstr.h" +#include "mi.h" + +#define TOUCH_HISTORY_SIZE 100 + + +/* If a touch queue resize is needed, the device id's bit is set. */ +static unsigned char resize_waiting[(MAXDEVICES + 7)/8]; + +/** + * Some documentation about touch points: + * The driver submits touch events with it's own (unique) touch point ID. + * The driver may re-use those IDs, the DDX doesn't care. It just passes on + * the data to the DIX. In the server, the driver's ID is referred to as the + * DDX id anyway. + * + * On a TouchBegin, we create a DDXTouchPointInfo that contains the DDX id + * and the client ID that this touchpoint will have. The client ID is the + * one visible on the protocol. + * + * TouchUpdate and TouchEnd will only be processed if there is an active + * touchpoint with the same DDX id. + * + * The DDXTouchPointInfo struct is stored dev->last.touches. When the event + * being processed, it becomes a TouchPointInfo in dev->touch-touches which + * contains amongst other things the sprite trace and delivery information. + */ + +/** + * Check which devices need a bigger touch event queue and grow their + * last.touches by half it's current size. + * + * @param client Always the serverClient + * @param closure Always NULL + * + * @return Always True. If we fail to grow we probably will topple over soon + * anyway and re-executing this won't help. + */ +static Bool +TouchResizeQueue(ClientPtr client, pointer closure) +{ + int i; + + OsBlockSignals(); + + /* first two ids are reserved */ + for (i = 2; i < MAXDEVICES; i++) + { + DeviceIntPtr dev; + DDXTouchPointInfoPtr tmp; + size_t size; + + if (!BitIsOn(resize_waiting, i)) + continue; + + ClearBit(resize_waiting, i); + + /* device may have disappeared by now */ + dixLookupDevice(&dev, i, serverClient, DixWriteAccess); + if (!dev) + continue; + + /* Need to grow the queue means dropping events. Grow sufficiently so we + * don't need to do it often */ + size = dev->last.num_touches + dev->last.num_touches/2 + 1; + + tmp = realloc(dev->last.touches, size * sizeof(*dev->last.touches)); + if (tmp) + { + int i; + dev->last.touches = tmp; + for (i = dev->last.num_touches; i < size; i++) + TouchInitDDXTouchPoint(dev, &dev->last.touches[i]); + dev->last.num_touches = size; + } + + } + OsReleaseSignals(); + + return TRUE; +} + +/** + * Given the DDX-facing ID (which is _not_ DeviceEvent::detail.touch), find the + * associated DDXTouchPointInfoRec. + * + * @param dev The device to create the touch point for + * @param ddx_id Touch id assigned by the driver/ddx + * @param create Create the touchpoint if it cannot be found + */ +DDXTouchPointInfoPtr +TouchFindByDDXID(DeviceIntPtr dev, uint32_t ddx_id, Bool create) +{ + DDXTouchPointInfoPtr ti; + int i; + + if (!dev->touch) + return NULL; + + for (i = 0; i < dev->last.num_touches; i++) + { + ti = &dev->last.touches[i]; + if (ti->active && ti->ddx_id == ddx_id) + return ti; + } + + return create ? TouchBeginDDXTouch(dev, ddx_id) : NULL; +} + +/** + * Given a unique DDX ID for a touchpoint, create a touchpoint record and + * return it. + * + * If no other touch points are active, mark new touchpoint for pointer + * emulation. + * + * Returns NULL on failure (i.e. if another touch with that ID is already active, + * allocation failure). + */ +DDXTouchPointInfoPtr +TouchBeginDDXTouch(DeviceIntPtr dev, uint32_t ddx_id) +{ + static int next_client_id = 1; + int i; + TouchClassPtr t = dev->touch; + DDXTouchPointInfoPtr ti = NULL; + Bool emulate_pointer = (t->mode == XIDirectTouch); + + if (!t) + return NULL; + + /* Look for another active touchpoint with the same DDX ID. DDX + * touchpoints must be unique. */ + if (TouchFindByDDXID(dev, ddx_id, FALSE)) + return NULL; + + for (i = 0; i < dev->last.num_touches; i++) + { + /* Only emulate pointer events on the first touch */ + if (dev->last.touches[i].active) + emulate_pointer = FALSE; + else if (!ti) /* ti is now first non-active touch rec */ + ti = &dev->last.touches[i]; + + if (!emulate_pointer && ti) + break; + } + + if (ti) + { + int client_id; + ti->active = TRUE; + ti->ddx_id = ddx_id; + client_id = next_client_id; + next_client_id++; + if (next_client_id == 0) + next_client_id = 1; + ti->client_id = client_id; + ti->emulate_pointer = emulate_pointer; + return ti; + } + + /* If we get here, then we've run out of touches and we need to drop the + * event (we're inside the SIGIO handler here) schedule a WorkProc to + * grow the queue for us for next time. */ + ErrorF("%s: not enough space for touch events (max %d touchpoints). " + "Dropping this event.\n", dev->name, dev->last.num_touches); + if (!BitIsOn(resize_waiting, dev->id)) { + SetBit(resize_waiting, dev->id); + QueueWorkProc(TouchResizeQueue, serverClient, NULL); + } + + return NULL; +} + +void +TouchEndDDXTouch(DeviceIntPtr dev, DDXTouchPointInfoPtr ti) +{ + TouchClassPtr t = dev->touch; + + if (!t) + return; + + ti->active = FALSE; +} + +void +TouchInitDDXTouchPoint(DeviceIntPtr dev, DDXTouchPointInfoPtr ddxtouch) +{ + memset(ddxtouch, 0, sizeof(*ddxtouch)); + ddxtouch->valuators = valuator_mask_new(dev->valuator->numAxes); +} + + +Bool +TouchInitTouchPoint(TouchClassPtr t, ValuatorClassPtr v, int index) +{ + TouchPointInfoPtr ti; + + if (index >= t->num_touches) + return FALSE; + ti = &t->touches[index]; + + memset(ti, 0, sizeof(*ti)); + + ti->valuators = valuator_mask_new(v->numAxes); + if (!ti->valuators) + return FALSE; + + ti->sprite.spriteTrace = calloc(32, sizeof(*ti->sprite.spriteTrace)); + if (!ti->sprite.spriteTrace) + { + valuator_mask_free(&ti->valuators); + return FALSE; + } + ti->sprite.spriteTraceSize = 32; + ti->sprite.spriteTrace[0] = screenInfo.screens[0]->root; + ti->sprite.hot.pScreen = screenInfo.screens[0]; + ti->sprite.hotPhys.pScreen = screenInfo.screens[0]; + + ti->client_id = -1; + + return TRUE; +} + +void +TouchFreeTouchPoint(DeviceIntPtr device, int index) +{ + TouchPointInfoPtr ti; + + if (!device->touch || index >= device->touch->num_touches) + return; + ti = &device->touch->touches[index]; + + if (ti->active) + TouchEndTouch(device, ti); + + valuator_mask_free(&ti->valuators); + free(ti->sprite.spriteTrace); + ti->sprite.spriteTrace = NULL; + free(ti->listeners); + ti->listeners = NULL; + free(ti->history); + ti->history = NULL; + ti->history_size = 0; + ti->history_elements = 0; +} + +/** + * Given a client-facing ID (e.g. DeviceEvent::detail.touch), find the + * associated TouchPointInfoRec. + */ +TouchPointInfoPtr +TouchFindByClientID(DeviceIntPtr dev, uint32_t client_id) +{ + TouchClassPtr t = dev->touch; + TouchPointInfoPtr ti; + int i; + + if (!t) + return NULL; + + for (i = 0; i < t->num_touches; i++) + { + ti = &t->touches[i]; + if (ti->active && ti->client_id == client_id) + return ti; + } + + return NULL; +} + + +/** + * Given a unique ID for a touchpoint, create a touchpoint record in the + * server. + * + * Returns NULL on failure (i.e. if another touch with that ID is already active, + * allocation failure). + */ +TouchPointInfoPtr +TouchBeginTouch(DeviceIntPtr dev, int sourceid, uint32_t touchid, + Bool emulate_pointer) +{ + int i; + TouchClassPtr t = dev->touch; + TouchPointInfoPtr ti; + void *tmp; + + if (!t) + return NULL; + + /* Look for another active touchpoint with the same client ID. It's + * technically legitimate for a touchpoint to still exist with the same + * ID but only once the 32 bits wrap over and you've used up 4 billion + * touch ids without lifting that one finger off once. In which case + * you deserve a medal or something, but not error handling code. */ + if (TouchFindByClientID(dev, touchid)) + return NULL; + +try_find_touch: + for (i = 0; i < t->num_touches; i++) + { + ti = &t->touches[i]; + if (!ti->active) { + ti->active = TRUE; + ti->client_id = touchid; + ti->sourceid = sourceid; + ti->emulate_pointer = emulate_pointer; + return ti; + } + } + + /* If we get here, then we've run out of touches: enlarge dev->touch and + * try again. */ + tmp = realloc(t->touches, (t->num_touches + 1) * sizeof(*ti)); + if (tmp) + { + t->touches = tmp; + t->num_touches++; + if (TouchInitTouchPoint(t, dev->valuator, t->num_touches - 1)) + goto try_find_touch; + } + + return NULL; +} + +/** + * Releases a touchpoint for use: this must only be called after all events + * related to that touchpoint have been sent and finalised. Called from + * ProcessTouchEvent and friends. Not by you. + */ +void +TouchEndTouch(DeviceIntPtr dev, TouchPointInfoPtr ti) +{ + if (ti->emulate_pointer) + { + GrabPtr grab; + DeviceEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.type = ET_TouchEnd; + ev.detail.button = 1; + ev.touchid = ti->client_id; + ev.flags = TOUCH_POINTER_EMULATED|TOUCH_END; + UpdateDeviceState(dev, &ev); + + if ((grab = dev->deviceGrab.grab)) + { + if (dev->deviceGrab.fromPassiveGrab && + !dev->button->buttonsDown && + !dev->touch->buttonsDown && + GrabIsPointerGrab(grab)) + (*dev->deviceGrab.DeactivateGrab)(dev); + } + } + + ti->active = FALSE; + ti->pending_finish = FALSE; + ti->sprite.spriteTraceGood = 0; + free(ti->listeners); + ti->listeners = NULL; + ti->num_listeners = 0; + ti->num_grabs = 0; + ti->client_id = 0; + + TouchEventHistoryFree(ti); + + valuator_mask_zero(ti->valuators); +} + +/** + * Allocate the event history for this touch pointer. Calling this on a + * touchpoint that already has an event history does nothing but counts as + * as success. + * + * @return TRUE on success, FALSE on allocation errors + */ +Bool +TouchEventHistoryAllocate(TouchPointInfoPtr ti) +{ + if (ti->history) + return TRUE; + + ti->history = calloc(TOUCH_HISTORY_SIZE, sizeof(*ti->history)); + ti->history_elements = 0; + if (ti->history) + ti->history_size = TOUCH_HISTORY_SIZE; + return ti->history != NULL; +} + +void +TouchEventHistoryFree(TouchPointInfoPtr ti) +{ + free(ti->history); + ti->history = NULL; + ti->history_size = 0; + ti->history_elements = 0; +} + +/** + * Store the given event on the event history (if one exists) + * A touch event history consists of one TouchBegin and several TouchUpdate + * events (if applicable) but no TouchEnd event. + * If more than one TouchBegin is pushed onto the stack, the push is + * ignored, calling this function multiple times for the TouchBegin is + * valid. + */ +void +TouchEventHistoryPush(TouchPointInfoPtr ti, const DeviceEvent *ev) +{ + if (!ti->history) + return; + + switch(ev->type) + { + case ET_TouchBegin: + /* don't store the same touchbegin twice */ + if (ti->history_elements > 0) + return; + break; + case ET_TouchUpdate: + break; + case ET_TouchEnd: + return; /* no TouchEnd events in the history */ + default: + return; + } + + /* We only store real events in the history */ + if (ev->flags & (TOUCH_CLIENT_ID|TOUCH_REPLAYING)) + return; + + ti->history[ti->history_elements++] = *ev; + /* FIXME: proper overflow fixes */ + if (ti->history_elements > ti->history_size - 1) + { + ti->history_elements = ti->history_size - 1; + DebugF("source device %d: history size %d overflowing for touch %u\n", + ti->sourceid, ti->history_size, ti->client_id); + } +} + +void +TouchEventHistoryReplay(TouchPointInfoPtr ti, DeviceIntPtr dev, XID resource) +{ + InternalEvent *tel = InitEventList(GetMaximumEventsNum()); + ValuatorMask *mask = valuator_mask_new(0); + int i, nev; + int flags; + + if (!ti->history) + return; + + valuator_mask_set_double(mask, 0, ti->history[0].valuators.data[0]); + valuator_mask_set_double(mask, 1, ti->history[0].valuators.data[1]); + + flags = TOUCH_CLIENT_ID|TOUCH_REPLAYING; + if (ti->emulate_pointer) + flags |= TOUCH_POINTER_EMULATED; + /* send fake begin event to next owner */ + nev = GetTouchEvents(tel, dev, ti->client_id, XI_TouchBegin, flags, mask); + for (i = 0; i < nev; i++) + DeliverTouchEvents(dev, ti, tel + i, resource); + + valuator_mask_free(&mask); + FreeEventList(tel, GetMaximumEventsNum()); + + /* First event was TouchBegin, already replayed that one */ + for (i = 1; i < ti->history_elements; i++) + { + DeviceEvent *ev = &ti->history[i]; + ev->flags |= TOUCH_REPLAYING; + DeliverTouchEvents(dev, ti, (InternalEvent*)ev, resource); + } +} + +Bool +TouchBuildDependentSpriteTrace(DeviceIntPtr dev, SpritePtr sprite) +{ + int i; + TouchClassPtr t = dev->touch; + WindowPtr *trace; + SpritePtr srcsprite; + + /* All touches should have the same sprite trace, so find and reuse an + * existing touch's sprite if possible, else use the device's sprite. */ + for (i = 0; i < t->num_touches; i++) + if (t->touches[i].sprite.spriteTraceGood > 0) + break; + if (i < t->num_touches) + srcsprite = &t->touches[i].sprite; + else if (dev->spriteInfo->sprite) + srcsprite = dev->spriteInfo->sprite; + else + return FALSE; + + if (srcsprite->spriteTraceGood > sprite->spriteTraceSize) + { + trace = realloc(sprite->spriteTrace, + srcsprite->spriteTraceSize * sizeof(*trace)); + if (!trace) + { + sprite->spriteTraceGood = 0; + return FALSE; + } + sprite->spriteTrace = trace; + sprite->spriteTraceSize = srcsprite->spriteTraceGood; + } + memcpy(sprite->spriteTrace, srcsprite->spriteTrace, + srcsprite->spriteTraceGood * sizeof(*trace)); + sprite->spriteTraceGood = srcsprite->spriteTraceGood; + + return TRUE; +} + +/** + * Ensure a window trace is present in ti->sprite, constructing one for + * TouchBegin events. + */ +Bool +TouchEnsureSprite(DeviceIntPtr sourcedev, TouchPointInfoPtr ti, + InternalEvent *ev) +{ + TouchClassPtr t = sourcedev->touch; + SpritePtr sprite = &ti->sprite; + + /* We may not have a sprite if there are no applicable grabs or + * event selections, or if they've disappeared, or if all the grab + * owners have rejected the touch. Don't bother delivering motion + * events if not, but TouchEnd events still need to be processed so + * we can call FinishTouchPoint and release it for later use. */ + if (ev->any.type == ET_TouchEnd) + return TRUE; + else if (ev->any.type != ET_TouchBegin) + return (sprite->spriteTraceGood > 0); + + if (t->mode == XIDirectTouch) + { + /* Focus immediately under the touchpoint in direct touch mode. + * XXX: Do we need to handle crossing screens here? */ + sprite->spriteTrace[0] = + sourcedev->spriteInfo->sprite->hotPhys.pScreen->root; + XYToWindow(sprite, ev->device_event.root_x, ev->device_event.root_y); + } + else if (!TouchBuildDependentSpriteTrace(sourcedev, sprite)) + return FALSE; + + if (sprite->spriteTraceGood <= 0) + return FALSE; + + /* Mark which grabs/event selections we're delivering to: max one grab per + * window plus the bottom-most event selection. */ + ti->listeners = calloc(sprite->spriteTraceGood + 1, sizeof(*ti->listeners)); + if (!ti->listeners) + { + sprite->spriteTraceGood = 0; + return FALSE; + } + ti->num_listeners = 0; + + return TRUE; +} + +/** + * Copy the touch event into the pointer_event, switching the required + * fields to make it a correct pointer event. + * + * @param event The original touch event + * @param[in] motion_event The respective motion event + * @param[in] button_event The respective button event (if any) + * + * @returns The number of converted events. + * @retval 0 An error occured + * @retval 1 only the motion event is valid + * @retval 2 motion and button event are valid + */ +int +TouchConvertToPointerEvent(const InternalEvent *event, + InternalEvent *motion_event, + InternalEvent *button_event) +{ + int ptrtype; + int nevents = 0; + + BUG_WARN(!event); + BUG_WARN(!motion_event); + + switch(event->any.type) + { + case ET_TouchUpdate: + nevents = 1; + break; + case ET_TouchBegin: + nevents = 2; /* motion + press */ + ptrtype = ET_ButtonPress; + break; + case ET_TouchEnd: + nevents = 2; /* motion + release */ + ptrtype = ET_ButtonRelease; + break; + default: + BUG_WARN_MSG(1,"Invalid event type %d\n", event->any.type); + return 0; + } + + BUG_WARN_MSG(!(event->device_event.flags & TOUCH_POINTER_EMULATED), + "Non-emulating touch event\n"); + + *motion_event = *event; + motion_event->any.type = ET_Motion; + motion_event->device_event.detail.button = 0; + motion_event->device_event.flags = XIPointerEmulated; + + if (nevents > 1) + { + BUG_WARN(!button_event); + *button_event = *event; + button_event->any.type = ptrtype; + button_event->device_event.flags = XIPointerEmulated; + /* detail is already correct */ + } + + return nevents; +} + +/** + * Return the corresponding pointer emulation internal event type for the given + * touch event or 0 if no such event type exists. + */ +int +TouchGetPointerEventType(const InternalEvent *event) +{ + int type = 0; + + switch(event->any.type) + { + case ET_TouchBegin: type = ET_ButtonPress; break; + case ET_TouchUpdate: type = ET_Motion; break; + case ET_TouchEnd: type = ET_ButtonRelease; break; + default: + break; + } + return type; +} + + +/** + * @returns TRUE if the specified grab or selection is the current owner of + * the touch sequence. + */ +Bool +TouchResourceIsOwner(TouchPointInfoPtr ti, XID resource) +{ + return (ti->listeners[0].listener == resource); +} + +/** + * Add the resource to this touch's listeners. + */ +void +TouchAddListener(TouchPointInfoPtr ti, XID resource, enum InputLevel level, + enum TouchListenerType type, enum TouchListenerState state) +{ + ti->listeners[ti->num_listeners].listener = resource; + ti->listeners[ti->num_listeners].level = level; + ti->listeners[ti->num_listeners].state = state; + ti->listeners[ti->num_listeners].type = type; + ti->num_listeners++; +} + +/** + * Remove the resource from this touch's listeners. + * + * @return TRUE if the resource was removed, FALSE if the resource was not + * in the list + */ +Bool +TouchRemoveListener(TouchPointInfoPtr ti, XID resource) +{ + int i; + for (i = 0; i < ti->num_listeners; i++) + { + if (ti->listeners[i].listener == resource) + { + int j; + for (j = i; j< ti->num_listeners - 1; j++) + ti->listeners[j] = ti->listeners[j + 1]; + ti->num_listeners--; + ti->listeners[ti->num_listeners].listener = 0; + ti->listeners[ti->num_listeners].state = LISTENER_AWAITING_BEGIN; + return TRUE; + } + } + return FALSE; +} + +static void +TouchAddGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, + InternalEvent *ev, GrabPtr grab) +{ + enum TouchListenerType type = LISTENER_GRAB; + + /* FIXME: owner_events */ + + if (grab->grabtype == XI2) + { + if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchOwnership)) + TouchEventHistoryAllocate(ti); + if (!xi2mask_isset(grab->xi2mask, dev, XI_TouchBegin)) + type = LISTENER_POINTER_GRAB; + } else if (grab->grabtype == XI || grab->grabtype == CORE) + { + TouchEventHistoryAllocate(ti); + type = LISTENER_POINTER_GRAB; + } + + TouchAddListener(ti, grab->resource, grab->grabtype, + type, LISTENER_AWAITING_BEGIN); + ti->num_grabs++; +} + +/** + * Add one listener if there is a grab on the given window. + */ +static void +TouchAddPassiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, + WindowPtr win, InternalEvent *ev) +{ + GrabPtr grab; + Bool check_core = IsMaster(dev) && ti->emulate_pointer; + + /* FIXME: make CheckPassiveGrabsOnWindow only trigger on TouchBegin */ + grab = CheckPassiveGrabsOnWindow(win, dev, ev, check_core, FALSE); + if (!grab) + return; + + TouchAddGrabListener(dev, ti, ev, grab); +} + +static Bool +TouchAddRegularListener(DeviceIntPtr dev, TouchPointInfoPtr ti, + WindowPtr win, InternalEvent *ev) +{ + InputClients *iclients = NULL; + OtherInputMasks *inputMasks = NULL; + uint16_t evtype = 0; /* may be event type or emulated event type */ + enum TouchListenerType type = LISTENER_REGULAR; + int mask; + + evtype = GetXI2Type(ev->any.type); + mask = EventIsDeliverable(dev, ev->any.type, win); + if (!mask && !ti->emulate_pointer) + return FALSE; + else if (!mask)/* now try for pointer event */ + { + mask = EventIsDeliverable(dev, TouchGetPointerEventType(ev), win); + if (mask) + { + evtype = GetXI2Type(TouchGetPointerEventType(ev)); + type = LISTENER_POINTER_REGULAR; + } + } + if (!mask) + return FALSE; + + inputMasks = wOtherInputMasks(win); + + if (mask & EVENT_XI2_MASK) + { + nt_list_for_each_entry(iclients, inputMasks->inputClients, next) + { + if (!xi2mask_isset(iclients->xi2mask, dev, evtype)) + continue; + + if (!xi2mask_isset(iclients->xi2mask, dev, XI_TouchOwnership)) + TouchEventHistoryAllocate(ti); + + TouchAddListener(ti, iclients->resource, XI2, + type, LISTENER_AWAITING_BEGIN); + return TRUE; + } + } + + if (mask & EVENT_XI1_MASK) + { + int xitype = GetXIType(TouchGetPointerEventType(ev)); + Mask xi_filter = event_get_filter_from_type(dev, xitype); + nt_list_for_each_entry(iclients, inputMasks->inputClients, next) + { + if (!(iclients->mask[dev->id] & xi_filter)) + continue; + + TouchEventHistoryAllocate(ti); + TouchAddListener(ti, iclients->resource, XI, + LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN); + return TRUE; + } + } + + if (mask & EVENT_CORE_MASK) + { + int coretype = GetCoreType(TouchGetPointerEventType(ev)); + Mask core_filter = event_get_filter_from_type(dev, coretype); + + /* window owner */ + if (IsMaster(dev) && (win->eventMask & core_filter)) + { + TouchEventHistoryAllocate(ti); + TouchAddListener(ti, win->drawable.id, CORE, + LISTENER_POINTER_REGULAR, LISTENER_AWAITING_BEGIN); + return TRUE; + } + + /* all others */ + nt_list_for_each_entry(iclients, (InputClients*)wOtherClients(win), next) + { + if (!(iclients->mask[XIAllDevices] & core_filter)) + continue; + + TouchEventHistoryAllocate(ti); + TouchAddListener(ti, iclients->resource, CORE, + type, LISTENER_AWAITING_BEGIN); + return TRUE; + } + } + + return FALSE; +} + +static void +TouchAddActiveGrabListener(DeviceIntPtr dev, TouchPointInfoPtr ti, + InternalEvent *ev, GrabPtr grab) +{ + if (!ti->emulate_pointer && + (grab->grabtype == CORE || grab->grabtype == XI)) + return; + + if (!ti->emulate_pointer && + grab->grabtype == XI2 && + (grab->type != XI_TouchBegin && grab->type != XI_TouchEnd && grab->type != XI_TouchUpdate)) + return; + + TouchAddGrabListener(dev, ti, ev, grab); +} + +void +TouchSetupListeners(DeviceIntPtr dev, TouchPointInfoPtr ti, InternalEvent *ev) +{ + int i; + SpritePtr sprite = &ti->sprite; + WindowPtr win; + + if (dev->deviceGrab.grab) + TouchAddActiveGrabListener(dev, ti, ev, dev->deviceGrab.grab); + + /* First, find all grabbing clients from the root window down + * to the deepest child window. */ + for (i = 0; i < sprite->spriteTraceGood; i++) + { + win = sprite->spriteTrace[i]; + TouchAddPassiveGrabListener(dev, ti, win, ev); + } + + /* Find the first client with an applicable event selection, + * going from deepest child window back up to the root window. */ + for (i = sprite->spriteTraceGood - 1; i >= 0; i--) + { + Bool delivered; + + win = sprite->spriteTrace[i]; + delivered = TouchAddRegularListener(dev, ti, win, ev); + if (delivered) + return; + } +} + +/** + * Remove the touch pointer grab from the device. Called from AllowSome() + */ +void +TouchRemovePointerGrab(DeviceIntPtr dev) +{ + TouchPointInfoPtr ti; + GrabPtr grab; + DeviceEvent *ev; + + if (!dev->touch) + return; + + grab = dev->deviceGrab.grab; + if (!grab) + return; + + ev = dev->deviceGrab.sync.event; + if (!IsTouchEvent((InternalEvent*)ev)) + return; + + ti = TouchFindByClientID(dev, ev->touchid); + if (!ti) + return; +} + +/* As touch grabs don't turn into active grabs with their own resources, we + * need to walk all the touches and remove this grab from any delivery + * lists. */ +void +TouchListenerGone(XID resource) +{ + TouchPointInfoPtr ti; + DeviceIntPtr dev; + InternalEvent *events = InitEventList(GetMaximumEventsNum()); + int i, j, k, nev; + + if (!events) + FatalError("TouchListenerGone: couldn't allocate events\n"); + + for (dev = inputInfo.devices; dev; dev = dev->next) + { + if (!dev->touch) + continue; + + for (i = 0; i < dev->touch->num_touches; i++) + { + ti = &dev->touch->touches[i]; + if (!ti->active) + continue; + + for (j = 0; j < ti->num_listeners; j++) + { + if (ti->listeners[j].listener != resource) + continue; + + nev = GetTouchOwnershipEvents(events, dev, ti, XIRejectTouch, + resource, 0); + for (k = 0; k < nev; k++) + mieqProcessDeviceEvent(dev, events + k, NULL); + + break; + } + } + } + + FreeEventList(events, GetMaximumEventsNum()); +} diff --git a/xorg-server/dix/window.c b/xorg-server/dix/window.c index 1953f025b..823294b9a 100644 --- a/xorg-server/dix/window.c +++ b/xorg-server/dix/window.c @@ -131,6 +131,7 @@ Equipment Corporation. #include "privates.h" #include "xace.h" +#include "exevents.h" #include <X11/Xatom.h> /* must come after server includes */ @@ -2971,8 +2972,10 @@ UnmapWindow(WindowPtr pWin, Bool fromConfigure) if (!fromConfigure && pScreen->PostValidateTree) (*pScreen->PostValidateTree)(pLayerWin->parent, pWin, VTUnmap); } - if (wasRealized && !fromConfigure) + if (wasRealized && !fromConfigure) { WindowsRestructured (); + WindowGone(pWin); + } return Success; } @@ -3055,8 +3058,10 @@ UnmapSubwindows(WindowPtr pWin) if (anyMarked && pScreen->PostValidateTree) (*pScreen->PostValidateTree)(pLayerWin->parent, pHead, VTUnmap); } - if (wasRealized) + if (wasRealized) { WindowsRestructured (); + WindowGone(pWin); + } } |