You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2240 lines
70 KiB
C
2240 lines
70 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 <limits.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 "windowstr.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 */
|
|
#include "probes.h"
|
|
|
|
/* 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_touch_ownership(DeviceIntPtr dev, TouchOwnershipEvent *event, Time ms)
|
|
{
|
|
memset(event, 0, sizeof(TouchOwnershipEvent));
|
|
event->header = ET_Internal;
|
|
event->type = ET_TouchOwnership;
|
|
event->length = sizeof(TouchOwnershipEvent);
|
|
event->time = ms;
|
|
event->deviceid = dev->id;
|
|
}
|
|
|
|
static void
|
|
init_raw(DeviceIntPtr dev, RawDeviceEvent *event, Time ms, int type, int detail)
|
|
{
|
|
memset(event, 0, sizeof(RawDeviceEvent));
|
|
event->header = ET_Internal;
|
|
event->length = sizeof(RawDeviceEvent);
|
|
switch (type) {
|
|
case MotionNotify:
|
|
event->type = ET_RawMotion;
|
|
break;
|
|
case ButtonPress:
|
|
event->type = ET_RawButtonPress;
|
|
break;
|
|
case ButtonRelease:
|
|
event->type = ET_RawButtonRelease;
|
|
break;
|
|
case KeyPress:
|
|
event->type = ET_RawKeyPress;
|
|
break;
|
|
case KeyRelease:
|
|
event->type = ET_RawKeyRelease;
|
|
break;
|
|
case XI_TouchBegin:
|
|
event->type = ET_RawTouchBegin;
|
|
break;
|
|
case XI_TouchUpdate:
|
|
event->type = ET_RawTouchUpdate;
|
|
break;
|
|
case XI_TouchEnd:
|
|
event->type = ET_RawTouchEnd;
|
|
break;
|
|
}
|
|
event->time = ms;
|
|
event->deviceid = dev->id;
|
|
event->sourceid = dev->id;
|
|
event->detail.button = detail;
|
|
}
|
|
|
|
static void
|
|
set_raw_valuators(RawDeviceEvent *event, ValuatorMask *mask,
|
|
BOOL use_unaccel, double *data)
|
|
{
|
|
int i;
|
|
|
|
use_unaccel = use_unaccel && valuator_mask_has_unaccelerated(mask);
|
|
|
|
for (i = 0; i < valuator_mask_size(mask); i++) {
|
|
if (valuator_mask_isset(mask, i)) {
|
|
double v;
|
|
|
|
SetBit(event->valuators.mask, i);
|
|
|
|
if (use_unaccel)
|
|
v = valuator_mask_get_unaccelerated(mask, i);
|
|
else
|
|
v = valuator_mask_get_double(mask, i);
|
|
|
|
data[i] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
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;
|
|
dce->valuators[i].value = slave->valuator->axisVal[i];
|
|
}
|
|
}
|
|
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 + 1;
|
|
}
|
|
if (to && to->min_value < to->max_value) {
|
|
tmin = to->min_value;
|
|
tmax = to->max_value + 1;
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* master->last.valuators[0]/[1] is in desktop-wide coords and the actual
|
|
* position of the pointer */
|
|
pDev->last.valuators[0] = master->last.valuators[0];
|
|
pDev->last.valuators[1] = master->last.valuators[1];
|
|
|
|
if (!pDev->valuator)
|
|
return;
|
|
|
|
/* scale back to device coordinates */
|
|
if (pDev->valuator->numAxes > 0) {
|
|
pDev->last.valuators[0] = rescaleValuatorAxis(pDev->last.valuators[0],
|
|
NULL,
|
|
pDev->valuator->axes + 0,
|
|
screenInfo.x,
|
|
screenInfo.width);
|
|
}
|
|
if (pDev->valuator->numAxes > 1) {
|
|
pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1],
|
|
NULL,
|
|
pDev->valuator->axes + 1,
|
|
screenInfo.y,
|
|
screenInfo.height);
|
|
}
|
|
|
|
/* other axes are left as-is */
|
|
}
|
|
|
|
/**
|
|
* Allocate the motion history buffer.
|
|
*/
|
|
void
|
|
AllocateMotionHistory(DeviceIntPtr pDev)
|
|
{
|
|
int size;
|
|
|
|
free(pDev->valuator->motion);
|
|
|
|
if (pDev->valuator->numMotionEvents < 1)
|
|
return;
|
|
|
|
/* An MD must have a motion history size large enough to keep all
|
|
* potential valuators, plus the respective range of the valuators.
|
|
* 3 * INT32 for (min_val, max_val, curr_val))
|
|
*/
|
|
if (IsMaster(pDev))
|
|
size = sizeof(INT32) * 3 * MAX_VALUATORS;
|
|
else {
|
|
ValuatorClassPtr v = pDev->valuator;
|
|
int numAxes;
|
|
|
|
/* XI1 doesn't understand mixed mode devices */
|
|
for (numAxes = 0; numAxes < v->numAxes; numAxes++)
|
|
if (valuator_get_mode(pDev, numAxes) != valuator_get_mode(pDev, 0))
|
|
break;
|
|
size = sizeof(INT32) * numAxes;
|
|
}
|
|
|
|
size += sizeof(Time);
|
|
|
|
pDev->valuator->motion = calloc(pDev->valuator->numMotionEvents, size);
|
|
pDev->valuator->first_motion = 0;
|
|
pDev->valuator->last_motion = 0;
|
|
if (!pDev->valuator->motion)
|
|
ErrorF("[dix] %s: Failed to alloc motion history (%d bytes).\n",
|
|
pDev->name, size * pDev->valuator->numMotionEvents);
|
|
}
|
|
|
|
/**
|
|
* Dump the motion history between start and stop into the supplied buffer.
|
|
* Only records the event for a given screen in theory, but in practice, we
|
|
* sort of ignore this.
|
|
*
|
|
* If core is set, we only generate x/y, in INT16, scaled to screen coords.
|
|
*/
|
|
int
|
|
GetMotionHistory(DeviceIntPtr pDev, xTimecoord ** buff, unsigned long start,
|
|
unsigned long stop, ScreenPtr pScreen, BOOL core)
|
|
{
|
|
char *ibuff = NULL, *obuff;
|
|
int i = 0, ret = 0;
|
|
int j, coord;
|
|
Time current;
|
|
|
|
/* The size of a single motion event. */
|
|
int size;
|
|
AxisInfo from, *to; /* for scaling */
|
|
INT32 *ocbuf, *icbuf; /* pointer to coordinates for copying */
|
|
INT16 *corebuf;
|
|
AxisInfo core_axis = { 0 };
|
|
|
|
if (!pDev->valuator || !pDev->valuator->numMotionEvents)
|
|
return 0;
|
|
|
|
if (core && !pScreen)
|
|
return 0;
|
|
|
|
if (IsMaster(pDev))
|
|
size = (sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(Time);
|
|
else
|
|
size = (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);
|
|
|
|
*buff = malloc(size * pDev->valuator->numMotionEvents);
|
|
if (!(*buff))
|
|
return 0;
|
|
obuff = (char *) *buff;
|
|
|
|
for (i = pDev->valuator->first_motion;
|
|
i != pDev->valuator->last_motion;
|
|
i = (i + 1) % pDev->valuator->numMotionEvents) {
|
|
/* We index the input buffer by which element we're accessing, which
|
|
* is not monotonic, and the output buffer by how many events we've
|
|
* written so far. */
|
|
ibuff = (char *) pDev->valuator->motion + (i * size);
|
|
memcpy(¤t, ibuff, sizeof(Time));
|
|
|
|
if (current > stop) {
|
|
return ret;
|
|
}
|
|
else if (current >= start) {
|
|
if (core) {
|
|
memcpy(obuff, ibuff, sizeof(Time)); /* copy timestamp */
|
|
|
|
icbuf = (INT32 *) (ibuff + sizeof(Time));
|
|
corebuf = (INT16 *) (obuff + sizeof(Time));
|
|
|
|
/* fetch x coordinate + range */
|
|
memcpy(&from.min_value, icbuf++, sizeof(INT32));
|
|
memcpy(&from.max_value, icbuf++, sizeof(INT32));
|
|
memcpy(&coord, icbuf++, sizeof(INT32));
|
|
|
|
/* scale to screen coords */
|
|
to = &core_axis;
|
|
to->max_value = pScreen->width;
|
|
coord =
|
|
rescaleValuatorAxis(coord, &from, to, 0, pScreen->width);
|
|
|
|
memcpy(corebuf, &coord, sizeof(INT16));
|
|
corebuf++;
|
|
|
|
/* fetch y coordinate + range */
|
|
memcpy(&from.min_value, icbuf++, sizeof(INT32));
|
|
memcpy(&from.max_value, icbuf++, sizeof(INT32));
|
|
memcpy(&coord, icbuf++, sizeof(INT32));
|
|
|
|
to->max_value = pScreen->height;
|
|
coord =
|
|
rescaleValuatorAxis(coord, &from, to, 0, pScreen->height);
|
|
memcpy(corebuf, &coord, sizeof(INT16));
|
|
|
|
}
|
|
else if (IsMaster(pDev)) {
|
|
memcpy(obuff, ibuff, sizeof(Time)); /* copy timestamp */
|
|
|
|
ocbuf = (INT32 *) (obuff + sizeof(Time));
|
|
icbuf = (INT32 *) (ibuff + sizeof(Time));
|
|
for (j = 0; j < MAX_VALUATORS; j++) {
|
|
if (j >= pDev->valuator->numAxes)
|
|
break;
|
|
|
|
/* fetch min/max/coordinate */
|
|
memcpy(&from.min_value, icbuf++, sizeof(INT32));
|
|
memcpy(&from.max_value, icbuf++, sizeof(INT32));
|
|
memcpy(&coord, icbuf++, sizeof(INT32));
|
|
|
|
to = (j <
|
|
pDev->valuator->numAxes) ? &pDev->valuator->
|
|
axes[j] : NULL;
|
|
|
|
/* x/y scaled to screen if no range is present */
|
|
if (j == 0 && (from.max_value < from.min_value))
|
|
from.max_value = pScreen->width;
|
|
else if (j == 1 && (from.max_value < from.min_value))
|
|
from.max_value = pScreen->height;
|
|
|
|
/* scale from stored range into current range */
|
|
coord = rescaleValuatorAxis(coord, &from, to, 0, 0);
|
|
memcpy(ocbuf, &coord, sizeof(INT32));
|
|
ocbuf++;
|
|
}
|
|
}
|
|
else
|
|
memcpy(obuff, ibuff, size);
|
|
|
|
/* don't advance by size here. size may be different to the
|
|
* actually written size if the MD has less valuators than MAX */
|
|
if (core)
|
|
obuff += sizeof(INT32) + sizeof(Time);
|
|
else
|
|
obuff +=
|
|
(sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);
|
|
ret++;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Update the motion history for a specific device, with the list of
|
|
* valuators.
|
|
*
|
|
* Layout of the history buffer:
|
|
* for SDs: [time] [val0] [val1] ... [valn]
|
|
* for MDs: [time] [min_val0] [max_val0] [val0] [min_val1] ... [valn]
|
|
*
|
|
* For events that have some valuators unset:
|
|
* min_val == max_val == val == 0.
|
|
*/
|
|
static void
|
|
updateMotionHistory(DeviceIntPtr pDev, CARD32 ms, ValuatorMask *mask,
|
|
double *valuators)
|
|
{
|
|
char *buff = (char *) pDev->valuator->motion;
|
|
ValuatorClassPtr v;
|
|
int i;
|
|
|
|
if (!pDev->valuator->numMotionEvents)
|
|
return;
|
|
|
|
v = pDev->valuator;
|
|
if (IsMaster(pDev)) {
|
|
buff += ((sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(CARD32)) *
|
|
v->last_motion;
|
|
|
|
memcpy(buff, &ms, sizeof(Time));
|
|
buff += sizeof(Time);
|
|
|
|
memset(buff, 0, sizeof(INT32) * 3 * MAX_VALUATORS);
|
|
|
|
for (i = 0; i < v->numAxes; i++) {
|
|
int val;
|
|
|
|
/* XI1 doesn't support mixed mode devices */
|
|
if (valuator_get_mode(pDev, i) != valuator_get_mode(pDev, 0))
|
|
break;
|
|
if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) {
|
|
buff += 3 * sizeof(INT32);
|
|
continue;
|
|
}
|
|
memcpy(buff, &v->axes[i].min_value, sizeof(INT32));
|
|
buff += sizeof(INT32);
|
|
memcpy(buff, &v->axes[i].max_value, sizeof(INT32));
|
|
buff += sizeof(INT32);
|
|
val = valuators[i];
|
|
memcpy(buff, &val, sizeof(INT32));
|
|
buff += sizeof(INT32);
|
|
}
|
|
}
|
|
else {
|
|
|
|
buff += ((sizeof(INT32) * pDev->valuator->numAxes) + sizeof(CARD32)) *
|
|
pDev->valuator->last_motion;
|
|
|
|
memcpy(buff, &ms, sizeof(Time));
|
|
buff += sizeof(Time);
|
|
|
|
memset(buff, 0, sizeof(INT32) * pDev->valuator->numAxes);
|
|
|
|
for (i = 0; i < MAX_VALUATORS; i++) {
|
|
int val;
|
|
|
|
if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) {
|
|
buff += sizeof(INT32);
|
|
continue;
|
|
}
|
|
val = valuators[i];
|
|
memcpy(buff, &val, sizeof(INT32));
|
|
buff += sizeof(INT32);
|
|
}
|
|
}
|
|
|
|
pDev->valuator->last_motion = (pDev->valuator->last_motion + 1) %
|
|
pDev->valuator->numMotionEvents;
|
|
/* If we're wrapping around, just keep the circular buffer going. */
|
|
if (pDev->valuator->first_motion == pDev->valuator->last_motion)
|
|
pDev->valuator->first_motion = (pDev->valuator->first_motion + 1) %
|
|
pDev->valuator->numMotionEvents;
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Returns the maximum number of events GetKeyboardEvents
|
|
* and GetPointerEvents will ever return.
|
|
*
|
|
* This MUST be absolutely constant, from init until exit.
|
|
*/
|
|
int
|
|
GetMaximumEventsNum(void)
|
|
{
|
|
/* One raw event
|
|
* One device event
|
|
* One possible device changed event
|
|
* Lots of possible separate button scroll events (horiz + vert)
|
|
* Lots of possible separate raw button scroll events (horiz + vert)
|
|
*/
|
|
return 100;
|
|
}
|
|
|
|
/**
|
|
* Clip an axis to its bounds, which are declared in the call to
|
|
* InitValuatorAxisClassStruct.
|
|
*/
|
|
static void
|
|
clipAxis(DeviceIntPtr pDev, int axisNum, double *val)
|
|
{
|
|
AxisInfoPtr axis;
|
|
|
|
if (axisNum >= pDev->valuator->numAxes)
|
|
return;
|
|
|
|
axis = pDev->valuator->axes + axisNum;
|
|
|
|
/* If a value range is defined, clip. If not, do nothing */
|
|
if (axis->max_value <= axis->min_value)
|
|
return;
|
|
|
|
if (*val < axis->min_value)
|
|
*val = axis->min_value;
|
|
if (*val > axis->max_value)
|
|
*val = axis->max_value;
|
|
}
|
|
|
|
/**
|
|
* Clip every axis in the list of valuators to its bounds.
|
|
*/
|
|
static void
|
|
clipValuators(DeviceIntPtr pDev, ValuatorMask *mask)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < valuator_mask_size(mask); i++)
|
|
if (valuator_mask_isset(mask, i)) {
|
|
double val = valuator_mask_get_double(mask, i);
|
|
|
|
clipAxis(pDev, i, &val);
|
|
valuator_mask_set_double(mask, i, val);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create the DCCE event (does not update the master's device state yet, this
|
|
* is done in the event processing).
|
|
* Pull in the coordinates from the MD if necessary.
|
|
*
|
|
* @param events Pointer to a pre-allocated event array.
|
|
* @param dev The slave device that generated an event.
|
|
* @param type Either DEVCHANGE_POINTER_EVENT and/or DEVCHANGE_KEYBOARD_EVENT
|
|
* @param num_events The current number of events, returns the number of
|
|
* events if a DCCE was generated.
|
|
* @return The updated @events pointer.
|
|
*/
|
|
InternalEvent *
|
|
UpdateFromMaster(InternalEvent *events, DeviceIntPtr dev, int type,
|
|
int *num_events)
|
|
{
|
|
DeviceIntPtr master;
|
|
|
|
master =
|
|
GetMaster(dev,
|
|
(type & DEVCHANGE_POINTER_EVENT) ? MASTER_POINTER :
|
|
MASTER_KEYBOARD);
|
|
|
|
if (master && master->last.slave != dev) {
|
|
CreateClassesChangedEvent(events, master, dev,
|
|
type | DEVCHANGE_SLAVE_SWITCH);
|
|
if (IsPointerDevice(master)) {
|
|
updateSlaveDeviceCoords(master, dev);
|
|
master->last.numValuators = dev->last.numValuators;
|
|
}
|
|
master->last.slave = dev;
|
|
(*num_events)++;
|
|
events++;
|
|
}
|
|
return events;
|
|
}
|
|
|
|
/**
|
|
* Move the device's pointer to the position given in the valuators.
|
|
*
|
|
* @param dev The device whose pointer is to be moved.
|
|
* @param mask Valuator data for this event.
|
|
*/
|
|
static void
|
|
clipAbsolute(DeviceIntPtr dev, ValuatorMask *mask)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < valuator_mask_size(mask); i++) {
|
|
double val;
|
|
|
|
if (!valuator_mask_isset(mask, i))
|
|
continue;
|
|
val = valuator_mask_get_double(mask, i);
|
|
clipAxis(dev, i, &val);
|
|
valuator_mask_set_double(mask, i, val);
|
|
}
|
|
}
|
|
|
|
static void
|
|
add_to_scroll_valuator(DeviceIntPtr dev, ValuatorMask *mask, int valuator, double value)
|
|
{
|
|
double v;
|
|
|
|
if (!valuator_mask_fetch_double(mask, valuator, &v))
|
|
return;
|
|
|
|
/* protect against scrolling overflow. INT_MAX for double, because
|
|
* we'll eventually write this as 32.32 fixed point */
|
|
if ((value > 0 && v > INT_MAX - value) || (value < 0 && v < INT_MIN - value)) {
|
|
v = 0;
|
|
|
|
/* reset last.scroll to avoid a button storm */
|
|
valuator_mask_set_double(dev->last.scroll, valuator, 0);
|
|
}
|
|
else
|
|
v += value;
|
|
|
|
valuator_mask_set_double(mask, valuator, v);
|
|
}
|
|
|
|
|
|
static void
|
|
scale_for_device_resolution(DeviceIntPtr dev, ValuatorMask *mask)
|
|
{
|
|
double y;
|
|
ValuatorClassPtr v = dev->valuator;
|
|
int xrange = v->axes[0].max_value - v->axes[0].min_value + 1;
|
|
int yrange = v->axes[1].max_value - v->axes[1].min_value + 1;
|
|
|
|
double screen_ratio = 1.0 * screenInfo.width/screenInfo.height;
|
|
double device_ratio = 1.0 * xrange/yrange;
|
|
double resolution_ratio = 1.0;
|
|
double ratio;
|
|
|
|
if (!valuator_mask_fetch_double(mask, 1, &y))
|
|
return;
|
|
|
|
if (v->axes[0].resolution != 0 && v->axes[1].resolution != 0)
|
|
resolution_ratio = 1.0 * v->axes[0].resolution/v->axes[1].resolution;
|
|
|
|
ratio = device_ratio/resolution_ratio/screen_ratio;
|
|
valuator_mask_set_double(mask, 1, y / ratio);
|
|
}
|
|
|
|
/**
|
|
* 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, int flags, ValuatorMask *mask)
|
|
{
|
|
int i;
|
|
Bool clip_xy = IsMaster(dev) || !IsFloating(dev);
|
|
ValuatorClassPtr v = dev->valuator;
|
|
|
|
/* for abs devices in relative mode, we've just scaled wrong, since we
|
|
mapped the device's shape into the screen shape. Undo this. */
|
|
if ((flags & POINTER_ABSOLUTE) == 0 && v && v->numAxes > 1 &&
|
|
v->axes[0].min_value < v->axes[0].max_value &&
|
|
v->axes[1].min_value < v->axes[1].max_value) {
|
|
scale_for_device_resolution(dev, mask);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
add_to_scroll_valuator(dev, mask, i, val);
|
|
|
|
/* 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)) {
|
|
val = valuator_mask_get_double(mask, i);
|
|
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/screen coordinates, modified in place
|
|
* to contain device coordinate range.
|
|
* @param flags If POINTER_SCREEN is set, mask is in per-screen coordinates.
|
|
* Otherwise, mask is in desktop coords.
|
|
*/
|
|
static void
|
|
scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask, int flags)
|
|
{
|
|
double scaled;
|
|
ScreenPtr scr = miPointerGetScreen(dev);
|
|
|
|
if (valuator_mask_isset(mask, 0)) {
|
|
scaled = valuator_mask_get_double(mask, 0);
|
|
if (flags & POINTER_SCREEN)
|
|
scaled += scr->x;
|
|
scaled = rescaleValuatorAxis(scaled,
|
|
NULL, dev->valuator->axes + 0,
|
|
screenInfo.x, screenInfo.width);
|
|
valuator_mask_set_double(mask, 0, scaled);
|
|
}
|
|
if (valuator_mask_isset(mask, 1)) {
|
|
scaled = valuator_mask_get_double(mask, 1);
|
|
if (flags & POINTER_SCREEN)
|
|
scaled += scr->y;
|
|
scaled = rescaleValuatorAxis(scaled,
|
|
NULL, dev->valuator->axes + 1,
|
|
screenInfo.y, screenInfo.height);
|
|
valuator_mask_set_double(mask, 1, scaled);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Scale from (absolute) device to screen coordinates here,
|
|
*
|
|
* The coordinates provided are always absolute. see fill_pointer_events for
|
|
* information on coordinate systems.
|
|
*
|
|
* @param dev The device to be moved.
|
|
* @param mask Mask of axis values for this event
|
|
* @param[out] devx x desktop-wide coordinate in device coordinate system
|
|
* @param[out] devy y desktop-wide coordinate in device coordinate system
|
|
* @param[out] screenx x coordinate in desktop coordinate system
|
|
* @param[out] screeny y coordinate in desktop coordinate system
|
|
*/
|
|
static ScreenPtr
|
|
scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask,
|
|
double *devx, double *devy, double *screenx, double *screeny)
|
|
{
|
|
ScreenPtr scr = miPointerGetScreen(dev);
|
|
double x, y;
|
|
|
|
BUG_WARN(dev->valuator && dev->valuator->numAxes < 2);
|
|
if (!dev->valuator || dev->valuator->numAxes < 2) {
|
|
/* if we have no axes, last.valuators must be in screen coords
|
|
* anyway */
|
|
*devx = *screenx = dev->last.valuators[0];
|
|
*devy = *screeny = dev->last.valuators[1];
|
|
return scr;
|
|
}
|
|
|
|
if (valuator_mask_isset(mask, 0))
|
|
x = valuator_mask_get_double(mask, 0);
|
|
else
|
|
x = dev->last.valuators[0];
|
|
if (valuator_mask_isset(mask, 1))
|
|
y = valuator_mask_get_double(mask, 1);
|
|
else
|
|
y = dev->last.valuators[1];
|
|
|
|
/* scale x&y to desktop coordinates */
|
|
*screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL,
|
|
screenInfo.x, screenInfo.width);
|
|
*screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL,
|
|
screenInfo.y, screenInfo.height);
|
|
|
|
*devx = x;
|
|
*devy = y;
|
|
|
|
return scr;
|
|
}
|
|
|
|
/**
|
|
* If we have HW cursors, this actually moves the visible sprite. If not, we
|
|
* just do all the screen crossing, etc.
|
|
*
|
|
* We use the screen coordinates here, call miPointerSetPosition() and then
|
|
* scale back into device coordinates (if needed). miPSP will change x/y if
|
|
* the screen was crossed.
|
|
*
|
|
* The coordinates provided are always absolute. The parameter mode
|
|
* specifies whether it was relative or absolute movement that landed us at
|
|
* those coordinates. see fill_pointer_events for information on coordinate
|
|
* systems.
|
|
*
|
|
* @param dev The device to be moved.
|
|
* @param mode Movement mode (Absolute or Relative)
|
|
* @param[out] mask Mask of axis values for this event, returns the
|
|
* per-screen device coordinates after confinement
|
|
* @param[in,out] devx x desktop-wide coordinate in device coordinate system
|
|
* @param[in,out] devy y desktop-wide coordinate in device coordinate system
|
|
* @param[in,out] screenx x coordinate in desktop coordinate system
|
|
* @param[in,out] screeny y coordinate in desktop coordinate system
|
|
* @param[out] nevents Number of barrier events added to events
|
|
* @param[in,out] events List of events barrier events are added to
|
|
*/
|
|
static ScreenPtr
|
|
positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask,
|
|
double *devx, double *devy, double *screenx, double *screeny,
|
|
int *nevents, InternalEvent* events)
|
|
{
|
|
ScreenPtr scr = miPointerGetScreen(dev);
|
|
double tmpx, tmpy;
|
|
|
|
if (!dev->valuator || dev->valuator->numAxes < 2)
|
|
return scr;
|
|
|
|
tmpx = *screenx;
|
|
tmpy = *screeny;
|
|
|
|
/* miPointerSetPosition takes care of crossing screens for us, as well as
|
|
* clipping to the current screen. Coordinates returned are in desktop
|
|
* coord system */
|
|
scr = miPointerSetPosition(dev, mode, screenx, screeny, nevents, events);
|
|
|
|
/* If we were constrained, rescale x/y from the screen coordinates so
|
|
* the device valuators reflect the correct position. For screen
|
|
* crossing this doesn't matter much, the coords would be 0 or max.
|
|
*/
|
|
if (tmpx != *screenx)
|
|
*devx = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0,
|
|
screenInfo.x, screenInfo.width);
|
|
|
|
if (tmpy != *screeny)
|
|
*devy = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1,
|
|
screenInfo.y, screenInfo.height);
|
|
|
|
/* Recalculate the per-screen device coordinates */
|
|
if (valuator_mask_isset(mask, 0)) {
|
|
double x;
|
|
|
|
x = rescaleValuatorAxis(*screenx - scr->x, NULL,
|
|
dev->valuator->axes + 0, 0, scr->width);
|
|
valuator_mask_set_double(mask, 0, x);
|
|
}
|
|
if (valuator_mask_isset(mask, 1)) {
|
|
double y;
|
|
|
|
y = rescaleValuatorAxis(*screeny - scr->y, NULL,
|
|
dev->valuator->axes + 1, 0, scr->height);
|
|
valuator_mask_set_double(mask, 1, y);
|
|
}
|
|
|
|
return scr;
|
|
}
|
|
|
|
/**
|
|
* Update the motion history for the device and (if appropriate) for its
|
|
* master device.
|
|
* @param dev Slave device to update.
|
|
* @param mask Bit mask of valid valuators to append to history.
|
|
* @param num Total number of valuators to append to history.
|
|
* @param ms Current time
|
|
*/
|
|
static void
|
|
updateHistory(DeviceIntPtr dev, ValuatorMask *mask, CARD32 ms)
|
|
{
|
|
if (!dev->valuator)
|
|
return;
|
|
|
|
updateMotionHistory(dev, ms, mask, dev->last.valuators);
|
|
if (!IsMaster(dev) && !IsFloating(dev)) {
|
|
DeviceIntPtr master = GetMaster(dev, MASTER_POINTER);
|
|
|
|
updateMotionHistory(master, ms, mask, dev->last.valuators);
|
|
}
|
|
}
|
|
|
|
static void
|
|
queueEventList(DeviceIntPtr device, InternalEvent *events, int nevents)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < nevents; i++)
|
|
mieqEnqueue(device, &events[i]);
|
|
}
|
|
|
|
static void
|
|
event_set_root_coordinates(DeviceEvent *event, double x, double y)
|
|
{
|
|
event->root_x = trunc(x);
|
|
event->root_y = trunc(y);
|
|
event->root_x_frac = x - trunc(x);
|
|
event->root_y_frac = y - trunc(y);
|
|
}
|
|
|
|
/**
|
|
* Generate internal events representing this keyboard event and enqueue
|
|
* them on the event queue.
|
|
*
|
|
* This function is not reentrant. Disable signals before calling.
|
|
*
|
|
* @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
|
|
*
|
|
*/
|
|
void
|
|
QueueKeyboardEvents(DeviceIntPtr device, int type,
|
|
int keycode)
|
|
{
|
|
int nevents;
|
|
|
|
nevents = GetKeyboardEvents(InputEventList, device, type, keycode);
|
|
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)
|
|
{
|
|
int num_events = 0;
|
|
CARD32 ms = 0;
|
|
DeviceEvent *event;
|
|
RawDeviceEvent *raw;
|
|
enum DeviceEventSource source_type = EVENT_SOURCE_NORMAL;
|
|
|
|
#ifdef XSERVER_DTRACE
|
|
if (XSERVER_INPUT_EVENT_ENABLED()) {
|
|
XSERVER_INPUT_EVENT(pDev->id, type, key_code, 0, 0,
|
|
NULL, NULL);
|
|
}
|
|
#endif
|
|
|
|
if (type == EnterNotify) {
|
|
source_type = EVENT_SOURCE_FOCUS;
|
|
type = KeyPress;
|
|
} else if (type == LeaveNotify) {
|
|
source_type = EVENT_SOURCE_FOCUS;
|
|
type = KeyRelease;
|
|
}
|
|
|
|
/* 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();
|
|
|
|
if (source_type == EVENT_SOURCE_NORMAL) {
|
|
raw = &events->raw_event;
|
|
init_raw(pDev, raw, ms, type, key_code);
|
|
events++;
|
|
num_events++;
|
|
}
|
|
|
|
event = &events->device_event;
|
|
init_device_event(event, pDev, ms, source_type);
|
|
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);
|
|
}
|
|
|
|
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
|
|
transformRelative(DeviceIntPtr dev, ValuatorMask *mask)
|
|
{
|
|
double x = 0, y = 0;
|
|
|
|
valuator_mask_fetch_double(mask, 0, &x);
|
|
valuator_mask_fetch_double(mask, 1, &y);
|
|
|
|
transform(&dev->relative_transform, &x, &y);
|
|
|
|
if (x)
|
|
valuator_mask_set_double(mask, 0, x);
|
|
else
|
|
valuator_mask_unset(mask, 0);
|
|
|
|
if (y)
|
|
valuator_mask_set_double(mask, 1, y);
|
|
else
|
|
valuator_mask_unset(mask, 1);
|
|
}
|
|
|
|
/**
|
|
* Apply the device's transformation matrix to the valuator mask and replace
|
|
* the scaled values in mask. This transformation only applies to valuators
|
|
* 0 and 1, others will be untouched.
|
|
*
|
|
* @param dev The device the valuators came from
|
|
* @param[in,out] mask The valuator mask.
|
|
*/
|
|
static void
|
|
transformAbsolute(DeviceIntPtr dev, ValuatorMask *mask)
|
|
{
|
|
double x, y, ox = 0.0, oy = 0.0;
|
|
int has_x, has_y;
|
|
|
|
has_x = valuator_mask_isset(mask, 0);
|
|
has_y = valuator_mask_isset(mask, 1);
|
|
|
|
if (!has_x && !has_y)
|
|
return;
|
|
|
|
if (!has_x || !has_y) {
|
|
struct pixman_f_transform invert;
|
|
|
|
/* undo transformation from last event */
|
|
ox = dev->last.valuators[0];
|
|
oy = dev->last.valuators[1];
|
|
|
|
pixman_f_transform_invert(&invert, &dev->scale_and_transform);
|
|
transform(&invert, &ox, &oy);
|
|
}
|
|
|
|
if (has_x)
|
|
ox = valuator_mask_get_double(mask, 0);
|
|
|
|
if (has_y)
|
|
oy = valuator_mask_get_double(mask, 1);
|
|
|
|
x = ox;
|
|
y = oy;
|
|
|
|
transform(&dev->scale_and_transform, &x, &y);
|
|
|
|
if (has_x || ox != x)
|
|
valuator_mask_set_double(mask, 0, x);
|
|
|
|
if (has_y || oy != y)
|
|
valuator_mask_set_double(mask, 1, y);
|
|
}
|
|
|
|
static void
|
|
storeLastValuators(DeviceIntPtr dev, ValuatorMask *mask,
|
|
int xaxis, int yaxis, double devx, double devy)
|
|
{
|
|
int i;
|
|
|
|
/* store desktop-wide in last.valuators */
|
|
if (valuator_mask_isset(mask, xaxis))
|
|
dev->last.valuators[0] = devx;
|
|
if (valuator_mask_isset(mask, yaxis))
|
|
dev->last.valuators[1] = devy;
|
|
|
|
for (i = 0; i < valuator_mask_size(mask); i++) {
|
|
if (i == xaxis || i == yaxis)
|
|
continue;
|
|
|
|
if (valuator_mask_isset(mask, i))
|
|
dev->last.valuators[i] = valuator_mask_get_double(mask, i);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Generate internal events representing this pointer event and enqueue them
|
|
* on the event queue.
|
|
*
|
|
* This function is not reentrant. Disable signals before calling.
|
|
*
|
|
* @param device The device to generate the event for
|
|
* @param type Event type, one of ButtonPress, ButtonRelease, MotionNotify
|
|
* @param buttons Button number of the buttons modified. Must be 0 for
|
|
* MotionNotify
|
|
* @param flags Event modification flags
|
|
* @param mask Valuator mask for valuators present for this event.
|
|
*/
|
|
void
|
|
QueuePointerEvents(DeviceIntPtr device, int type,
|
|
int buttons, int flags, const ValuatorMask *mask)
|
|
{
|
|
int nevents;
|
|
|
|
nevents =
|
|
GetPointerEvents(InputEventList, device, type, buttons, flags, mask);
|
|
queueEventList(device, InputEventList, nevents);
|
|
}
|
|
|
|
/**
|
|
* Helper function for GetPointerEvents, which only generates motion and
|
|
* raw motion events for the slave device: does not update the master device.
|
|
*
|
|
* Should not be called by anyone other than GetPointerEvents.
|
|
*
|
|
* We use several different coordinate systems and need to switch between
|
|
* the three in fill_pointer_events, positionSprite and
|
|
* miPointerSetPosition. "desktop" refers to the width/height of all
|
|
* screenInfo.screens[n]->width/height added up. "screen" is ScreenRec, not
|
|
* output.
|
|
*
|
|
* Coordinate systems:
|
|
* - relative events have a mask_in in relative coordinates, mapped to
|
|
* pixels. These events are mapped to the current position±delta.
|
|
* - absolute events have a mask_in in absolute device coordinates in
|
|
* device-specific range. This range is mapped to the desktop.
|
|
* - POINTER_SCREEN absolute events (x86WarpCursor) are in screen-relative
|
|
* screen coordinate range.
|
|
* - rootx/rooty in events must be be relative to the current screen's
|
|
* origin (screen coordinate system)
|
|
* - XI2 valuators must be relative to the current screen's origin. On
|
|
* the protocol the device min/max range maps to the current screen.
|
|
*
|
|
* For screen switching we need to get the desktop coordinates for each
|
|
* event, then map that to the respective position on each screen and
|
|
* position the cursor there.
|
|
* The device's last.valuator[] stores the last position in desktop-wide
|
|
* coordinates (in device range for slave devices, desktop range for master
|
|
* devices).
|
|
*
|
|
* screen-relative device coordinates requires scaling: A device coordinate
|
|
* x/y of range [n..m] that maps to positions Sx/Sy on Screen S must be
|
|
* rescaled to match Sx/Sy for [n..m]. In the simplest example, x of (m/2-1)
|
|
* is the last coordinate on the first screen and must be rescaled for the
|
|
* event to be m. XI2 clients that do their own coordinate mapping would
|
|
* otherwise interpret the position of the device elsewhere to the cursor.
|
|
* However, this scaling leads to losses:
|
|
* if we have two ScreenRecs we scale from e.g. [0..44704] (Wacom I4) to
|
|
* [0..2048[. that gives us 2047.954 as desktop coord, or the per-screen
|
|
* coordinate 1023.954. Scaling that back into the device coordinate range
|
|
* gives us 44703. So off by one device unit. It's a bug, but we'll have to
|
|
* live with it because with all this scaling, we just cannot win.
|
|
*
|
|
* @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 = 0;
|
|
DeviceEvent *event;
|
|
RawDeviceEvent *raw = NULL;
|
|
double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */
|
|
double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */
|
|
int sx = 0, sy = 0; /* for POINTER_SCREEN */
|
|
ValuatorMask mask;
|
|
ScreenPtr scr;
|
|
int num_barrier_events = 0;
|
|
|
|
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);
|
|
|
|
if (flags & POINTER_EMULATED)
|
|
raw->flags = XIPointerEmulated;
|
|
|
|
set_raw_valuators(raw, &mask, TRUE, raw->valuators.data_raw);
|
|
}
|
|
|
|
valuator_mask_drop_unaccelerated(&mask);
|
|
|
|
/* valuators are in driver-native format (rel or abs) */
|
|
|
|
if (flags & POINTER_ABSOLUTE) {
|
|
if (flags & (POINTER_SCREEN | POINTER_DESKTOP)) { /* valuators are in screen/desktop coords */
|
|
sx = valuator_mask_get(&mask, 0);
|
|
sy = valuator_mask_get(&mask, 1);
|
|
scale_from_screen(pDev, &mask, flags);
|
|
}
|
|
|
|
transformAbsolute(pDev, &mask);
|
|
clipAbsolute(pDev, &mask);
|
|
if ((flags & POINTER_NORAW) == 0 && raw)
|
|
set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);
|
|
}
|
|
else {
|
|
transformRelative(pDev, &mask);
|
|
|
|
if (flags & POINTER_ACCELERATE)
|
|
accelPointer(pDev, &mask, ms);
|
|
if ((flags & POINTER_NORAW) == 0 && raw)
|
|
set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);
|
|
|
|
moveRelative(pDev, flags, &mask);
|
|
}
|
|
|
|
/* valuators are in device coordinate system in absolute coordinates */
|
|
scale_to_desktop(pDev, &mask, &devx, &devy, &screenx, &screeny);
|
|
|
|
/* #53037 XWarpPointer's scaling back and forth between screen and
|
|
device may leave us with rounding errors. End result is that the
|
|
pointer doesn't end up on the pixel it should.
|
|
Avoid this by forcing screenx/screeny back to what the input
|
|
coordinates were.
|
|
*/
|
|
if (flags & POINTER_SCREEN) {
|
|
scr = miPointerGetScreen(pDev);
|
|
screenx = sx + scr->x;
|
|
screeny = sy + scr->y;
|
|
}
|
|
|
|
scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative,
|
|
&mask, &devx, &devy, &screenx, &screeny,
|
|
&num_barrier_events, events);
|
|
num_events += num_barrier_events;
|
|
events += num_barrier_events;
|
|
|
|
/* screenx, screeny are in desktop coordinates,
|
|
mask is in device coordinates per-screen (the event data)
|
|
devx/devy is in device coordinate desktop-wide */
|
|
updateHistory(pDev, &mask, ms);
|
|
|
|
clipValuators(pDev, &mask);
|
|
|
|
storeLastValuators(pDev, &mask, 0, 1, devx, devy);
|
|
|
|
/* Update the MD's coordinates, 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;
|
|
}
|
|
|
|
if ((flags & POINTER_RAWONLY) == 0) {
|
|
num_events++;
|
|
|
|
event = &events->device_event;
|
|
init_device_event(event, pDev, ms, EVENT_SOURCE_NORMAL);
|
|
|
|
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 coordinates */
|
|
event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y);
|
|
|
|
if (flags & POINTER_EMULATED)
|
|
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;
|
|
|
|
BUG_WARN_MSG(incr == 0, "for device %s\n", dev->name);
|
|
if (incr == 0)
|
|
return 0;
|
|
|
|
if (type != ButtonPress && type != ButtonRelease)
|
|
flags |= POINTER_EMULATED;
|
|
|
|
if (!valuator_mask_isset(last, axis))
|
|
valuator_mask_set_double(last, axis, 0);
|
|
|
|
delta =
|
|
valuator_mask_get_double(mask, axis) - valuator_mask_get_double(last,
|
|
axis);
|
|
total = delta;
|
|
b = (ax->scroll.type == SCROLL_TYPE_VERTICAL) ? 5 : 7;
|
|
|
|
if ((incr > 0 && delta < 0) || (incr < 0 && delta > 0))
|
|
b--; /* we're scrolling up or left → button 4 or 6 */
|
|
|
|
while (fabs(delta) >= fabs(incr)) {
|
|
int nev_tmp;
|
|
|
|
if (delta > 0)
|
|
delta -= fabs(incr);
|
|
else if (delta < 0)
|
|
delta += fabs(incr);
|
|
|
|
/* fill_pointer_events() generates four events: one normal and one raw
|
|
* event for button press and button release.
|
|
* We may get a bigger scroll delta than we can generate events
|
|
* for. In that case, we keep decreasing delta, but skip events.
|
|
*/
|
|
if (num_events + 4 < max_events) {
|
|
if (type != ButtonRelease) {
|
|
nev_tmp = fill_pointer_events(events, dev, ButtonPress, b, ms,
|
|
flags, NULL);
|
|
events += nev_tmp;
|
|
num_events += nev_tmp;
|
|
}
|
|
if (type != ButtonPress) {
|
|
nev_tmp = fill_pointer_events(events, dev, ButtonRelease, b, ms,
|
|
flags, NULL);
|
|
events += nev_tmp;
|
|
num_events += nev_tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We emulated, update last.scroll */
|
|
if (total != delta) {
|
|
total -= delta;
|
|
valuator_mask_set_double(last, axis,
|
|
valuator_mask_get_double(last, axis) + total);
|
|
}
|
|
|
|
return num_events;
|
|
}
|
|
|
|
|
|
/**
|
|
* Generate a complete series of InternalEvents (filled into the EventList)
|
|
* representing pointer motion, or button presses. If the device is a slave
|
|
* device, also potentially generate a DeviceClassesChangedEvent to update
|
|
* the master device.
|
|
*
|
|
* events is not NULL-terminated; the return value is the number of events.
|
|
* The DDX is responsible for allocating the event structure in the first
|
|
* place via InitEventList() and GetMaximumEventsNum(), and for freeing it.
|
|
*
|
|
* In the generated events rootX/Y will be in absolute screen coords and
|
|
* the valuator information in the absolute or relative device coords.
|
|
*
|
|
* last.valuators[x] of the device is always in absolute device coords.
|
|
* last.valuators[x] of the master device is in absolute screen coords.
|
|
*
|
|
* master->last.valuators[x] for x > 2 is undefined.
|
|
*/
|
|
int
|
|
GetPointerEvents(InternalEvent *events, DeviceIntPtr pDev, int type,
|
|
int buttons, int flags, const ValuatorMask *mask_in)
|
|
{
|
|
CARD32 ms = GetTimeInMillis();
|
|
int num_events = 0, nev_tmp;
|
|
ValuatorMask mask;
|
|
ValuatorMask scroll;
|
|
int i;
|
|
int realtype = type;
|
|
|
|
#ifdef XSERVER_DTRACE
|
|
if (XSERVER_INPUT_EVENT_ENABLED()) {
|
|
XSERVER_INPUT_EVENT(pDev->id, type, buttons, flags,
|
|
mask_in ? mask_in->last_bit + 1 : 0,
|
|
mask_in ? mask_in->mask : NULL,
|
|
mask_in ? mask_in->valuators : NULL);
|
|
}
|
|
#endif
|
|
|
|
BUG_RETURN_VAL(buttons >= MAX_BUTTONS, 0);
|
|
|
|
/* 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 adj;
|
|
int axis;
|
|
int h_scroll_axis = -1;
|
|
int v_scroll_axis = -1;
|
|
|
|
if (pDev->valuator) {
|
|
h_scroll_axis = pDev->valuator->h_scroll_axis;
|
|
v_scroll_axis = pDev->valuator->v_scroll_axis;
|
|
}
|
|
|
|
/* Up is negative on valuators, down positive */
|
|
switch (buttons) {
|
|
case 4:
|
|
adj = -1.0;
|
|
axis = v_scroll_axis;
|
|
break;
|
|
case 5:
|
|
adj = 1.0;
|
|
axis = v_scroll_axis;
|
|
break;
|
|
case 6:
|
|
adj = -1.0;
|
|
axis = h_scroll_axis;
|
|
break;
|
|
case 7:
|
|
adj = 1.0;
|
|
axis = h_scroll_axis;
|
|
break;
|
|
default:
|
|
adj = 0.0;
|
|
axis = -1;
|
|
break;
|
|
}
|
|
|
|
if (adj != 0.0 && axis != -1) {
|
|
adj *= pDev->valuator->axes[axis].scroll.increment;
|
|
if (!valuator_mask_isset(&mask, axis))
|
|
valuator_mask_set(&mask, axis, 0);
|
|
add_to_scroll_valuator(pDev, &mask, axis, adj);
|
|
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 ( !pDev->valuator || (i >= pDev->valuator->numAxes))
|
|
break;
|
|
|
|
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;
|
|
|
|
#ifdef XSERVER_DTRACE
|
|
if (XSERVER_INPUT_EVENT_ENABLED()) {
|
|
XSERVER_INPUT_EVENT(pDev->id, type, 0, 0,
|
|
mask_in ? mask_in->last_bit + 1 : 0,
|
|
mask_in ? mask_in->mask : NULL,
|
|
mask_in ? mask_in->valuators : NULL);
|
|
}
|
|
#endif
|
|
|
|
/* refuse events from disabled devices */
|
|
if (!pDev->enabled)
|
|
return 0;
|
|
|
|
/* Sanity checks. */
|
|
if ((type != ProximityIn && type != ProximityOut) || !mask_in)
|
|
return 0;
|
|
if (!pDev->valuator || !pDev->proximity)
|
|
return 0;
|
|
|
|
valuator_mask_copy(&mask, mask_in);
|
|
|
|
/* ignore relative axes for proximity. */
|
|
for (i = 0; i < valuator_mask_size(&mask); i++) {
|
|
if (valuator_mask_isset(&mask, i) &&
|
|
valuator_get_mode(pDev, i) == Relative)
|
|
valuator_mask_unset(&mask, i);
|
|
}
|
|
|
|
/* FIXME: posting proximity events with relative valuators only results
|
|
* in an empty event, EventToXI() will fail to convert → no event sent
|
|
* to client. */
|
|
|
|
events =
|
|
UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT, &num_events);
|
|
|
|
event = &events->device_event;
|
|
init_device_event(event, pDev, GetTimeInMillis(), EVENT_SOURCE_NORMAL);
|
|
event->type = (type == ProximityIn) ? ET_ProximityIn : ET_ProximityOut;
|
|
|
|
clipValuators(pDev, &mask);
|
|
|
|
set_valuators(pDev, event, &mask);
|
|
|
|
return num_events;
|
|
}
|
|
|
|
int
|
|
GetTouchOwnershipEvents(InternalEvent *events, DeviceIntPtr pDev,
|
|
TouchPointInfoPtr ti, uint8_t reason, XID resource,
|
|
uint32_t flags)
|
|
{
|
|
TouchClassPtr t = pDev->touch;
|
|
TouchOwnershipEvent *event;
|
|
CARD32 ms = GetTimeInMillis();
|
|
|
|
if (!pDev->enabled || !t || !ti)
|
|
return 0;
|
|
|
|
event = &events->touch_ownership_event;
|
|
init_touch_ownership(pDev, event, ms);
|
|
|
|
event->touchid = ti->client_id;
|
|
event->sourceid = ti->sourceid;
|
|
event->resource = resource;
|
|
event->flags = flags;
|
|
event->reason = reason;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Generate internal events representing this touch event and enqueue them
|
|
* on the event queue.
|
|
*
|
|
* This function is not reentrant. Disable signals before calling.
|
|
*
|
|
* @param device The device to generate the event for
|
|
* @param type Event type, one of XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd
|
|
* @param touchid Touch point ID
|
|
* @param flags Event modification flags
|
|
* @param mask Valuator mask for valuators present for this event.
|
|
*/
|
|
void
|
|
QueueTouchEvents(DeviceIntPtr device, int type,
|
|
uint32_t ddx_touchid, int flags, const ValuatorMask *mask)
|
|
{
|
|
int nevents;
|
|
|
|
nevents =
|
|
GetTouchEvents(InputEventList, device, ddx_touchid, type, flags, mask);
|
|
queueEventList(device, InputEventList, nevents);
|
|
}
|
|
|
|
/**
|
|
* Get events for a touch. Generates a TouchBegin event if end is not set and
|
|
* the touch id is not active. Generates a TouchUpdate event if end is not set
|
|
* and the touch id is active. Generates a TouchEnd event if end is set and the
|
|
* touch id is active.
|
|
*
|
|
* events is not NULL-terminated; the return value is the number of events.
|
|
* The DDX is responsible for allocating the event structure in the first
|
|
* place via GetMaximumEventsNum(), and for freeing it.
|
|
*
|
|
* @param[out] events The list of events generated
|
|
* @param dev The device to generate the events for
|
|
* @param ddx_touchid The touch ID as assigned by the DDX
|
|
* @param type XI_TouchBegin, XI_TouchUpdate or XI_TouchEnd
|
|
* @param flags Event flags
|
|
* @param mask_in Valuator information for this event
|
|
*/
|
|
int
|
|
GetTouchEvents(InternalEvent *events, DeviceIntPtr dev, uint32_t ddx_touchid,
|
|
uint16_t type, uint32_t flags, const ValuatorMask *mask_in)
|
|
{
|
|
ScreenPtr scr;
|
|
TouchClassPtr t = dev->touch;
|
|
ValuatorClassPtr v = dev->valuator;
|
|
DeviceEvent *event;
|
|
CARD32 ms = GetTimeInMillis();
|
|
ValuatorMask mask;
|
|
double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */
|
|
double devx = 0.0, devy = 0.0; /* desktop-wide in device coords */
|
|
int i;
|
|
int num_events = 0;
|
|
RawDeviceEvent *raw;
|
|
DDXTouchPointInfoPtr ti;
|
|
int need_rawevent = TRUE;
|
|
Bool emulate_pointer = FALSE;
|
|
int client_id = 0;
|
|
|
|
#ifdef XSERVER_DTRACE
|
|
if (XSERVER_INPUT_EVENT_ENABLED()) {
|
|
XSERVER_INPUT_EVENT(dev->id, type, ddx_touchid, flags,
|
|
mask_in ? mask_in->last_bit + 1 : 0,
|
|
mask_in ? mask_in->mask : NULL,
|
|
mask_in ? mask_in->valuators : NULL);
|
|
}
|
|
#endif
|
|
|
|
if (!dev->enabled || !t || !v)
|
|
return 0;
|
|
|
|
/* Find and/or create the DDX touch info */
|
|
|
|
ti = TouchFindByDDXID(dev, ddx_touchid, (type == XI_TouchBegin));
|
|
if (!ti) {
|
|
ErrorFSigSafe("[dix] %s: unable to %s touch point %u\n", dev->name,
|
|
type == XI_TouchBegin ? "begin" : "find", ddx_touchid);
|
|
return 0;
|
|
}
|
|
client_id = ti->client_id;
|
|
|
|
emulate_pointer = ti->emulate_pointer;
|
|
|
|
if (!IsMaster(dev))
|
|
events =
|
|
UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT, &num_events);
|
|
|
|
valuator_mask_copy(&mask, mask_in);
|
|
|
|
if (need_rawevent) {
|
|
raw = &events->raw_event;
|
|
events++;
|
|
num_events++;
|
|
init_raw(dev, raw, ms, type, client_id);
|
|
set_raw_valuators(raw, &mask, TRUE, raw->valuators.data_raw);
|
|
}
|
|
|
|
event = &events->device_event;
|
|
num_events++;
|
|
|
|
init_device_event(event, dev, ms, EVENT_SOURCE_NORMAL);
|
|
|
|
switch (type) {
|
|
case XI_TouchBegin:
|
|
event->type = ET_TouchBegin;
|
|
/* If we're starting a touch, we must have x & y coordinates. */
|
|
if (!mask_in ||
|
|
!valuator_mask_isset(mask_in, 0) ||
|
|
!valuator_mask_isset(mask_in, 1)) {
|
|
ErrorFSigSafe("%s: Attempted to start touch without x/y "
|
|
"(driver bug)\n", dev->name);
|
|
return 0;
|
|
}
|
|
break;
|
|
case XI_TouchUpdate:
|
|
event->type = ET_TouchUpdate;
|
|
if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0) {
|
|
ErrorFSigSafe("%s: TouchUpdate with no valuators? Driver bug\n",
|
|
dev->name);
|
|
}
|
|
break;
|
|
case XI_TouchEnd:
|
|
event->type = ET_TouchEnd;
|
|
/* We can end the DDX touch here, since we don't use the active
|
|
* field below */
|
|
TouchEndDDXTouch(dev, ti);
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Get our screen event coordinates (root_x/root_y/event_x/event_y):
|
|
* these come from the touchpoint in Absolute mode, or the sprite in
|
|
* Relative. */
|
|
if (t->mode == XIDirectTouch) {
|
|
for (i = 0; i < max(valuator_mask_size(&mask), 2); i++) {
|
|
double val;
|
|
|
|
if (valuator_mask_fetch_double(&mask, i, &val))
|
|
valuator_mask_set_double(ti->valuators, i, val);
|
|
/* If the device doesn't post new X and Y axis values,
|
|
* use the last values posted.
|
|
*/
|
|
else if (i < 2 &&
|
|
valuator_mask_fetch_double(ti->valuators, i, &val))
|
|
valuator_mask_set_double(&mask, i, val);
|
|
}
|
|
|
|
transformAbsolute(dev, &mask);
|
|
clipAbsolute(dev, &mask);
|
|
}
|
|
else {
|
|
screenx = dev->spriteInfo->sprite->hotPhys.x;
|
|
screeny = dev->spriteInfo->sprite->hotPhys.y;
|
|
}
|
|
if (need_rawevent)
|
|
set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);
|
|
|
|
scr = dev->spriteInfo->sprite->hotPhys.pScreen;
|
|
|
|
/* Indirect device touch coordinates are not used for cursor positioning.
|
|
* They are merely informational, and are provided in device coordinates.
|
|
* The device sprite is used for positioning instead, and it is already
|
|
* scaled. */
|
|
if (t->mode == XIDirectTouch)
|
|
scr = scale_to_desktop(dev, &mask, &devx, &devy, &screenx, &screeny);
|
|
if (emulate_pointer)
|
|
scr = positionSprite(dev, Absolute, &mask,
|
|
&devx, &devy, &screenx, &screeny, NULL, NULL);
|
|
|
|
/* see fill_pointer_events for coordinate systems */
|
|
if (emulate_pointer)
|
|
updateHistory(dev, &mask, ms);
|
|
|
|
clipValuators(dev, &mask);
|
|
|
|
if (emulate_pointer)
|
|
storeLastValuators(dev, &mask, 0, 1, devx, devy);
|
|
|
|
/* Update the MD's coordinates, which are always in desktop space. */
|
|
if (emulate_pointer && !IsMaster(dev) && !IsFloating(dev)) {
|
|
DeviceIntPtr master = GetMaster(dev, MASTER_POINTER);
|
|
|
|
master->last.valuators[0] = screenx;
|
|
master->last.valuators[1] = screeny;
|
|
}
|
|
|
|
event->root = scr->root->drawable.id;
|
|
|
|
event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y);
|
|
event->touchid = client_id;
|
|
event->flags = flags;
|
|
|
|
if (emulate_pointer) {
|
|
event->flags |= TOUCH_POINTER_EMULATED;
|
|
event->detail.button = 1;
|
|
}
|
|
|
|
set_valuators(dev, event, &mask);
|
|
for (i = 0; i < v->numAxes; i++) {
|
|
if (valuator_mask_isset(&mask, i))
|
|
v->axisVal[i] = valuator_mask_get(&mask, i);
|
|
}
|
|
|
|
return num_events;
|
|
}
|
|
|
|
void
|
|
GetDixTouchEnd(InternalEvent *ievent, DeviceIntPtr dev, TouchPointInfoPtr ti,
|
|
uint32_t flags)
|
|
{
|
|
ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen;
|
|
DeviceEvent *event = &ievent->device_event;
|
|
CARD32 ms = GetTimeInMillis();
|
|
|
|
BUG_WARN(!dev->enabled);
|
|
|
|
init_device_event(event, dev, ms, EVENT_SOURCE_NORMAL);
|
|
|
|
event->sourceid = ti->sourceid;
|
|
event->type = ET_TouchEnd;
|
|
|
|
event->root = scr->root->drawable.id;
|
|
|
|
/* Get screen event coordinates from the sprite. Is this really the best
|
|
* we can do? */
|
|
event_set_root_coordinates(event,
|
|
dev->last.valuators[0] - scr->x,
|
|
dev->last.valuators[1] - scr->y);
|
|
event->touchid = ti->client_id;
|
|
event->flags = flags;
|
|
|
|
if (flags & TOUCH_POINTER_EMULATED) {
|
|
event->flags |= TOUCH_POINTER_EMULATED;
|
|
event->detail.button = 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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, EVENT_SOURCE_NORMAL);
|
|
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);
|
|
}
|
|
|
|
void
|
|
InitGestureEvent(InternalEvent *ievent, DeviceIntPtr dev, CARD32 ms,
|
|
int type, uint16_t num_touches, uint32_t flags,
|
|
double delta_x, double delta_y,
|
|
double delta_unaccel_x, double delta_unaccel_y,
|
|
double scale, double delta_angle)
|
|
{
|
|
ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen;
|
|
GestureEvent *event = &ievent->gesture_event;
|
|
double screenx = 0.0, screeny = 0.0; /* desktop coordinate system */
|
|
|
|
init_gesture_event(event, dev, ms);
|
|
|
|
screenx = dev->spriteInfo->sprite->hotPhys.x;
|
|
screeny = dev->spriteInfo->sprite->hotPhys.y;
|
|
|
|
event->type = type;
|
|
event->root = scr->root->drawable.id;
|
|
event->root_x = screenx - scr->x;
|
|
event->root_y = screeny - scr->y;
|
|
event->num_touches = num_touches;
|
|
event->flags = flags;
|
|
|
|
event->delta_x = delta_x;
|
|
event->delta_y = delta_y;
|
|
event->delta_unaccel_x = delta_unaccel_x;
|
|
event->delta_unaccel_y = delta_unaccel_y;
|
|
event->scale = scale;
|
|
event->delta_angle = delta_angle;
|
|
}
|
|
|
|
/**
|
|
* Get events for a pinch or swipe gesture.
|
|
*
|
|
* events is not NULL-terminated; the return value is the number of events.
|
|
* The DDX is responsible for allocating the event structure in the first
|
|
* place via GetMaximumEventsNum(), and for freeing it.
|
|
*
|
|
* @param[out] events The list of events generated
|
|
* @param dev The device to generate the events for
|
|
* @param type XI_Gesture{Pinch,Swipe}{Begin,Update,End}
|
|
* @prama num_touches The number of touches in the gesture
|
|
* @param flags Event flags
|
|
* @param delta_x,delta_y accelerated relative motion delta
|
|
* @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
|
|
* @param scale (valid only to pinch events) absolute scale of a pinch gesture
|
|
* @param delta_angle (valid only to pinch events) the ange delta in degrees between the last and
|
|
* the current pinch event.
|
|
*/
|
|
int
|
|
GetGestureEvents(InternalEvent *events, DeviceIntPtr dev,
|
|
uint16_t type, uint16_t num_touches, uint32_t flags,
|
|
double delta_x, double delta_y,
|
|
double delta_unaccel_x, double delta_unaccel_y,
|
|
double scale, double delta_angle)
|
|
|
|
{
|
|
GestureClassPtr g = dev->gesture;
|
|
CARD32 ms = GetTimeInMillis();
|
|
enum EventType evtype;
|
|
int num_events = 0;
|
|
uint32_t evflags = 0;
|
|
|
|
if (!dev->enabled || !g)
|
|
return 0;
|
|
|
|
if (!IsMaster(dev))
|
|
events = UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT,
|
|
&num_events);
|
|
|
|
switch (type) {
|
|
case XI_GesturePinchBegin:
|
|
evtype = ET_GesturePinchBegin;
|
|
break;
|
|
case XI_GesturePinchUpdate:
|
|
evtype = ET_GesturePinchUpdate;
|
|
break;
|
|
case XI_GesturePinchEnd:
|
|
evtype = ET_GesturePinchEnd;
|
|
if (flags & XIGesturePinchEventCancelled)
|
|
evflags |= GESTURE_CANCELLED;
|
|
break;
|
|
case XI_GestureSwipeBegin:
|
|
evtype = ET_GestureSwipeBegin;
|
|
break;
|
|
case XI_GestureSwipeUpdate:
|
|
evtype = ET_GestureSwipeUpdate;
|
|
break;
|
|
case XI_GestureSwipeEnd:
|
|
evtype = ET_GestureSwipeEnd;
|
|
if (flags & XIGestureSwipeEventCancelled)
|
|
evflags |= GESTURE_CANCELLED;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
InitGestureEvent(events, dev, ms, evtype, num_touches, evflags,
|
|
delta_x, delta_y, delta_unaccel_x, delta_unaccel_y,
|
|
scale, delta_angle);
|
|
num_events++;
|
|
|
|
return num_events;
|
|
}
|
|
|
|
void
|
|
QueueGesturePinchEvents(DeviceIntPtr dev, uint16_t type,
|
|
uint16_t num_touches, uint32_t flags,
|
|
double delta_x, double delta_y,
|
|
double delta_unaccel_x,
|
|
double delta_unaccel_y,
|
|
double scale, double delta_angle)
|
|
{
|
|
int nevents;
|
|
nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
|
|
delta_x, delta_y,
|
|
delta_unaccel_x, delta_unaccel_y,
|
|
scale, delta_angle);
|
|
queueEventList(dev, InputEventList, nevents);
|
|
}
|
|
|
|
void
|
|
QueueGestureSwipeEvents(DeviceIntPtr dev, uint16_t type,
|
|
uint16_t num_touches, uint32_t flags,
|
|
double delta_x, double delta_y,
|
|
double delta_unaccel_x,
|
|
double delta_unaccel_y)
|
|
{
|
|
int nevents;
|
|
nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
|
|
delta_x, delta_y,
|
|
delta_unaccel_x, delta_unaccel_y,
|
|
0.0, 0.0);
|
|
queueEventList(dev, InputEventList, nevents);
|
|
}
|