xserver-multidpi/dix/getevents.c

1614 lines
49 KiB
C
Raw Normal View History

/*
* Copyright © 2006 Nokia Corporation
* Copyright © 2006-2007 Daniel Stone
* Copyright © 2008 Red Hat, Inc.
* Copyright © 2011 The Chromium Authors
2006-10-19 23:44:46 +02:00
*
* 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>
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
#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>
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
#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 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)
{
ScreenPtr scr = miPointerGetScreen(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,
0, scr->width);
}
if(pDev->valuator->numAxes > 1)
{
pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1],
NULL,
pDev->valuator->axes + 1,
0, 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, 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;
2006-10-19 23:44:46 +02:00
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, 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)
2008-04-30 05:47:14 +02:00
*val = axis->min_value;
if (*val > axis->max_value)
2008-04-30 05:47:14 +02:00
*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);
}
}
/**
* 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
* 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[in,out] mask Mask of axis values for this event, returns the
* per-screen device coordinates after confinement
* @param[out] devx x desktop-wide coordinate in device coordinate system
* @param[out] devy y desktop-wide coordinate in device coordinate system
* @param[out] screenx x coordinate in desktop coordinate system
* @param[out] screeny y coordinate in desktop coordinate system
*/
static ScreenPtr
positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask,
double *devx, double *devy,
double *screenx, double *screeny)
{
double x, y;
double tmpx, tmpy;
ScreenPtr scr = miPointerGetScreen(dev);
if (!dev->valuator || dev->valuator->numAxes < 2)
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);
tmpx = *screenx;
tmpy = *screeny;
*devx = x;
*devy = y;
/* 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)) {
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)) {
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;
/* 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;
xkb: post-fix PointerKeys button events with a DeviceChangedEvent. commit 14327858391ebe929b806efb53ad79e789361883 xkb: release XTEST pointer buttons on physical releases. (#28808) revealed a bug with the XTEST/PointerKeys interaction. Events resulting from PointerKeys are injected into the event processing stream, not appended to the event queue. The events generated for the fake button press include a DeviceChangedEvent (DCE), a raw button event and the button event itself. The DCE causes the master to switch classes to the attached XTEST pointer device. Once the fake button is processed, normal event processing continues with events in the EQ. The master still contains the XTEST classes, causing some events to be dropped if e.g. the number of valuators of the event in the queue exceeds the XTEST device's number of valuators. Example: the EQ contains the following events, processed one-by-one, left to right. [DCE (dev)][Btn down][Btn up][Motion][Motion][...] ^ XkbFakeDeviceButton injects [DCE (XTEST)][Btn up] Thus the event sequence processed looks like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][Motion][Motion][...] The first DCE causes the master to switch to the device. The button up event injects a DCE to the XTEST device, causing the following Motion events to be processed with the master still being on XTEST classes. This patch post-fixes the injected event sequence with a DCE to restore the classes of the original slave device, resulting in an event sequence like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][DCE (dev)][Motion][Motion] Note that this is a simplified description. The event sequence injected by the PointerKeys code is injected for the master device only and the matching slave device that caused the injection has already finished processing on the slave. Furthermore, the injection happens as part of the the XKB layer, before the unwrapping of the processInputProc takes us into the DIX where the DCE is actually handled. Bug reproducible with a device that reports more than 2 valuators. Simply cause button releases on the device and wait for a "too many valuators" warning message. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Acked-by: Daniel Stone <daniel@fooishbar.org> Reviewed-by: Keith Packard <keithp@keithp.com>
2010-07-23 03:46:30 +02:00
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.
*/
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
static void
transform(struct pixman_f_transform *m, double *x, double *y)
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
{
struct pixman_f_vector p = {.v = {*x, *y, 1}};
pixman_f_transform_point(m, &p);
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
*x = p.v[0];
*y = p.v[1];
dix: add 3x3 transformation matrix xinput property for multi-head handling For absolute input devices (E.G. touchscreens) in multi-head setups, we need a way to bind the device to an randr output. This adds the infrastructure to the server to allow us to do so. positionSprite() scales input coordinates to the dimensions of the shared (total) screen frame buffer, so to restrict motion to an output we need to scale/rotate/translate device coordinates to a subset of the frame buffer before passing them on to positionSprite. This is done here using a 3x3 transformation matrix, which is applied to the device coordinates using homogeneous coordinates, E.G.: [ c0 c1 c2 ] [ x ] [ c3 c4 c5 ] * [ y ] [ c6 c7 c8 ] [ 1 ] Notice: As input devices have varying input ranges, the coordinates are first scaled to the [0..1] range for generality, and afterwards scaled back up. E.G. for a dual head setup (using same resolution) next to each other, you would want to scale the X coordinates of the touchscreen connected to the both heads by 50%, and translate (offset) the coordinates of the rightmost head by 50%, or in matrix form: left: right: [ 0.5 0 0 ] [ 0.5 0 0.5 ] [ 0 1 0 ] [ 0 1 0 ] [ 0 0 1 ] [ 0 0 0 ] Which can be done using xinput: xinput set-prop <left> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0 0 1 0 0 0 1 xinput set-prop <right> --type=float "Coordinate Transformation Matrix" \ 0.5 0 0.5 0 1 0 0 0 1 Likewise more complication setups involving more heads, rotation or different resolution can be handled. Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com> Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
2010-05-25 11:03:28 +02:00
}
/**
* 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);
}
/**
* 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, i;
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);
} else {
if (flags & POINTER_ACCELERATE)
accelPointer(pDev, &mask, ms);
moveRelative(pDev, &mask);
}
/* valuators are in device coordinate system in absolute coordinates */
if ((flags & POINTER_NORAW) == 0)
set_raw_valuators(raw, &mask, raw->valuators.data);
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);
/* store desktop-wide in last.valuators */
if (valuator_mask_isset(&mask, 0))
pDev->last.valuators[0] = devx;
if (valuator_mask_isset(&mask, 1))
pDev->last.valuators[1] = devy;
for (i = 2; i < valuator_mask_size(&mask); i++)
{
if (valuator_mask_isset(&mask, i))
pDev->last.valuators[i] = valuator_mask_get_double(&mask, i);
}
/* 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;
int h_scroll_axis = pDev->valuator->h_scroll_axis;
int v_scroll_axis = pDev->valuator->v_scroll_axis;
ValuatorMask mask;
ValuatorMask scroll;
int i;
int realtype = type;
/* 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;
/* 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;
/* 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. */
xkb: post-fix PointerKeys button events with a DeviceChangedEvent. commit 14327858391ebe929b806efb53ad79e789361883 xkb: release XTEST pointer buttons on physical releases. (#28808) revealed a bug with the XTEST/PointerKeys interaction. Events resulting from PointerKeys are injected into the event processing stream, not appended to the event queue. The events generated for the fake button press include a DeviceChangedEvent (DCE), a raw button event and the button event itself. The DCE causes the master to switch classes to the attached XTEST pointer device. Once the fake button is processed, normal event processing continues with events in the EQ. The master still contains the XTEST classes, causing some events to be dropped if e.g. the number of valuators of the event in the queue exceeds the XTEST device's number of valuators. Example: the EQ contains the following events, processed one-by-one, left to right. [DCE (dev)][Btn down][Btn up][Motion][Motion][...] ^ XkbFakeDeviceButton injects [DCE (XTEST)][Btn up] Thus the event sequence processed looks like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][Motion][Motion][...] The first DCE causes the master to switch to the device. The button up event injects a DCE to the XTEST device, causing the following Motion events to be processed with the master still being on XTEST classes. This patch post-fixes the injected event sequence with a DCE to restore the classes of the original slave device, resulting in an event sequence like this: [DCE (dev)][Btn down][Btn up][DCE (XTEST)][Btn up][DCE (dev)][Motion][Motion] Note that this is a simplified description. The event sequence injected by the PointerKeys code is injected for the master device only and the matching slave device that caused the injection has already finished processing on the slave. Furthermore, the injection happens as part of the the XKB layer, before the unwrapping of the processInputProc takes us into the DIX where the DCE is actually handled. Bug reproducible with a device that reports more than 2 valuators. Simply cause button releases on the device and wait for a "too many valuators" warning message. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Acked-by: Daniel Stone <daniel@fooishbar.org> Reviewed-by: Keith Packard <keithp@keithp.com>
2010-07-23 03:46:30 +02:00
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);
}