/* * Copyright © 2006 Nokia Corporation * Copyright © 2006-2007 Daniel Stone * Copyright © 2008 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. * * 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 #include "xkbsrv.h" #ifdef PANORAMIX #include "panoramiX.h" #include "panoramiXsrv.h" #endif #include #include #include #include "exglobals.h" #include "exevents.h" #include "extnsionst.h" #include "listdev.h" /* for sizing up DeviceClassesChangedEvent */ /* 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_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; event->time = ms; event->deviceid = dev->id; event->sourceid = dev->id; event->detail.button = detail; } static void set_raw_valuators(RawDeviceEvent *event, ValuatorMask *mask, int32_t* 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(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(mask, i); event->valuators.data_frac[i] = dev->last.remainder[i] * (1 << 16) * (1 << 16); } 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 type) { int i; DeviceChangedEvent *dce; CARD32 ms = GetTimeInMillis(); dce = &event->changed_event; memset(dce, 0, sizeof(DeviceChangedEvent)); dce->deviceid = slave->id; dce->masterid = master->id; dce->header = ET_Internal; dce->length = sizeof(DeviceChangedEvent); dce->type = ET_DeviceChanged; dce->time = ms; dce->flags = type; dce->flags |= DEVCHANGE_SLAVE_SWITCH; 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; } } 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 int rescaleValuatorAxis(int coord, float remainder, float *remainder_return, AxisInfoPtr from, AxisInfoPtr to, int defmax) { int fmin = 0, tmin = 0, fmax = defmax, tmax = defmax, coord_return; float value; 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) { if (remainder_return) *remainder_return = remainder; return coord; } if(fmax == fmin) { /* avoid division by 0 */ if (remainder_return) *remainder_return = 0.0; return 0; } value = (coord + remainder - fmin) * (tmax - tmin) / (fmax - fmin) + tmin; coord_return = lroundf(value); if (remainder_return) *remainder_return = value - coord_return; return coord_return; } /** * 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) { ScreenPtr scr = miPointerGetScreen(pDev); int i; DeviceIntPtr lastSlave; /* master->last.valuators[0]/[1] is in screen coords and the actual * position of the pointer */ pDev->last.valuators[0] = master->last.valuators[0]; pDev->last.valuators[1] = master->last.valuators[1]; pDev->last.remainder[0] = master->last.remainder[0]; pDev->last.remainder[1] = master->last.remainder[1]; if (!pDev->valuator) return; /* scale back to device coordinates */ if(pDev->valuator->numAxes > 0) pDev->last.valuators[0] = rescaleValuatorAxis(pDev->last.valuators[0], pDev->last.remainder[0], &pDev->last.remainder[0], NULL, pDev->valuator->axes + 0, scr->width); if(pDev->valuator->numAxes > 1) pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1], pDev->last.remainder[1], &pDev->last.remainder[1], NULL, pDev->valuator->axes + 1, scr->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; pDev->last.remainder[i] = 0; } else { pDev->last.valuators[i] = rescaleValuatorAxis(pDev->last.valuators[i], pDev->last.remainder[i], &pDev->last.remainder[i], lastSlave->valuator->axes + i, pDev->valuator->axes + i, 0); } } } } /** * 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; int dflt; 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, 0.0, NULL, &from, to, 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, 0.0, NULL, &from, to, 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; if (j == 0 && (to->max_value < to->min_value)) dflt = pScreen->width; else if (j == 1 && (to->max_value < to->min_value)) dflt = pScreen->height; else dflt = 0; /* scale from stored range into current range */ coord = rescaleValuatorAxis(coord, 0.0, NULL, &from, to, 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, int *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++) { /* 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); memcpy(buff, &valuators[i], 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++) { if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) { buff += sizeof(INT32); continue; } memcpy(buff, &valuators[i], 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 */ return 3; } /** * Clip an axis to its bounds, which are declared in the call to * InitValuatorAxisClassStruct. */ static void clipAxis(DeviceIntPtr pDev, int axisNum, int *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)) { int val = valuator_mask_get(mask, i); clipAxis(pDev, i, &val); valuator_mask_set(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); 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 which's pointer is to be moved. * @param x Returns the x position of the pointer after the move. * @param y Returns the y position of the pointer after the move. * @param mask Bit mask of valid valuators. * @param valuators Valuator data for each axis between @first and * @first+@num. */ static void moveAbsolute(DeviceIntPtr dev, int *x, int *y, ValuatorMask *mask) { int i; if (valuator_mask_isset(mask, 0)) *x = valuator_mask_get(mask, 0); else *x = dev->last.valuators[0]; if (valuator_mask_isset(mask, 1)) *y = valuator_mask_get(mask, 1); else *y = dev->last.valuators[1]; clipAxis(dev, 0, x); clipAxis(dev, 1, y); for (i = 2; i < valuator_mask_size(mask); i++) { if (valuator_mask_isset(mask, i)) { dev->last.valuators[i] = valuator_mask_get(mask, i); clipAxis(dev, i, &dev->last.valuators[i]); } } } /** * Move the device's pointer by the values given in @valuators. * * @param dev The device which's pointer is to be moved. * @param x Returns the x position of the pointer after the move. * @param y Returns the y position of the pointer after the move. * @param mask Bit mask of valid valuators. * @param valuators Valuator data for each axis between @first and * @first+@num. */ static void moveRelative(DeviceIntPtr dev, int *x, int *y, ValuatorMask *mask) { int i; *x = dev->last.valuators[0]; *y = dev->last.valuators[1]; if (valuator_mask_isset(mask, 0)) *x += valuator_mask_get(mask, 0); if (valuator_mask_isset(mask, 1)) *y += valuator_mask_get(mask, 1); /* if attached, clip both x and y to the defined limits (usually * co-ord space limit). If it is attached, we need x/y to go over the * limits to be able to change screens. */ if (dev->valuator && (IsMaster(dev) || !IsFloating(dev))) { if (valuator_get_mode(dev, 0) == Absolute) clipAxis(dev, 0, x); if (valuator_get_mode(dev, 1) == Absolute) clipAxis(dev, 1, y); } /* calc other axes, clip, drop back into valuators */ for (i = 2; i < valuator_mask_size(mask); i++) { if (valuator_mask_isset(mask, i)) { dev->last.valuators[i] += valuator_mask_get(mask, i); if (valuator_get_mode(dev, i) == Absolute) clipAxis(dev, i, &dev->last.valuators[i]); valuator_mask_set(mask, i, dev->last.valuators[i]); } } } /** * 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); } /** * If we have HW cursors, this actually moves the visible sprite. If not, we * just do all the screen crossing, etc. * * 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 whether * it was relative or absolute movement that landed us at those coordinates. * * @param dev The device to be moved. * @param mode Movement mode (Absolute or Relative) * @param x Pointer to current x-axis value, may be modified. * @param y Pointer to current y-axis value, may be modified. * @param x_frac Fractional part of current x-axis value, may be modified. * @param y_frac Fractional part of current y-axis value, may be modified. * @param scr Screen the device's sprite is currently on. * @param screenx Screen x coordinate the sprite is on after the update. * @param screeny Screen y coordinate the sprite is on after the update. * @param screenx_frac Fractional part of screen x coordinate, as above. * @param screeny_frac Fractional part of screen y coordinate, as above. */ static void positionSprite(DeviceIntPtr dev, int mode, int *x, int *y, float x_frac, float y_frac, ScreenPtr scr, int *screenx, int *screeny, float *screenx_frac, float *screeny_frac) { int old_screenx, old_screeny; /* scale x&y to screen */ if (dev->valuator && dev->valuator->numAxes > 0) { *screenx = rescaleValuatorAxis(*x, x_frac, screenx_frac, dev->valuator->axes + 0, NULL, scr->width); } else { *screenx = dev->last.valuators[0]; *screenx_frac = dev->last.remainder[0]; } if (dev->valuator && dev->valuator->numAxes > 1) { *screeny = rescaleValuatorAxis(*y, y_frac, screeny_frac, dev->valuator->axes + 1, NULL, scr->height); } else { *screeny = dev->last.valuators[1]; *screeny_frac = dev->last.remainder[1]; } /* Hit the left screen edge? */ if (*screenx <= 0 && *screenx_frac < 0.0f) { *screenx_frac = 0.0f; x_frac = 0.0f; } if (*screeny <= 0 && *screeny_frac < 0.0f) { *screeny_frac = 0.0f; y_frac = 0.0f; } old_screenx = *screenx; old_screeny = *screeny; /* This takes care of crossing screens for us, as well as clipping * to the current screen. */ miPointerSetPosition(dev, mode, screenx, screeny); if(!IsMaster(dev) && !IsFloating(dev)) { DeviceIntPtr master = GetMaster(dev, MASTER_POINTER); master->last.valuators[0] = *screenx; master->last.valuators[1] = *screeny; master->last.remainder[0] = *screenx_frac; master->last.remainder[1] = *screeny_frac; } if (dev->valuator) { /* Crossed screen? Scale back to device coordiantes */ if(*screenx != old_screenx) { scr = miPointerGetScreen(dev); *x = rescaleValuatorAxis(*screenx, *screenx_frac, &x_frac, NULL, dev->valuator->axes + 0, scr->width); } if(*screeny != old_screeny) { scr = miPointerGetScreen(dev); *y = rescaleValuatorAxis(*screeny, *screeny_frac, &y_frac, NULL, dev->valuator->axes + 1, scr->height); } } /* dropy x/y (device coordinates) back into valuators for next event */ dev->last.valuators[0] = *x; dev->last.valuators[1] = *y; dev->last.remainder[0] = x_frac; dev->last.remainder[1] = y_frac; } /** * 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]); } /** * 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; /* 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; 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_event(pDev, event, 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); 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, int *x, int *y) { struct pixman_f_vector p = {.v = {*x, *y, 1}}; pixman_f_transform_point(m, &p); *x = lround(p.v[0]); *y = lround(p.v[1]); } static void transformAbsolute(DeviceIntPtr dev, ValuatorMask *mask) { int x, y, ox, oy; ox = x = valuator_mask_isset(mask, 0) ? valuator_mask_get(mask, 0) : dev->last.valuators[0]; oy = y = valuator_mask_isset(mask, 1) ? valuator_mask_get(mask, 1) : dev->last.valuators[1]; transform(&dev->transform, &x, &y); if (valuator_mask_isset(mask, 0) || ox != x) valuator_mask_set(mask, 0, x); if (valuator_mask_isset(mask, 1) || oy != y) valuator_mask_set(mask, 1, y); } /** * 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); } /** * Generate a series of InternalEvents representing pointer motion, or * button presses. * * The DDX is responsible for allocating the events 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. * * @return the number of events written into events. */ int GetPointerEvents(InternalEvent *events, DeviceIntPtr pDev, int type, int buttons, int flags, const ValuatorMask *mask_in) { int num_events = 1; CARD32 ms; DeviceEvent *event; RawDeviceEvent *raw; int x = 0, y = 0, /* device coords */ cx, cy; /* only screen coordinates */ float x_frac = 0.0, y_frac = 0.0, cx_frac, cy_frac; ScreenPtr scr = miPointerGetScreen(pDev); ValuatorMask mask; /* refuse events from disabled devices */ if (!pDev->enabled) return 0; if (!scr) return 0; switch (type) { case MotionNotify: if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0) return 0; break; case ButtonPress: case ButtonRelease: if (!pDev->button || !buttons) return 0; break; default: return 0; } ms = GetTimeInMillis(); /* before pointer update to help precision */ events = UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT, &num_events); 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); } if (flags & POINTER_ABSOLUTE) { if (flags & POINTER_SCREEN) /* valuators are in screen coords */ { int scaled; if (valuator_mask_isset(&mask, 0)) { scaled = rescaleValuatorAxis(valuator_mask_get(&mask, 0), 0.0, &x_frac, NULL, pDev->valuator->axes + 0, scr->width); valuator_mask_set(&mask, 0, scaled); } if (valuator_mask_isset(&mask, 1)) { scaled = rescaleValuatorAxis(valuator_mask_get(&mask, 1), 0.0, &y_frac, NULL, pDev->valuator->axes + 1, scr->height); valuator_mask_set(&mask, 1, scaled); } } transformAbsolute(pDev, &mask); moveAbsolute(pDev, &x, &y, &mask); } else { if (flags & POINTER_ACCELERATE) { accelPointer(pDev, &mask, ms); /* The pointer acceleration code modifies the fractional part * in-place, so we need to extract this information first */ x_frac = pDev->last.remainder[0]; y_frac = pDev->last.remainder[1]; } moveRelative(pDev, &x, &y, &mask); } if ((flags & POINTER_NORAW) == 0) set_raw_valuators(raw, &mask, raw->valuators.data); positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, &x, &y, x_frac, y_frac, scr, &cx, &cy, &cx_frac, &cy_frac); updateHistory(pDev, &mask, ms); /* Update the valuators with the true value sent to the client*/ if (valuator_mask_isset(&mask, 0)) valuator_mask_set(&mask, 0, x); if (valuator_mask_isset(&mask, 1)) valuator_mask_set(&mask, 1, y); clipValuators(pDev, &mask); event = &events->device_event; init_event(pDev, event, 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; } event->root_x = cx; /* root_x/y always in screen coords */ event->root_y = cy; event->root_x_frac = cx_frac; event->root_y_frac = cy_frac; set_valuators(pDev, event, &mask); 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; /* 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_event(pDev, event, GetTimeInMillis()); event->type = (type == ProximityIn) ? ET_ProximityIn : ET_ProximityOut; clipValuators(pDev, &mask); set_valuators(pDev, event, &mask); 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_event(pDev, &ev, 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); }