/* * Copyright © 2006 Nokia Corporation * Copyright © 2006-2007 Daniel Stone * Copyright © 2008 Red Hat, Inc. * Copyright © 2011 The Chromium Authors * * 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. * * Authors: Daniel Stone * Peter Hutterer */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include #include #include #include "misc.h" #include "resource.h" #include "inputstr.h" #include "scrnintstr.h" #include "cursorstr.h" #include "dixstruct.h" #include "globals.h" #include "dixevents.h" #include "mipointer.h" #include "eventstr.h" #include "eventconvert.h" #include "inpututils.h" #include "mi.h" #include "windowstr.h" #include #include "xkbsrv.h" #ifdef PANORAMIX #include "panoramiX.h" #include "panoramiXsrv.h" #endif #include #include #include #include #include "exglobals.h" #include "exevents.h" #include "extnsionst.h" #include "listdev.h" /* for sizing up DeviceClassesChangedEvent */ #if XSERVER_DTRACE #include typedef const char *string; #include #endif /* Number of motion history events to store. */ #define MOTION_HISTORY_SIZE 256 /** * InputEventList is the storage for input events generated by * QueuePointerEvents, QueueKeyboardEvents, and QueueProximityEvents. * This list is allocated on startup by the DIX. */ InternalEvent *InputEventList = NULL; /** * Pick some arbitrary size for Xi motion history. */ int GetMotionHistorySize(void) { return MOTION_HISTORY_SIZE; } void set_button_down(DeviceIntPtr pDev, int button, int type) { if (type == BUTTON_PROCESSED) SetBit(pDev->button->down, button); else SetBit(pDev->button->postdown, button); } void set_button_up(DeviceIntPtr pDev, int button, int type) { if (type == BUTTON_PROCESSED) ClearBit(pDev->button->down, button); else ClearBit(pDev->button->postdown, button); } Bool button_is_down(DeviceIntPtr pDev, int button, int type) { Bool ret = FALSE; if (type & BUTTON_PROCESSED) ret = ret || BitIsOn(pDev->button->down, button); if (type & BUTTON_POSTED) ret = ret || BitIsOn(pDev->button->postdown, button); return ret; } void set_key_down(DeviceIntPtr pDev, int key_code, int type) { if (type == KEY_PROCESSED) SetBit(pDev->key->down, key_code); else SetBit(pDev->key->postdown, key_code); } void set_key_up(DeviceIntPtr pDev, int key_code, int type) { if (type == KEY_PROCESSED) ClearBit(pDev->key->down, key_code); else ClearBit(pDev->key->postdown, key_code); } Bool key_is_down(DeviceIntPtr pDev, int key_code, int type) { Bool ret = FALSE; if (type & KEY_PROCESSED) ret = ret || BitIsOn(pDev->key->down, key_code); if (type & KEY_POSTED) ret = ret || BitIsOn(pDev->key->postdown, key_code); return ret; } static Bool key_autorepeats(DeviceIntPtr pDev, int key_code) { return ! !(pDev->kbdfeed->ctrl.autoRepeats[key_code >> 3] & (1 << (key_code & 7))); } 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); 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; event->detail.button = detail; } static void set_raw_valuators(RawDeviceEvent *event, ValuatorMask *mask, double *data) { int i; for (i = 0; i < valuator_mask_size(mask); i++) { if (valuator_mask_isset(mask, i)) { SetBit(event->valuators.mask, i); data[i] = valuator_mask_get_double(mask, i); } } } static void set_valuators(DeviceIntPtr dev, DeviceEvent *event, ValuatorMask *mask) { int i; /* Set the data to the previous value for unset absolute axes. The values * may be used when sent as part of an XI 1.x valuator event. */ for (i = 0; i < valuator_mask_size(mask); i++) { if (valuator_mask_isset(mask, i)) { SetBit(event->valuators.mask, i); if (valuator_get_mode(dev, i) == Absolute) SetBit(event->valuators.mode, i); event->valuators.data[i] = valuator_mask_get_double(mask, i); } else if (valuator_get_mode(dev, i) == Absolute) event->valuators.data[i] = dev->valuator->axisVal[i]; } } void CreateClassesChangedEvent(InternalEvent *event, DeviceIntPtr master, DeviceIntPtr slave, int flags) { int i; DeviceChangedEvent *dce; CARD32 ms = GetTimeInMillis(); dce = &event->changed_event; memset(dce, 0, sizeof(DeviceChangedEvent)); dce->deviceid = slave->id; dce->masterid = master ? master->id : 0; dce->header = ET_Internal; dce->length = sizeof(DeviceChangedEvent); dce->type = ET_DeviceChanged; dce->time = ms; dce->flags = flags; dce->sourceid = slave->id; if (slave->button) { dce->buttons.num_buttons = slave->button->numButtons; for (i = 0; i < dce->buttons.num_buttons; i++) dce->buttons.names[i] = slave->button->labels[i]; } if (slave->valuator) { dce->num_valuators = slave->valuator->numAxes; for (i = 0; i < dce->num_valuators; i++) { dce->valuators[i].min = slave->valuator->axes[i].min_value; dce->valuators[i].max = slave->valuator->axes[i].max_value; dce->valuators[i].resolution = slave->valuator->axes[i].resolution; dce->valuators[i].mode = slave->valuator->axes[i].mode; dce->valuators[i].name = slave->valuator->axes[i].label; dce->valuators[i].scroll = slave->valuator->axes[i].scroll; } } if (slave->key) { dce->keys.min_keycode = slave->key->xkbInfo->desc->min_key_code; dce->keys.max_keycode = slave->key->xkbInfo->desc->max_key_code; } } /** * Rescale the coord between the two axis ranges. */ static double rescaleValuatorAxis(double coord, AxisInfoPtr from, AxisInfoPtr to, double defmin, double defmax) { double fmin = defmin, fmax = defmax; double tmin = defmin, tmax = defmax; if (from && from->min_value < from->max_value) { fmin = from->min_value; fmax = from->max_value; } if (to && to->min_value < to->max_value) { tmin = to->min_value; tmax = to->max_value; } if (fmin == tmin && fmax == tmax) return coord; if (fmax == fmin) /* avoid division by 0 */ return 0.0; return (coord - fmin) * (tmax - tmin) / (fmax - fmin) + tmin; } /** * Update all coordinates when changing to a different SD * to ensure that relative reporting will work as expected * without loss of precision. * * pDev->last.valuators will be in absolute device coordinates after this * function. */ static void updateSlaveDeviceCoords(DeviceIntPtr master, DeviceIntPtr pDev) { int i; DeviceIntPtr lastSlave; /* master->last.valuators[0]/[1] is in desktop-wide coords and the actual * position of the pointer */ pDev->last.valuators[0] = master->last.valuators[0]; pDev->last.valuators[1] = master->last.valuators[1]; if (!pDev->valuator) return; /* scale back to device coordinates */ if (pDev->valuator->numAxes > 0) { pDev->last.valuators[0] = rescaleValuatorAxis(pDev->last.valuators[0], NULL, pDev->valuator->axes + 0, screenInfo.x, screenInfo.width); } if (pDev->valuator->numAxes > 1) { pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1], NULL, pDev->valuator->axes + 1, screenInfo.y, screenInfo.height); } /* calculate the other axis as well based on info from the old * slave-device. If the old slave had less axes than this one, * last.valuators is reset to 0. */ if ((lastSlave = master->last.slave) && lastSlave->valuator) { for (i = 2; i < pDev->valuator->numAxes; i++) { if (i >= lastSlave->valuator->numAxes) { pDev->last.valuators[i] = 0; valuator_mask_set_double(pDev->last.scroll, i, 0); } else { double val = pDev->last.valuators[i]; val = rescaleValuatorAxis(val, lastSlave->valuator->axes + i, pDev->valuator->axes + i, 0, 0); pDev->last.valuators[i] = val; valuator_mask_set_double(pDev->last.scroll, i, val); } } } } /** * Allocate the motion history buffer. */ void AllocateMotionHistory(DeviceIntPtr pDev) { int size; free(pDev->valuator->motion); if (pDev->valuator->numMotionEvents < 1) return; /* An MD must have a motion history size large enough to keep all * potential valuators, plus the respective range of the valuators. * 3 * INT32 for (min_val, max_val, curr_val)) */ if (IsMaster(pDev)) size = sizeof(INT32) * 3 * MAX_VALUATORS; else { ValuatorClassPtr v = pDev->valuator; int numAxes; /* XI1 doesn't understand mixed mode devices */ for (numAxes = 0; numAxes < v->numAxes; numAxes++) if (valuator_get_mode(pDev, numAxes) != valuator_get_mode(pDev, 0)) break; size = sizeof(INT32) * numAxes; } size += sizeof(Time); pDev->valuator->motion = calloc(pDev->valuator->numMotionEvents, size); pDev->valuator->first_motion = 0; pDev->valuator->last_motion = 0; if (!pDev->valuator->motion) ErrorF("[dix] %s: Failed to alloc motion history (%d bytes).\n", pDev->name, size * pDev->valuator->numMotionEvents); } /** * Dump the motion history between start and stop into the supplied buffer. * Only records the event for a given screen in theory, but in practice, we * sort of ignore this. * * If core is set, we only generate x/y, in INT16, scaled to screen coords. */ int GetMotionHistory(DeviceIntPtr pDev, xTimecoord ** buff, unsigned long start, unsigned long stop, ScreenPtr pScreen, BOOL core) { char *ibuff = NULL, *obuff; int i = 0, ret = 0; int j, coord; Time current; /* The size of a single motion event. */ int size; AxisInfo from, *to; /* for scaling */ INT32 *ocbuf, *icbuf; /* pointer to coordinates for copying */ INT16 *corebuf; AxisInfo core_axis = { 0 }; if (!pDev->valuator || !pDev->valuator->numMotionEvents) return 0; if (core && !pScreen) return 0; if (IsMaster(pDev)) size = (sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(Time); else size = (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time); *buff = malloc(size * pDev->valuator->numMotionEvents); if (!(*buff)) return 0; obuff = (char *) *buff; for (i = pDev->valuator->first_motion; i != pDev->valuator->last_motion; i = (i + 1) % pDev->valuator->numMotionEvents) { /* We index the input buffer by which element we're accessing, which * is not monotonic, and the output buffer by how many events we've * written so far. */ ibuff = (char *) pDev->valuator->motion + (i * size); memcpy(¤t, ibuff, sizeof(Time)); if (current > stop) { return ret; } else if (current >= start) { if (core) { memcpy(obuff, ibuff, sizeof(Time)); /* copy timestamp */ icbuf = (INT32 *) (ibuff + sizeof(Time)); corebuf = (INT16 *) (obuff + sizeof(Time)); /* fetch x coordinate + range */ memcpy(&from.min_value, icbuf++, sizeof(INT32)); memcpy(&from.max_value, icbuf++, sizeof(INT32)); memcpy(&coord, icbuf++, sizeof(INT32)); /* scale to screen coords */ to = &core_axis; to->max_value = pScreen->width; coord = rescaleValuatorAxis(coord, &from, to, 0, pScreen->width); memcpy(corebuf, &coord, sizeof(INT16)); corebuf++; /* fetch y coordinate + range */ memcpy(&from.min_value, icbuf++, sizeof(INT32)); memcpy(&from.max_value, icbuf++, sizeof(INT32)); memcpy(&coord, icbuf++, sizeof(INT32)); to->max_value = pScreen->height; coord = rescaleValuatorAxis(coord, &from, to, 0, pScreen->height); memcpy(corebuf, &coord, sizeof(INT16)); } else if (IsMaster(pDev)) { memcpy(obuff, ibuff, sizeof(Time)); /* copy timestamp */ ocbuf = (INT32 *) (obuff + sizeof(Time)); icbuf = (INT32 *) (ibuff + sizeof(Time)); for (j = 0; j < MAX_VALUATORS; j++) { if (j >= pDev->valuator->numAxes) break; /* fetch min/max/coordinate */ memcpy(&from.min_value, icbuf++, sizeof(INT32)); memcpy(&from.max_value, icbuf++, sizeof(INT32)); memcpy(&coord, icbuf++, sizeof(INT32)); to = (j < pDev->valuator->numAxes) ? &pDev->valuator-> axes[j] : NULL; /* x/y scaled to screen if no range is present */ if (j == 0 && (from.max_value < from.min_value)) from.max_value = pScreen->width; else if (j == 1 && (from.max_value < from.min_value)) from.max_value = pScreen->height; /* scale from stored range into current range */ coord = rescaleValuatorAxis(coord, &from, to, 0, 0); memcpy(ocbuf, &coord, sizeof(INT32)); ocbuf++; } } else memcpy(obuff, ibuff, size); /* don't advance by size here. size may be different to the * actually written size if the MD has less valuators than MAX */ if (core) obuff += sizeof(INT32) + sizeof(Time); else obuff += (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time); ret++; } } return ret; } /** * Update the motion history for a specific device, with the list of * valuators. * * Layout of the history buffer: * for SDs: [time] [val0] [val1] ... [valn] * for MDs: [time] [min_val0] [max_val0] [val0] [min_val1] ... [valn] * * For events that have some valuators unset: * min_val == max_val == val == 0. */ static void updateMotionHistory(DeviceIntPtr pDev, CARD32 ms, ValuatorMask *mask, double *valuators) { char *buff = (char *) pDev->valuator->motion; ValuatorClassPtr v; int i; if (!pDev->valuator->numMotionEvents) return; v = pDev->valuator; if (IsMaster(pDev)) { buff += ((sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(CARD32)) * v->last_motion; memcpy(buff, &ms, sizeof(Time)); buff += sizeof(Time); memset(buff, 0, sizeof(INT32) * 3 * MAX_VALUATORS); for (i = 0; i < v->numAxes; i++) { int val; /* XI1 doesn't support mixed mode devices */ if (valuator_get_mode(pDev, i) != valuator_get_mode(pDev, 0)) break; if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) { buff += 3 * sizeof(INT32); continue; } memcpy(buff, &v->axes[i].min_value, sizeof(INT32)); buff += sizeof(INT32); memcpy(buff, &v->axes[i].max_value, sizeof(INT32)); buff += sizeof(INT32); val = valuators[i]; memcpy(buff, &val, sizeof(INT32)); buff += sizeof(INT32); } } else { buff += ((sizeof(INT32) * pDev->valuator->numAxes) + sizeof(CARD32)) * pDev->valuator->last_motion; memcpy(buff, &ms, sizeof(Time)); buff += sizeof(Time); memset(buff, 0, sizeof(INT32) * pDev->valuator->numAxes); for (i = 0; i < MAX_VALUATORS; i++) { int val; if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) { buff += sizeof(INT32); continue; } val = valuators[i]; memcpy(buff, &val, sizeof(INT32)); buff += sizeof(INT32); } } pDev->valuator->last_motion = (pDev->valuator->last_motion + 1) % pDev->valuator->numMotionEvents; /* If we're wrapping around, just keep the circular buffer going. */ if (pDev->valuator->first_motion == pDev->valuator->last_motion) pDev->valuator->first_motion = (pDev->valuator->first_motion + 1) % pDev->valuator->numMotionEvents; return; } /** * Returns the maximum number of events GetKeyboardEvents * and GetPointerEvents will ever return. * * This MUST be absolutely constant, from init until exit. */ int GetMaximumEventsNum(void) { /* One raw event * One device event * One possible device changed event * Lots of possible separate button scroll events (horiz + vert) * Lots of possible separate raw button scroll events (horiz + vert) */ return 100; } /** * Clip an axis to its bounds, which are declared in the call to * InitValuatorAxisClassStruct. */ static void clipAxis(DeviceIntPtr pDev, int axisNum, double *val) { AxisInfoPtr axis; if (axisNum >= pDev->valuator->numAxes) return; axis = pDev->valuator->axes + axisNum; /* If a value range is defined, clip. If not, do nothing */ if (axis->max_value <= axis->min_value) return; if (*val < axis->min_value) *val = axis->min_value; if (*val > axis->max_value) *val = axis->max_value; } /** * Clip every axis in the list of valuators to its bounds. */ static void clipValuators(DeviceIntPtr pDev, ValuatorMask *mask) { int i; for (i = 0; i < valuator_mask_size(mask); i++) if (valuator_mask_isset(mask, i)) { double val = valuator_mask_get_double(mask, i); clipAxis(pDev, i, &val); valuator_mask_set_double(mask, i, val); } } /** * Create the DCCE event (does not update the master's device state yet, this * is done in the event processing). * Pull in the coordinates from the MD if necessary. * * @param events Pointer to a pre-allocated event array. * @param dev The slave device that generated an event. * @param type Either DEVCHANGE_POINTER_EVENT and/or DEVCHANGE_KEYBOARD_EVENT * @param num_events The current number of events, returns the number of * events if a DCCE was generated. * @return The updated @events pointer. */ InternalEvent * UpdateFromMaster(InternalEvent *events, DeviceIntPtr dev, int type, int *num_events) { DeviceIntPtr master; master = GetMaster(dev, (type & DEVCHANGE_POINTER_EVENT) ? MASTER_POINTER : MASTER_KEYBOARD); if (master && master->last.slave != dev) { CreateClassesChangedEvent(events, master, dev, type | DEVCHANGE_SLAVE_SWITCH); if (IsPointerDevice(master)) { updateSlaveDeviceCoords(master, dev); master->last.numValuators = dev->last.numValuators; } master->last.slave = dev; (*num_events)++; events++; } return events; } /** * Move the device's pointer to the position given in the valuators. * * @param dev The device whose pointer is to be moved. * @param mask Valuator data for this event. */ static void clipAbsolute(DeviceIntPtr dev, ValuatorMask *mask) { int i; for (i = 0; i < valuator_mask_size(mask); i++) { double val; if (!valuator_mask_isset(mask, i)) continue; val = valuator_mask_get_double(mask, i); clipAxis(dev, i, &val); valuator_mask_set_double(mask, i, val); } } /** * Move the device's pointer by the values given in @valuators. * * @param dev The device whose pointer is to be moved. * @param[in,out] mask Valuator data for this event, modified in-place. */ static void moveRelative(DeviceIntPtr dev, ValuatorMask *mask) { int i; Bool clip_xy = IsMaster(dev) || !IsFloating(dev); /* calc other axes, clip, drop back into valuators */ for (i = 0; i < valuator_mask_size(mask); i++) { double val = dev->last.valuators[i]; if (!valuator_mask_isset(mask, i)) continue; val += valuator_mask_get_double(mask, i); /* x & y need to go over the limits to cross screens if the SD * isn't currently attached; otherwise, clip to screen bounds. */ if (valuator_get_mode(dev, i) == Absolute && ((i != 0 && i != 1) || clip_xy)) clipAxis(dev, i, &val); valuator_mask_set_double(mask, i, val); } } /** * Accelerate the data in valuators based on the device's acceleration scheme. * * @param dev The device which's pointer is to be moved. * @param valuators Valuator mask * @param ms Current time. */ static void accelPointer(DeviceIntPtr dev, ValuatorMask *valuators, CARD32 ms) { if (dev->valuator->accelScheme.AccelSchemeProc) dev->valuator->accelScheme.AccelSchemeProc(dev, valuators, ms); } /** * Scale from absolute screen coordinates to absolute coordinates in the * device's coordinate range. * * @param dev The device to scale for. * @param[in, out] mask The mask in desktop coordinates, modified in place * to contain device coordinate range. */ static void scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask) { double scaled; ScreenPtr scr = miPointerGetScreen(dev); if (valuator_mask_isset(mask, 0)) { scaled = valuator_mask_get_double(mask, 0) + scr->x; scaled = rescaleValuatorAxis(scaled, NULL, dev->valuator->axes + 0, 0, scr->width); valuator_mask_set_double(mask, 0, scaled); } if (valuator_mask_isset(mask, 1)) { scaled = valuator_mask_get_double(mask, 1) + scr->y; scaled = rescaleValuatorAxis(scaled, NULL, dev->valuator->axes + 1, 0, scr->height); valuator_mask_set_double(mask, 1, scaled); } } /** * Scale from (absolute) device to screen coordinates here, * * The coordinates provided are always absolute. see fill_pointer_events for * information on coordinate systems. * * @param dev The device to be moved. * @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 scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask, double *devx, double *devy, double *screenx, double *screeny) { ScreenPtr scr = miPointerGetScreen(dev); double x, y; BUG_WARN(!dev->valuator || dev->valuator->numAxes < 2); if (!dev->valuator || dev->valuator->numAxes < 2) { /* if we have no axes, last.valuators must be in screen coords * anyway */ *devx = *screenx = dev->last.valuators[0]; *devy = *screeny = dev->last.valuators[1]; return scr; } if (valuator_mask_isset(mask, 0)) x = valuator_mask_get_double(mask, 0); else x = dev->last.valuators[0]; if (valuator_mask_isset(mask, 1)) y = valuator_mask_get_double(mask, 1); else y = dev->last.valuators[1]; /* scale x&y to desktop coordinates */ *screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL, screenInfo.x, screenInfo.width); *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL, screenInfo.y, screenInfo.height); *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 */ scr = miPointerSetPosition(dev, mode, screenx, screeny); /* If we were constrained, rescale x/y from the screen coordinates so * the device valuators reflect the correct position. For screen * crossing this doesn't matter much, the coords would be 0 or max. */ if (tmpx != *screenx) *devx = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0, screenInfo.x, screenInfo.width); if (tmpy != *screeny) *devy = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1, screenInfo.y, screenInfo.height); /* 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); } return scr; } /** * Update the motion history for the device and (if appropriate) for its * master device. * @param dev Slave device to update. * @param mask Bit mask of valid valuators to append to history. * @param num Total number of valuators to append to history. * @param ms Current time */ static void updateHistory(DeviceIntPtr dev, ValuatorMask *mask, CARD32 ms) { if (!dev->valuator) return; updateMotionHistory(dev, ms, mask, dev->last.valuators); if (!IsMaster(dev) && !IsFloating(dev)) { DeviceIntPtr master = GetMaster(dev, MASTER_POINTER); updateMotionHistory(master, ms, mask, dev->last.valuators); } } static void queueEventList(DeviceIntPtr device, InternalEvent *events, int nevents) { int i; for (i = 0; i < nevents; i++) mieqEnqueue(device, &events[i]); } static void event_set_root_coordinates(DeviceEvent *event, double x, double y) { event->root_x = trunc(x); event->root_y = trunc(y); event->root_x_frac = x - trunc(x); event->root_y_frac = y - trunc(y); } /** * Generate internal events representing this keyboard event and enqueue * them on the event queue. * * This function is not reentrant. Disable signals before calling. * * FIXME: flags for relative/abs motion? * * @param device The device to generate the event for * @param type Event type, one of KeyPress or KeyRelease * @param keycode Key code of the pressed/released key * @param mask Valuator mask for valuators present for this event. * */ void QueueKeyboardEvents(DeviceIntPtr device, int type, int keycode, const ValuatorMask *mask) { int nevents; nevents = GetKeyboardEvents(InputEventList, device, type, keycode, mask); queueEventList(device, InputEventList, nevents); } /** * Returns a set of InternalEvents for KeyPress/KeyRelease, optionally * also with valuator events. * * The DDX is responsible for allocating the event list in the first * place via InitEventList(), and for freeing it. * * @return the number of events written into events. */ int GetKeyboardEvents(InternalEvent *events, DeviceIntPtr pDev, int type, int key_code, const ValuatorMask *mask_in) { int num_events = 0; CARD32 ms = 0; DeviceEvent *event; RawDeviceEvent *raw; ValuatorMask mask; #if XSERVER_DTRACE if (XSERVER_INPUT_EVENT_ENABLED()) { XSERVER_INPUT_EVENT(pDev->id, type, key_code, 0, mask_in ? mask_in->last_bit + 1 : 0, mask_in ? mask_in->mask : NULL, mask_in ? mask_in->valuators : NULL); } #endif /* refuse events from disabled devices */ if (!pDev->enabled) return 0; if (!events || !pDev->key || !pDev->focus || !pDev->kbdfeed || (type != KeyPress && type != KeyRelease) || (key_code < 8 || key_code > 255)) return 0; if (mask_in && valuator_mask_size(mask_in) > 1) { ErrorF("[dix] the server does not handle valuator masks with " "keyboard events. This is a bug. You may fix it.\n"); } num_events = 1; events = UpdateFromMaster(events, pDev, DEVCHANGE_KEYBOARD_EVENT, &num_events); /* Handle core repeating, via press/release/press/release. */ if (type == KeyPress && key_is_down(pDev, key_code, KEY_POSTED)) { /* If autorepeating is disabled either globally or just for that key, * or we have a modifier, don't generate a repeat event. */ if (!pDev->kbdfeed->ctrl.autoRepeat || !key_autorepeats(pDev, key_code) || pDev->key->xkbInfo->desc->map->modmap[key_code]) return 0; } ms = GetTimeInMillis(); raw = &events->raw_event; events++; num_events++; valuator_mask_copy(&mask, mask_in); init_raw(pDev, raw, ms, type, key_code); set_raw_valuators(raw, &mask, raw->valuators.data_raw); clipValuators(pDev, &mask); set_raw_valuators(raw, &mask, raw->valuators.data); event = &events->device_event; init_device_event(event, pDev, ms); event->detail.key = key_code; if (type == KeyPress) { event->type = ET_KeyPress; set_key_down(pDev, key_code, KEY_POSTED); } else if (type == KeyRelease) { event->type = ET_KeyRelease; set_key_up(pDev, key_code, KEY_POSTED); } clipValuators(pDev, &mask); set_valuators(pDev, event, &mask); if (!IsFloating(pDev)) { DeviceIntPtr master = GetMaster(pDev, MASTER_POINTER); event_set_root_coordinates(event, master->last.valuators[0], master->last.valuators[1]); } return num_events; } /** * Initialize an event array large enough for num_events arrays. * This event list is to be passed into GetPointerEvents() and * GetKeyboardEvents(). * * @param num_events Number of elements in list. */ InternalEvent * InitEventList(int num_events) { InternalEvent *events = calloc(num_events, sizeof(InternalEvent)); return events; } /** * Free an event list. * * @param list The list to be freed. * @param num_events Number of elements in list. */ void FreeEventList(InternalEvent *list, int num_events) { free(list); } /** * Transform vector x/y according to matrix m and drop the rounded coords * back into x/y. */ static void transform(struct pixman_f_transform *m, double *x, double *y) { struct pixman_f_vector p = {.v = {*x, *y, 1} }; pixman_f_transform_point(m, &p); *x = p.v[0]; *y = p.v[1]; } /** * Apply the device's transformation matrix to the valuator mask and replace * the scaled values in mask. This transformation only applies to valuators * 0 and 1, others will be untouched. * * @param dev The device the valuators came from * @param[in,out] mask The valuator mask. */ static void transformAbsolute(DeviceIntPtr dev, ValuatorMask *mask) { double x, y, ox, oy; if (valuator_mask_isset(mask, 0)) ox = x = valuator_mask_get_double(mask, 0); else ox = x = dev->last.valuators[0]; if (valuator_mask_isset(mask, 1)) oy = y = valuator_mask_get_double(mask, 1); else oy = y = dev->last.valuators[1]; transform(&dev->transform, &x, &y); if (valuator_mask_isset(mask, 0) || ox != x) valuator_mask_set_double(mask, 0, x); if (valuator_mask_isset(mask, 1) || oy != y) valuator_mask_set_double(mask, 1, y); } static void storeLastValuators(DeviceIntPtr dev, ValuatorMask *mask, int xaxis, int yaxis, double devx, double devy) { int i; /* store desktop-wide in last.valuators */ if (valuator_mask_isset(mask, xaxis)) dev->last.valuators[0] = devx; if (valuator_mask_isset(mask, yaxis)) dev->last.valuators[1] = devy; for (i = 0; i < valuator_mask_size(mask); i++) { if (i == xaxis || i == yaxis) continue; if (valuator_mask_isset(mask, i)) dev->last.valuators[i] = valuator_mask_get_double(mask, i); } } /** * Generate internal events representing this pointer 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 ButtonPress, ButtonRelease, MotionNotify * @param buttons Button number of the buttons modified. Must be 0 for * MotionNotify * @param flags Event modification flags * @param mask Valuator mask for valuators present for this event. */ void QueuePointerEvents(DeviceIntPtr device, int type, int buttons, int flags, const ValuatorMask *mask) { int nevents; nevents = GetPointerEvents(InputEventList, device, type, buttons, flags, mask); queueEventList(device, InputEventList, nevents); } /** * Helper function for GetPointerEvents, which only generates motion and * raw motion events for the slave device: does not update the master device. * * Should not be called by anyone other than GetPointerEvents. * * We use several different coordinate systems and need to switch between * the three in fill_pointer_events, positionSprite and * miPointerSetPosition. "desktop" refers to the width/height of all * screenInfo.screens[n]->width/height added up. "screen" is ScreenRec, not * output. * * Coordinate systems: * - relative events have a mask_in in relative coordinates, mapped to * pixels. These events are mapped to the current position±delta. * - absolute events have a mask_in in absolute device coordinates in * device-specific range. This range is mapped to the desktop. * - POINTER_SCREEN absolute events (x86WarpCursor) are in screen-relative * screen coordinate range. * - rootx/rooty in events must be be relative to the current screen's * origin (screen coordinate system) * - XI2 valuators must be relative to the current screen's origin. On * the protocol the device min/max range maps to the current screen. * * For screen switching we need to get the desktop coordinates for each * event, then map that to the respective position on each screen and * position the cursor there. * The device's last.valuator[] stores the last position in desktop-wide * coordinates (in device range for slave devices, desktop range for master * devices). * * screen-relative device coordinates requires scaling: A device coordinate * x/y of range [n..m] that maps to positions Sx/Sy on Screen S must be * rescaled to match Sx/Sy for [n..m]. In the simplest example, x of (m/2-1) * is the last coordinate on the first screen and must be rescaled for the * event to be m. XI2 clients that do their own coordinate mapping would * otherwise interpret the position of the device elsewere to the cursor. * * @return the number of events written into events. */ static int fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type, int buttons, CARD32 ms, int flags, const ValuatorMask *mask_in) { int num_events = 1; DeviceEvent *event; RawDeviceEvent *raw; double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */ double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */ ValuatorMask mask; ScreenPtr scr; switch (type) { case MotionNotify: if (!pDev->valuator) { ErrorF("[dix] motion events from device %d without valuators\n", pDev->id); return 0; } if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0) return 0; break; case ButtonPress: case ButtonRelease: if (!pDev->button || !buttons) return 0; if (mask_in && valuator_mask_size(mask_in) > 0 && !pDev->valuator) { ErrorF ("[dix] button event with valuator from device %d without valuators\n", pDev->id); return 0; } break; default: return 0; } valuator_mask_copy(&mask, mask_in); if ((flags & POINTER_NORAW) == 0) { raw = &events->raw_event; events++; num_events++; init_raw(pDev, raw, ms, type, buttons); set_raw_valuators(raw, &mask, raw->valuators.data_raw); } /* valuators are in driver-native format (rel or abs) */ if (flags & POINTER_ABSOLUTE) { if (flags & POINTER_SCREEN) /* valuators are in screen coords */ scale_from_screen(pDev, &mask); transformAbsolute(pDev, &mask); clipAbsolute(pDev, &mask); if ((flags & POINTER_NORAW) == 0) set_raw_valuators(raw, &mask, raw->valuators.data); } else { if (flags & POINTER_ACCELERATE) accelPointer(pDev, &mask, ms); if ((flags & POINTER_NORAW) == 0) set_raw_valuators(raw, &mask, raw->valuators.data); moveRelative(pDev, &mask); } /* valuators are in device coordinate system in absolute coordinates */ scale_to_desktop(pDev, &mask, &devx, &devy, &screenx, &screeny); scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, &mask, &devx, &devy, &screenx, &screeny); /* screenx, screeny are in desktop coordinates, mask is in device coordinates per-screen (the event data) devx/devy is in device coordinate desktop-wide */ updateHistory(pDev, &mask, ms); clipValuators(pDev, &mask); storeLastValuators(pDev, &mask, 0, 1, devx, devy); /* Update the MD's co-ordinates, which are always in desktop space. */ if (!IsMaster(pDev) && !IsFloating(pDev)) { DeviceIntPtr master = GetMaster(pDev, MASTER_POINTER); master->last.valuators[0] = screenx; master->last.valuators[1] = screeny; } event = &events->device_event; init_device_event(event, pDev, ms); if (type == MotionNotify) { event->type = ET_Motion; event->detail.button = 0; } else { if (type == ButtonPress) { event->type = ET_ButtonPress; set_button_down(pDev, buttons, BUTTON_POSTED); } else if (type == ButtonRelease) { event->type = ET_ButtonRelease; set_button_up(pDev, buttons, BUTTON_POSTED); } event->detail.button = buttons; } /* root_x and root_y must be in per-screen co-ordinates */ event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y); if (flags & POINTER_EMULATED) { raw->flags = XIPointerEmulated; event->flags = XIPointerEmulated; } set_valuators(pDev, event, &mask); return num_events; } /** * Generate events for each scroll axis that changed between before/after * for the device. * * @param events The pointer to the event list to fill the events * @param dev The device to generate the events for * @param type The real type of the event * @param axis The axis number to generate events for * @param mask State before this event in absolute coords * @param[in,out] last Last scroll state posted in absolute coords (modified * in-place) * @param ms Current time in ms * @param max_events Max number of events to be generated * @return The number of events generated */ static int emulate_scroll_button_events(InternalEvent *events, DeviceIntPtr dev, int type, int axis, const ValuatorMask *mask, ValuatorMask *last, CARD32 ms, int max_events) { AxisInfoPtr ax; double delta; double incr; int num_events = 0; double total; int b; int flags = 0; if (dev->valuator->axes[axis].scroll.type == SCROLL_TYPE_NONE) return 0; if (!valuator_mask_isset(mask, axis)) return 0; ax = &dev->valuator->axes[axis]; incr = ax->scroll.increment; if (type != ButtonPress && type != ButtonRelease) flags |= POINTER_EMULATED; if (!valuator_mask_isset(last, axis)) valuator_mask_set_double(last, axis, 0); delta = valuator_mask_get_double(mask, axis) - valuator_mask_get_double(last, axis); total = delta; b = (ax->scroll.type == SCROLL_TYPE_VERTICAL) ? 5 : 7; if ((incr > 0 && delta < 0) || (incr < 0 && delta > 0)) b--; /* we're scrolling up or left → button 4 or 6 */ while (fabs(delta) >= fabs(incr)) { int nev_tmp; if (delta > 0) delta -= fabs(incr); else if (delta < 0) delta += fabs(incr); /* fill_pointer_events() generates four events: one normal and one raw * event for button press and button release. * We may get a bigger scroll delta than we can generate events * for. In that case, we keep decreasing delta, but skip events. */ if (num_events + 4 < max_events) { if (type != ButtonRelease) { nev_tmp = fill_pointer_events(events, dev, ButtonPress, b, ms, flags, NULL); events += nev_tmp; num_events += nev_tmp; } if (type != ButtonPress) { nev_tmp = fill_pointer_events(events, dev, ButtonRelease, b, ms, flags, NULL); events += nev_tmp; num_events += nev_tmp; } } } /* We emulated, update last.scroll */ if (total != delta) { total -= delta; valuator_mask_set_double(last, axis, valuator_mask_get_double(last, axis) + total); } return num_events; } /** * Generate a complete series of InternalEvents (filled into the EventList) * representing pointer motion, or button presses. If the device is a slave * device, also potentially generate a DeviceClassesChangedEvent to update * the master device. * * 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 InitEventList() and GetMaximumEventsNum(), and for freeing it. * * In the generated events rootX/Y will be in absolute screen coords and * the valuator information in the absolute or relative device coords. * * last.valuators[x] of the device is always in absolute device coords. * last.valuators[x] of the master device is in absolute screen coords. * * master->last.valuators[x] for x > 2 is undefined. */ int GetPointerEvents(InternalEvent *events, DeviceIntPtr pDev, int type, int buttons, int flags, const ValuatorMask *mask_in) { CARD32 ms = GetTimeInMillis(); int num_events = 0, nev_tmp; ValuatorMask mask; ValuatorMask scroll; int i; int realtype = type; #if XSERVER_DTRACE if (XSERVER_INPUT_EVENT_ENABLED()) { XSERVER_INPUT_EVENT(pDev->id, type, buttons, flags, mask_in ? mask_in->last_bit + 1 : 0, mask_in ? mask_in->mask : NULL, mask_in ? mask_in->valuators : NULL); } #endif /* refuse events from disabled devices */ if (!pDev->enabled) return 0; if (!miPointerGetScreen(pDev)) return 0; events = UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT, &num_events); valuator_mask_copy(&mask, mask_in); /* Turn a scroll button press into a smooth-scrolling event if * necessary. This only needs to cater for the XIScrollFlagPreferred * axis (if more than one scrolling axis is present) */ if (type == ButtonPress) { double val, adj; int axis; int h_scroll_axis = -1; int v_scroll_axis = -1; if (pDev->valuator) { h_scroll_axis = pDev->valuator->h_scroll_axis; v_scroll_axis = pDev->valuator->v_scroll_axis; } /* Up is negative on valuators, down positive */ switch (buttons) { case 4: adj = -1.0; axis = v_scroll_axis; break; case 5: adj = 1.0; axis = v_scroll_axis; break; case 6: adj = -1.0; axis = h_scroll_axis; break; case 7: adj = 1.0; axis = h_scroll_axis; break; default: adj = 0.0; axis = -1; break; } if (adj != 0.0 && axis != -1) { adj *= pDev->valuator->axes[axis].scroll.increment; val = valuator_mask_get_double(&mask, axis) + adj; valuator_mask_set_double(&mask, axis, val); type = MotionNotify; buttons = 0; flags |= POINTER_EMULATED; } } /* First fill out the original event set, with smooth-scrolling axes. */ nev_tmp = fill_pointer_events(events, pDev, type, buttons, ms, flags, &mask); events += nev_tmp; num_events += nev_tmp; valuator_mask_zero(&scroll); /* Now turn the smooth-scrolling axes back into emulated button presses * for legacy clients, based on the integer delta between before and now */ for (i = 0; i < valuator_mask_size(&mask); i++) { if (!valuator_mask_isset(&mask, i)) continue; valuator_mask_set_double(&scroll, i, pDev->last.valuators[i]); nev_tmp = emulate_scroll_button_events(events, pDev, realtype, i, &scroll, pDev->last.scroll, ms, GetMaximumEventsNum() - num_events); events += nev_tmp; num_events += nev_tmp; } return num_events; } /** * Generate internal events representing this proximity 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 ProximityIn or ProximityOut * @param keycode Key code of the pressed/released key * @param mask Valuator mask for valuators present for this event. * */ void QueueProximityEvents(DeviceIntPtr device, int type, const ValuatorMask *mask) { int nevents; nevents = GetProximityEvents(InputEventList, device, type, mask); queueEventList(device, InputEventList, nevents); } /** * Generate ProximityIn/ProximityOut InternalEvents, accompanied by * valuators. * * The DDX is responsible for allocating the events in the first place via * InitEventList(), and for freeing it. * * @return the number of events written into events. */ int GetProximityEvents(InternalEvent *events, DeviceIntPtr pDev, int type, const ValuatorMask *mask_in) { int num_events = 1, i; DeviceEvent *event; ValuatorMask mask; #if XSERVER_DTRACE if (XSERVER_INPUT_EVENT_ENABLED()) { XSERVER_INPUT_EVENT(pDev->id, type, 0, 0, mask_in ? mask_in->last_bit + 1 : 0, mask_in ? mask_in->mask : NULL, mask_in ? mask_in->valuators : NULL); } #endif /* refuse events from disabled devices */ if (!pDev->enabled) return 0; /* Sanity checks. */ if ((type != ProximityIn && type != ProximityOut) || !mask_in) return 0; if (!pDev->valuator || !pDev->proximity) return 0; valuator_mask_copy(&mask, mask_in); /* ignore relative axes for proximity. */ for (i = 0; i < valuator_mask_size(&mask); i++) { if (valuator_mask_isset(&mask, i) && valuator_get_mode(pDev, i) == Relative) valuator_mask_unset(&mask, i); } /* FIXME: posting proximity events with relative valuators only results * in an empty event, EventToXI() will fail to convert → no event sent * to client. */ events = UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT, &num_events); event = &events->device_event; init_device_event(event, pDev, GetTimeInMillis()); event->type = (type == ProximityIn) ? ET_ProximityIn : ET_ProximityOut; clipValuators(pDev, &mask); set_valuators(pDev, event, &mask); 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 XSERVER_DTRACE if (XSERVER_INPUT_EVENT_ENABLED()) { XSERVER_INPUT_EVENT(dev->id, type, ddx_touchid, flags, mask_in ? mask_in->last_bit + 1 : 0, mask_in ? mask_in->mask : NULL, mask_in ? mask_in->valuators : NULL); } #endif 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 (t->mode == XIDirectTouch && !(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); /* Indirect device touch coordinates are not used for cursor positioning. * They are merely informational, and are provided in device coordinates. * The device sprite is used for positioning instead, and it is already * scaled. */ if (t->mode == XIDirectTouch) 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 */ if (emulate_pointer) updateHistory(dev, &mask, ms); clipValuators(dev, &mask); if (emulate_pointer) 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. * * Used in cursor functions, e.g. when cursor confinement changes, and we need * to shift the pointer to get it inside the new bounds. */ void PostSyntheticMotion(DeviceIntPtr pDev, int x, int y, int screen, unsigned long time) { DeviceEvent ev; #ifdef PANORAMIX /* Translate back to the sprite screen since processInputProc will translate from sprite screen to screen 0 upon reentry to the DIX layer. */ if (!noPanoramiXExtension) { x += screenInfo.screens[0]->x - screenInfo.screens[screen]->x; y += screenInfo.screens[0]->y - screenInfo.screens[screen]->y; } #endif memset(&ev, 0, sizeof(DeviceEvent)); init_device_event(&ev, pDev, time); ev.root_x = x; ev.root_y = y; ev.type = ET_Motion; ev.time = time; /* FIXME: MD/SD considerations? */ (*pDev->public.processInputProc) ((InternalEvent *) &ev, pDev); }