You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
482 lines
15 KiB
482 lines
15 KiB
/* |
|
* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. |
|
* Copyright 1993 by David Dawes <dawes@xfree86.org> |
|
* Copyright 2002 by SuSE Linux AG, Author: Egbert Eich |
|
* Copyright 1994-2002 by The XFree86 Project, Inc. |
|
* Copyright 2002 by Paul Elliott |
|
* (Ported from xf86-input-mouse, above copyrights taken from there) |
|
* Copyright 2008 by Chris Salch |
|
* Copyright © 2008 Red Hat, Inc. |
|
* |
|
* Permission to use, copy, modify, distribute, and sell this software |
|
* and its documentation for any purpose is hereby granted without |
|
* fee, provided that the above copyright notice appear in all copies |
|
* and that both that copyright notice and this permission notice |
|
* appear in supporting documentation, and that the name of the authors |
|
* not be used in advertising or publicity pertaining to distribution of the |
|
* software without specific, written prior permission. The authors make no |
|
* representations about the suitability of this software for any |
|
* purpose. It is provided "as is" without express or implied |
|
* warranty. |
|
* |
|
* THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN |
|
* NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS |
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
|
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
* |
|
*/ |
|
|
|
/* Mouse wheel emulation code. */ |
|
#ifdef HAVE_CONFIG_H |
|
#include "config.h" |
|
#endif |
|
#include "evdev.h" |
|
|
|
#include <X11/Xatom.h> |
|
#include <xf86.h> |
|
#include <xf86Xinput.h> |
|
#include <exevents.h> |
|
|
|
#include <evdev-properties.h> |
|
|
|
#define WHEEL_NOT_CONFIGURED 0 |
|
|
|
static Atom prop_wheel_emu = 0; |
|
static Atom prop_wheel_axismap = 0; |
|
static Atom prop_wheel_inertia = 0; |
|
static Atom prop_wheel_timeout = 0; |
|
static Atom prop_wheel_button = 0; |
|
|
|
/* Local Funciton Prototypes */ |
|
static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value); |
|
|
|
/* Filter mouse button events */ |
|
BOOL |
|
EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value) |
|
{ |
|
EvdevPtr pEvdev = (EvdevPtr)pInfo->private; |
|
int ms; |
|
|
|
/* Has wheel emulation been configured to be enabled? */ |
|
if (!pEvdev->emulateWheel.enabled) |
|
return FALSE; |
|
|
|
/* Check for EmulateWheelButton */ |
|
if (pEvdev->emulateWheel.button == button) { |
|
pEvdev->emulateWheel.button_state = value; |
|
|
|
if (value) |
|
/* Start the timer when the button is pressed */ |
|
pEvdev->emulateWheel.expires = pEvdev->emulateWheel.timeout + |
|
GetTimeInMillis(); |
|
else { |
|
ms = pEvdev->emulateWheel.expires - GetTimeInMillis(); |
|
if (ms > 0) { |
|
/* |
|
* If the button is released early enough emit the button |
|
* press/release events |
|
*/ |
|
EvdevQueueButtonClicks(pInfo, button, 1); |
|
} |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
/* Don't care about this event */ |
|
return FALSE; |
|
} |
|
|
|
/* Filter mouse wheel events */ |
|
BOOL |
|
EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv) |
|
{ |
|
EvdevPtr pEvdev = (EvdevPtr)pInfo->private; |
|
WheelAxisPtr pAxis = NULL, pOtherAxis = NULL; |
|
int value = pEv->value; |
|
|
|
/* Has wheel emulation been configured to be enabled? */ |
|
if (!pEvdev->emulateWheel.enabled) |
|
return FALSE; |
|
|
|
/* Handle our motion events if the emuWheel button is pressed |
|
* wheel button of 0 means always emulate wheel. |
|
*/ |
|
if (pEvdev->emulateWheel.button_state || !pEvdev->emulateWheel.button) { |
|
/* Just return if the timeout hasn't expired yet */ |
|
if (pEvdev->emulateWheel.button) |
|
{ |
|
int ms = pEvdev->emulateWheel.expires - GetTimeInMillis(); |
|
if (ms > 0) |
|
return TRUE; |
|
} |
|
|
|
if(pEv->type == EV_ABS) { |
|
int axis = pEvdev->abs_axis_map[pEv->code]; |
|
int oldValue; |
|
|
|
if (axis > -1 && valuator_mask_fetch(pEvdev->old_vals, axis, &oldValue)) { |
|
valuator_mask_set(pEvdev->abs_vals, axis, value); |
|
value -= oldValue; /* make value into a differential measurement */ |
|
} else |
|
value = 0; /* avoid a jump on the first touch */ |
|
} |
|
|
|
switch(pEv->code) { |
|
|
|
/* ABS_X has the same value as REL_X, so this case catches both */ |
|
case REL_X: |
|
pAxis = &(pEvdev->emulateWheel.X); |
|
pOtherAxis = &(pEvdev->emulateWheel.Y); |
|
break; |
|
|
|
/* ABS_Y has the same value as REL_Y, so this case catches both */ |
|
case REL_Y: |
|
pAxis = &(pEvdev->emulateWheel.Y); |
|
pOtherAxis = &(pEvdev->emulateWheel.X); |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
|
|
/* If we found REL_X, REL_Y, ABS_X or ABS_Y then emulate a mouse |
|
wheel. Reset the inertia of the other axis when a scroll event |
|
was sent to avoid the buildup of erroneous scroll events if the |
|
user doesn't move in a perfectly straight line. |
|
*/ |
|
if (pAxis) |
|
{ |
|
if (EvdevWheelEmuInertia(pInfo, pAxis, value)) |
|
pOtherAxis->traveled_distance = 0; |
|
} |
|
|
|
/* Eat motion events while emulateWheel button pressed. */ |
|
return TRUE; |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
/* Simulate inertia for our emulated mouse wheel. |
|
Returns the number of wheel events generated. |
|
*/ |
|
static int |
|
EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value) |
|
{ |
|
EvdevPtr pEvdev = (EvdevPtr)pInfo->private; |
|
int button; |
|
int inertia; |
|
int rc = 0; |
|
|
|
/* if this axis has not been configured, just eat the motion */ |
|
if (!axis->up_button) |
|
return rc; |
|
|
|
axis->traveled_distance += value; |
|
|
|
if (axis->traveled_distance < 0) { |
|
button = axis->up_button; |
|
inertia = -pEvdev->emulateWheel.inertia; |
|
} else { |
|
button = axis->down_button; |
|
inertia = pEvdev->emulateWheel.inertia; |
|
} |
|
|
|
/* Produce button press events for wheel motion */ |
|
while(abs(axis->traveled_distance) > pEvdev->emulateWheel.inertia) { |
|
axis->traveled_distance -= inertia; |
|
EvdevQueueButtonClicks(pInfo, button, 1); |
|
rc++; |
|
} |
|
return rc; |
|
} |
|
|
|
/* Handle button mapping here to avoid code duplication, |
|
returns true if a button mapping was found. */ |
|
static BOOL |
|
EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, |
|
const char *axis_name) |
|
{ |
|
EvdevPtr pEvdev = (EvdevPtr)pInfo->private; |
|
char *option_string; |
|
|
|
pAxis->up_button = WHEEL_NOT_CONFIGURED; |
|
|
|
/* Check to see if there is configuration for this axis */ |
|
option_string = xf86SetStrOption(pInfo->options, axis_name, NULL); |
|
if (option_string) { |
|
int up_button = 0; |
|
int down_button = 0; |
|
char *msg = NULL; |
|
|
|
if ((sscanf(option_string, "%d %d", &up_button, &down_button) == 2) && |
|
((up_button > 0) && (up_button <= EVDEV_MAXBUTTONS)) && |
|
((down_button > 0) && (down_button <= EVDEV_MAXBUTTONS))) { |
|
|
|
/* Use xstrdup to allocate a string for us */ |
|
msg = xstrdup("buttons XX and YY"); |
|
|
|
if (msg) |
|
sprintf(msg, "buttons %d and %d", up_button, down_button); |
|
|
|
pAxis->up_button = up_button; |
|
pAxis->down_button = down_button; |
|
|
|
/* Update the number of buttons if needed */ |
|
if (up_button > pEvdev->num_buttons) pEvdev->num_buttons = up_button; |
|
if (down_button > pEvdev->num_buttons) pEvdev->num_buttons = down_button; |
|
|
|
} else { |
|
xf86IDrvMsg(pInfo, X_WARNING, "Invalid %s value:\"%s\"\n", |
|
axis_name, option_string); |
|
} |
|
free(option_string); |
|
|
|
/* Clean up and log what happened */ |
|
if (msg) { |
|
xf86IDrvMsg(pInfo, X_CONFIG, "%s: %s\n", axis_name, msg); |
|
free(msg); |
|
return TRUE; |
|
} |
|
} |
|
return FALSE; |
|
} |
|
|
|
/* Setup the basic configuration options used by mouse wheel emulation */ |
|
void |
|
EvdevWheelEmuPreInit(InputInfoPtr pInfo) |
|
{ |
|
EvdevPtr pEvdev = (EvdevPtr)pInfo->private; |
|
int wheelButton; |
|
int inertia; |
|
int timeout; |
|
|
|
if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) { |
|
pEvdev->emulateWheel.enabled = TRUE; |
|
} else |
|
pEvdev->emulateWheel.enabled = FALSE; |
|
|
|
wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4); |
|
|
|
if ((wheelButton < 0) || (wheelButton > EVDEV_MAXBUTTONS)) { |
|
xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelButton value: %d\n", |
|
wheelButton); |
|
xf86IDrvMsg(pInfo, X_WARNING, "Wheel emulation disabled.\n"); |
|
|
|
pEvdev->emulateWheel.enabled = FALSE; |
|
} |
|
|
|
pEvdev->emulateWheel.button = wheelButton; |
|
|
|
inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10); |
|
|
|
if (inertia <= 0) { |
|
xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelInertia value: %d\n", |
|
inertia); |
|
xf86IDrvMsg(pInfo, X_WARNING, "Using built-in inertia value.\n"); |
|
|
|
inertia = 10; |
|
} |
|
|
|
pEvdev->emulateWheel.inertia = inertia; |
|
|
|
timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200); |
|
|
|
if (timeout < 0) { |
|
xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelTimeout value: %d\n", |
|
timeout); |
|
xf86IDrvMsg(pInfo, X_WARNING, "Using built-in timeout value.\n"); |
|
|
|
timeout = 200; |
|
} |
|
|
|
pEvdev->emulateWheel.timeout = timeout; |
|
|
|
/* Configure the Y axis or default it */ |
|
if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y), |
|
"YAxisMapping")) { |
|
/* Default the Y axis to sane values */ |
|
pEvdev->emulateWheel.Y.up_button = 4; |
|
pEvdev->emulateWheel.Y.down_button = 5; |
|
|
|
/* Simpler to check just the largest value in this case */ |
|
/* XXX: we should post this to the server */ |
|
if (5 > pEvdev->num_buttons) |
|
pEvdev->num_buttons = 5; |
|
|
|
/* Display default Configuration */ |
|
xf86IDrvMsg(pInfo, X_CONFIG, "YAxisMapping: buttons %d and %d\n", |
|
pEvdev->emulateWheel.Y.up_button, |
|
pEvdev->emulateWheel.Y.down_button); |
|
} |
|
|
|
|
|
/* This axis should default to an unconfigured state as most people |
|
are not going to expect a Horizontal wheel. */ |
|
EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.X), |
|
"XAxisMapping"); |
|
|
|
/* Used by the inertia code */ |
|
pEvdev->emulateWheel.X.traveled_distance = 0; |
|
pEvdev->emulateWheel.Y.traveled_distance = 0; |
|
|
|
xf86IDrvMsg(pInfo, X_CONFIG, |
|
"EmulateWheelButton: %d, " |
|
"EmulateWheelInertia: %d, " |
|
"EmulateWheelTimeout: %d\n", |
|
pEvdev->emulateWheel.button, inertia, timeout); |
|
} |
|
|
|
static int |
|
EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val, |
|
BOOL checkonly) |
|
{ |
|
InputInfoPtr pInfo = dev->public.devicePrivate; |
|
EvdevPtr pEvdev = pInfo->private; |
|
|
|
if (atom == prop_wheel_emu) |
|
{ |
|
if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) |
|
return BadMatch; |
|
|
|
if (!checkonly) |
|
{ |
|
pEvdev->emulateWheel.enabled = *((BOOL*)val->data); |
|
/* Don't enable with zero inertia, otherwise we may get stuck in an |
|
* infinite loop */ |
|
if (pEvdev->emulateWheel.inertia <= 0) |
|
{ |
|
pEvdev->emulateWheel.inertia = 10; |
|
/* We may get here before the property is actually enabled */ |
|
if (prop_wheel_inertia) |
|
XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, |
|
16, PropModeReplace, 1, |
|
&pEvdev->emulateWheel.inertia, TRUE); |
|
} |
|
} |
|
} |
|
else if (atom == prop_wheel_button) |
|
{ |
|
int bt; |
|
|
|
if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER) |
|
return BadMatch; |
|
|
|
bt = *((CARD8*)val->data); |
|
|
|
if (bt < 0 || bt >= EVDEV_MAXBUTTONS) |
|
return BadValue; |
|
|
|
if (!checkonly) |
|
pEvdev->emulateWheel.button = bt; |
|
} else if (atom == prop_wheel_axismap) |
|
{ |
|
if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER) |
|
return BadMatch; |
|
|
|
if (!checkonly) |
|
{ |
|
pEvdev->emulateWheel.X.up_button = *((CARD8*)val->data); |
|
pEvdev->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1); |
|
pEvdev->emulateWheel.Y.up_button = *(((CARD8*)val->data) + 2); |
|
pEvdev->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 3); |
|
} |
|
} else if (atom == prop_wheel_inertia) |
|
{ |
|
int inertia; |
|
|
|
if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER) |
|
return BadMatch; |
|
|
|
inertia = *((CARD16*)val->data); |
|
|
|
if (inertia <= 0) |
|
return BadValue; |
|
|
|
if (!checkonly) |
|
pEvdev->emulateWheel.inertia = inertia; |
|
} else if (atom == prop_wheel_timeout) |
|
{ |
|
int timeout; |
|
|
|
if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER) |
|
return BadMatch; |
|
|
|
timeout = *((CARD16*)val->data); |
|
|
|
if (timeout < 0) |
|
return BadValue; |
|
|
|
if (!checkonly) |
|
pEvdev->emulateWheel.timeout = timeout; |
|
} |
|
return Success; |
|
} |
|
|
|
void |
|
EvdevWheelEmuInitProperty(DeviceIntPtr dev) |
|
{ |
|
InputInfoPtr pInfo = dev->public.devicePrivate; |
|
EvdevPtr pEvdev = pInfo->private; |
|
int rc = TRUE; |
|
char vals[4]; |
|
|
|
if (!dev->button) /* don't init prop for keyboards */ |
|
return; |
|
|
|
prop_wheel_emu = MakeAtom(EVDEV_PROP_WHEEL, strlen(EVDEV_PROP_WHEEL), TRUE); |
|
rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8, |
|
PropModeReplace, 1, |
|
&pEvdev->emulateWheel.enabled, FALSE); |
|
if (rc != Success) |
|
return; |
|
|
|
XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE); |
|
|
|
vals[0] = pEvdev->emulateWheel.X.up_button; |
|
vals[1] = pEvdev->emulateWheel.X.down_button; |
|
vals[2] = pEvdev->emulateWheel.Y.up_button; |
|
vals[3] = pEvdev->emulateWheel.Y.down_button; |
|
|
|
prop_wheel_axismap = MakeAtom(EVDEV_PROP_WHEEL_AXES, strlen(EVDEV_PROP_WHEEL_AXES), TRUE); |
|
rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8, |
|
PropModeReplace, 4, vals, FALSE); |
|
|
|
if (rc != Success) |
|
return; |
|
|
|
XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE); |
|
|
|
prop_wheel_inertia = MakeAtom(EVDEV_PROP_WHEEL_INERTIA, strlen(EVDEV_PROP_WHEEL_INERTIA), TRUE); |
|
rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16, |
|
PropModeReplace, 1, |
|
&pEvdev->emulateWheel.inertia, FALSE); |
|
if (rc != Success) |
|
return; |
|
|
|
XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE); |
|
|
|
prop_wheel_timeout = MakeAtom(EVDEV_PROP_WHEEL_TIMEOUT, strlen(EVDEV_PROP_WHEEL_TIMEOUT), TRUE); |
|
rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 16, |
|
PropModeReplace, 1, |
|
&pEvdev->emulateWheel.timeout, FALSE); |
|
if (rc != Success) |
|
return; |
|
|
|
XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE); |
|
|
|
prop_wheel_button = MakeAtom(EVDEV_PROP_WHEEL_BUTTON, strlen(EVDEV_PROP_WHEEL_BUTTON), TRUE); |
|
rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8, |
|
PropModeReplace, 1, |
|
&pEvdev->emulateWheel.button, FALSE); |
|
if (rc != Success) |
|
return; |
|
|
|
XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE); |
|
|
|
XIRegisterPropertyHandler(dev, EvdevWheelEmuSetProperty, NULL, NULL); |
|
}
|
|
|