aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/dix
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/dix')
-rw-r--r--xorg-server/dix/Makefile.am1
-rw-r--r--xorg-server/dix/devices.c87
-rw-r--r--xorg-server/dix/eventconvert.c63
-rw-r--r--xorg-server/dix/events.c69
-rw-r--r--xorg-server/dix/getevents.c352
-rw-r--r--xorg-server/dix/grabs.c3
-rw-r--r--xorg-server/dix/inpututils.c7
-rw-r--r--xorg-server/dix/touch.c982
-rw-r--r--xorg-server/dix/window.c9
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);
+ }
}