xserver-multidpi/dix/getevents.c
Peter Hutterer 3304bbff9b Input: Add smooth-scrolling support to GetPointerEvents
For scroll wheel support, we used to send buttons 4/5 and 6/7 for
horizontal/vertical positive/negative scroll events.  For touchpads, we
really want more fine-grained scroll values.  GetPointerEvents now
accepts both old-school scroll button presses, and new-style scroll axis
events, while emitting both types of events to support both old and new
clients.

This works with the new XIScrollClass to mark axes as scrolling axes.
Drivers mark any valuators that send scroll events with SetScrollValuator.
(Currently missing: the XIDeviceChangeEvent being sent when a driver changes
a scroll axis at run-time. This can be added later.)

Note: the SCROLL_TYPE enums are intentionally different values to the XI2
proto values to avoid copy/overlapping range bugs.

Co-authored-by: Daniel Stone <daniel@fooishbar.org>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Daniel Stone <daniel@fooishbar.org>
2011-09-30 09:24:18 +10:00

1484 lines
44 KiB
C

/*
* 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 <daniel@fooishbar.org>
* Peter Hutterer <peter.hutterer@who-t.net>
*/
#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <X11/X.h>
#include <X11/keysym.h>
#include <X11/Xproto.h>
#include <math.h>
#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 <X11/extensions/XKBproto.h>
#include "xkbsrv.h"
#ifdef PANORAMIX
#include "panoramiX.h"
#include "panoramiXsrv.h"
#endif
#include <X11/extensions/XI.h>
#include <X11/extensions/XI2.h>
#include <X11/extensions/XIproto.h>
#include <pixman.h>
#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_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, 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 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 double
rescaleValuatorAxis(double coord, AxisInfoPtr from, AxisInfoPtr to,
double defmax)
{
double fmin = 0.0, fmax = defmax;
double tmin = 0.0, 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)
{
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];
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,
scr->width);
}
if(pDev->valuator->numAxes > 1)
{
pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[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;
}
else
{
double val = pDev->last.valuators[i];
val = rescaleValuatorAxis(val, lastSlave->valuator->axes + i,
pDev->valuator->axes + i, 0);
pDev->last.valuators[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(&current, 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, 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, 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);
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);
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
moveAbsolute(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 mask Valuator data for this event.
*/
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);
}
/**
* 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 scr Screen the device's sprite is currently on.
* @param mask Mask of axis values for this event
* @param screenx Screen x coordinate the sprite is on after the update.
* @param screeny Screen y coordinate the sprite is on after the update.
*/
static void
positionSprite(DeviceIntPtr dev, int mode, ScreenPtr scr, ValuatorMask *mask,
double *screenx, double *screeny)
{
int isx, isy; /* screen {x, y}, in int */
double x, y;
if (!dev->valuator || dev->valuator->numAxes < 2)
return;
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 screen */
*screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL,
scr->width);
*screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL,
scr->height);
/* miPointerSetPosition takes care of crossing screens for us, as well as
* clipping to the current screen. In the event we actually change screen,
* we just drop the float component on the floor, then convert from
* screenx back into device co-ordinates. */
isx = trunc(*screenx);
isy = trunc(*screeny);
miPointerSetPosition(dev, mode, &isx, &isy);
scr = miPointerGetScreen(dev);
if (isx != trunc(*screenx))
{
*screenx -= trunc(*screenx) - isx;
x = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0,
scr->width);
}
if (isy != trunc(*screeny))
{
*screeny -= trunc(*screeny) - isy;
y = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1,
scr->height);
}
/* Update the MD's co-ordinates, which are always in screen space. */
if (!IsMaster(dev) || !IsFloating(dev)) {
DeviceIntPtr master = GetMaster(dev, MASTER_POINTER);
master->last.valuators[0] = *screenx;
master->last.valuators[1] = *screeny;
}
if (valuator_mask_isset(mask, 0))
valuator_mask_set_double(mask, 0, x);
if (valuator_mask_isset(mask, 1))
valuator_mask_set_double(mask, 1, y);
}
/**
* 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_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);
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];
}
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);
}
/**
* 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.
*
* @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, i;
DeviceEvent *event;
RawDeviceEvent *raw;
double screenx = 0.0, screeny = 0.0;
ScreenPtr scr = miPointerGetScreen(pDev);
ValuatorMask mask;
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;
}
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 */
{
double scaled;
if (valuator_mask_isset(&mask, 0))
{
scaled = rescaleValuatorAxis(valuator_mask_get_double(&mask, 0),
NULL, pDev->valuator->axes + 0,
scr->width);
valuator_mask_set_double(&mask, 0, scaled);
}
if (valuator_mask_isset(&mask, 1))
{
scaled = rescaleValuatorAxis(valuator_mask_get_double(&mask, 1),
NULL, pDev->valuator->axes + 1,
scr->height);
valuator_mask_set_double(&mask, 1, scaled);
}
}
transformAbsolute(pDev, &mask);
moveAbsolute(pDev, &mask);
} else {
if (flags & POINTER_ACCELERATE)
accelPointer(pDev, &mask, ms);
moveRelative(pDev, &mask);
}
if ((flags & POINTER_NORAW) == 0)
set_raw_valuators(raw, &mask, raw->valuators.data);
positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative, scr,
&mask, &screenx, &screeny);
updateHistory(pDev, &mask, ms);
clipValuators(pDev, &mask);
for (i = 0; i < valuator_mask_size(&mask); i++)
{
if (valuator_mask_isset(&mask, i))
pDev->last.valuators[i] = valuator_mask_get_double(&mask, i);
}
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 screen co-ordinates */
event->root_x = trunc(screenx);
event->root_y = trunc(screeny);
event->root_x_frac = screenx - trunc(screenx);
event->root_y_frac = screeny - trunc(screeny);
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 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 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;
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 (!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)
{
nev_tmp = fill_pointer_events(events, dev, ButtonPress, b, ms,
POINTER_EMULATED, NULL);
events += nev_tmp;
num_events += nev_tmp;
nev_tmp = fill_pointer_events(events, dev, ButtonRelease, b, ms,
POINTER_EMULATED, 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;
int h_scroll_axis = pDev->valuator->h_scroll_axis;
int v_scroll_axis = pDev->valuator->v_scroll_axis;
ValuatorMask mask;
ValuatorMask scroll;
int i;
/* 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;
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;
}
}
/* 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, 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;
/* refuse events from disabled devices */
if (!pDev->enabled)
return 0;
/* Sanity checks. */
if ((type != ProximityIn && type != ProximityOut) || !mask_in)
return 0;
if (!pDev->valuator)
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;
}
/**
* 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);
}