#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <X11/Xatom.h>
#include <xf86.h>
#include <xf86Xinput.h>
#include <exevents.h>
#include "evdev.h"
#define WHEEL_NOT_CONFIGURED 0
static const char *propname_wheel_emu = "Wheel Emulation";
static const char *propname_wheel_xmap = "Wheel Emulation X Axis";
static const char *propname_wheel_ymap = "Wheel Emulation Y Axis";
static const char *propname_wheel_inertia = "Wheel Emulation Inertia";
static const char *propname_wheel_button = "Wheel Emulation Button";
static Atom prop_wheel_emu;
static Atom prop_wheel_xmap;
static Atom prop_wheel_ymap;
static Atom prop_wheel_inertia;
static Atom prop_wheel_button;
static BOOL EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name);
static void EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value);
BOOL
EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
if (!pEvdev->emulateWheel.enabled)
return FALSE;
if (pEvdev->emulateWheel.button == button) {
pEvdev->emulateWheel.button_state = value;
return TRUE;
}
return FALSE;
}
BOOL
EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
WheelAxisPtr pAxis = NULL;
int value = pEv->value;
if (!pEvdev->emulateWheel.enabled)
return FALSE;
if (pEvdev->emulateWheel.button_state) {
switch(pEv->code) {
case REL_X:
pAxis = &(pEvdev->emulateWheel.X);
break;
case REL_Y:
pAxis = &(pEvdev->emulateWheel.Y);
break;
default:
break;
}
if (pAxis)
EvdevWheelEmuInertia(pInfo, pAxis, value);
return TRUE;
}
return FALSE;
}
static void
EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
int button;
int inertia;
if (!axis->up_button)
return;
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;
}
while(abs(axis->traveled_distance) > pEvdev->emulateWheel.inertia) {
axis->traveled_distance -= inertia;
xf86PostButtonEvent(pInfo->dev, 0, button, 1, 0, 0);
xf86PostButtonEvent(pInfo->dev, 0, button, 0, 0, 0);
}
}
static BOOL
EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
char *option_string;
pAxis->up_button = WHEEL_NOT_CONFIGURED;
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))) {
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;
if (up_button > pEvdev->buttons) pEvdev->buttons = up_button;
if (down_button > pEvdev->buttons) pEvdev->buttons = down_button;
} else {
xf86Msg(X_WARNING, "%s: Invalid %s value:\"%s\"\n",
pInfo->name, axis_name, option_string);
}
if (msg) {
xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg);
xfree(msg);
return TRUE;
}
}
return FALSE;
}
void
EvdevWheelEmuPreInit(InputInfoPtr pInfo)
{
EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
int val[2];
pEvdev->emulateWheel.enabled = FALSE;
if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) {
int wheelButton;
int inertia;
pEvdev->emulateWheel.enabled = TRUE;
wheelButton = xf86SetIntOption(pInfo->options,
"EmulateWheelButton", 4);
if ((wheelButton < 0) || (wheelButton > EVDEV_MAXBUTTONS)) {
xf86Msg(X_WARNING, "%s: Invalid EmulateWheelButton value: %d\n",
pInfo->name, wheelButton);
xf86Msg(X_WARNING, "%s: Wheel emulation disabled.\n", pInfo->name);
pEvdev->emulateWheel.enabled = FALSE;
return;
}
pEvdev->emulateWheel.button = wheelButton;
inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
if (inertia <= 0) {
xf86Msg(X_WARNING, "%s: Invalid EmulateWheelInertia value: %d\n",
pInfo->name, inertia);
xf86Msg(X_WARNING, "%s: Using built-in inertia value.\n",
pInfo->name);
inertia = 10;
}
pEvdev->emulateWheel.inertia = inertia;
if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y),
"YAxisMapping")) {
pEvdev->emulateWheel.Y.up_button = 4;
pEvdev->emulateWheel.Y.down_button = 5;
if (5 > pEvdev->buttons)
pEvdev->buttons = 5;
xf86Msg(X_CONFIG, "%s: YAxisMapping: buttons %d and %d\n",
pInfo->name, pEvdev->emulateWheel.Y.up_button,
pEvdev->emulateWheel.Y.down_button);
}
EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.X),
"XAxisMapping");
pEvdev->emulateWheel.X.traveled_distance = 0;
pEvdev->emulateWheel.Y.traveled_distance = 0;
xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, EmulateWheelInertia: %d\n",
pInfo->name, pEvdev->emulateWheel.button, inertia);
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
XIChangeDeviceProperty(pInfo->dev, prop_wheel_emu, XA_INTEGER, 8,
PropModeReplace, 1, &pEvdev->emulateWheel.enabled,
TRUE, FALSE, FALSE);
XIChangeDeviceProperty(pInfo->dev, prop_wheel_button, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateWheel.button,
TRUE, FALSE, FALSE);
XIChangeDeviceProperty(pInfo->dev, prop_wheel_inertia, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateWheel.inertia,
TRUE, FALSE, FALSE);
val[0] = pEvdev->emulateWheel.X.up_button;
val[1] = pEvdev->emulateWheel.X.down_button;
XIChangeDeviceProperty(pInfo->dev, prop_wheel_xmap, XA_INTEGER, 8,
PropModeReplace, 2, val,
TRUE, FALSE, FALSE);
val[0] = pEvdev->emulateWheel.Y.up_button;
val[1] = pEvdev->emulateWheel.Y.down_button;
XIChangeDeviceProperty(pInfo->dev, prop_wheel_ymap, XA_INTEGER, 8,
PropModeReplace, 2, val,
TRUE, FALSE, FALSE);
#endif
}
}
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3
void
EvdevWheelEmuInitProperty(DeviceIntPtr dev)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
int rc = TRUE;
INT32 valid_vals[] = { TRUE, FALSE};
if (!dev->button)
return;
prop_wheel_emu = MakeAtom((char*)propname_wheel_emu, strlen(propname_wheel_emu), TRUE);
rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateWheel.enabled,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_wheel_emu, FALSE, FALSE,
FALSE, 2, valid_vals);
if (rc != Success)
return;
valid_vals[0] = pEvdev->emulateWheel.X.up_button;
valid_vals[1] = pEvdev->emulateWheel.X.down_button;
prop_wheel_xmap = MakeAtom((char*)propname_wheel_xmap, strlen(propname_wheel_xmap), TRUE);
rc = XIChangeDeviceProperty(dev, prop_wheel_xmap, XA_INTEGER, 8,
PropModeReplace, 2, valid_vals,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_wheel_xmap, FALSE, FALSE,
FALSE, 0, NULL);
if (rc != Success)
return;
valid_vals[0] = pEvdev->emulateWheel.Y.up_button;
valid_vals[1] = pEvdev->emulateWheel.Y.down_button;
prop_wheel_ymap = MakeAtom((char*)propname_wheel_ymap, strlen(propname_wheel_ymap), TRUE);
rc = XIChangeDeviceProperty(dev, prop_wheel_ymap, XA_INTEGER, 8,
PropModeReplace, 2, valid_vals,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_wheel_ymap, FALSE, FALSE,
FALSE, 0, NULL);
prop_wheel_inertia = MakeAtom((char*)propname_wheel_inertia, strlen(propname_wheel_inertia), TRUE);
rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
PropModeReplace, 1,
&pEvdev->emulateWheel.inertia,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_wheel_inertia, FALSE, FALSE,
FALSE, 0, NULL);
if (rc != Success)
return;
prop_wheel_button = MakeAtom((char*)propname_wheel_button, strlen(propname_wheel_button), TRUE);
rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8,
PropModeReplace, 1,
&pEvdev->emulateWheel.button,
FALSE, FALSE, FALSE);
if (rc != Success)
return;
rc = XIConfigureDeviceProperty(dev, prop_wheel_button, FALSE, FALSE,
FALSE, 0, NULL);
if (rc != Success)
return;
}
BOOL
EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val)
{
InputInfoPtr pInfo = dev->public.devicePrivate;
EvdevPtr pEvdev = pInfo->private;
if (atom == prop_wheel_emu)
{
pEvdev->emulateWheel.enabled = *((BOOL*)val->data);
if (pEvdev->emulateWheel.inertia <= 0)
{
pEvdev->emulateWheel.inertia = 10;
XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
PropModeReplace, 1,
&pEvdev->emulateWheel.inertia,
TRUE, FALSE, FALSE);
}
}
else if (atom == prop_wheel_button)
{
int bt = *((CARD8*)val->data);
if (bt < 0 || bt >= EVDEV_MAXBUTTONS)
return FALSE;
pEvdev->emulateWheel.button = bt;
} else if (atom == prop_wheel_xmap)
{
if (val->size != 2)
return FALSE;
pEvdev->emulateWheel.X.up_button = *((CARD8*)val->data);
pEvdev->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1);
} else if (atom == prop_wheel_ymap)
{
if (val->size != 2)
return FALSE;
pEvdev->emulateWheel.Y.up_button = *((CARD8*)val->data);
pEvdev->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 1);
} else if (atom == prop_wheel_inertia)
{
int inertia = *((CARD16*)val->data);
if (inertia < 0)
return FALSE;
pEvdev->emulateWheel.inertia = inertia;
}
return TRUE;
}
#endif