From a9d72b40fbe178fa4fbb9d0e7c02dc6c5250969a Mon Sep 17 00:00:00 2001 From: Chris Salch Date: Wed, 6 Aug 2008 22:08:13 -0500 Subject: Adding mouse wheel emulation code. Signed-off-by: Peter Hutterer --- man/evdev.man | 44 +++++++++- src/Makefile.am | 4 +- src/emuWheel.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/evdev.c | 11 +++ src/evdev.h | 20 +++++ 5 files changed, 334 insertions(+), 2 deletions(-) create mode 100644 src/emuWheel.c diff --git a/man/evdev.man b/man/evdev.man index 0a4b71a..3b0f3b1 100644 --- a/man/evdev.man +++ b/man/evdev.man @@ -77,7 +77,49 @@ buttons can have the same mapping. For example, a left-handed mouse with deactivated scroll-wheel would use a mapping of "3 2 1 0 0". Invalid mappings are ignored and the default mapping is used. Buttons not specified in the user's mapping use the default mapping. - +.TP 7 +.BI "Option \*qEmulateWheel\*q \*q" boolean \*q +Enable/disable "wheel" emulation. Wheel emulation means emulating button +press/release events when the mouse is moved while a specific real button +is pressed. Wheel button events (typically buttons 4 and 5) are +usually used for scrolling. Wheel emulation is useful for getting wheel-like +behaviour with trackballs. It can also be useful for mice with 4 or +more buttons but no wheel. See the description of the +.BR EmulateWheelButton , +.BR EmulateWheelInertia , +.BR XAxisMapping , +and +.B YAxisMapping +options below. Default: off. +.TP 7 +.BI "Option \*qEmulateWheelButton\*q \*q" integer \*q +Specifies which button must be held down to enable wheel emulation mode. +While this button is down, X and/or Y pointer movement will generate button +press/release events as specified for the +.B XAxisMapping +and +.B YAxisMapping +settings. Default: 4. +.TP 7 +.BI "Option \*qEmulateWheelInertia\*q \*q" integer \*q +Specifies how far (in pixels) the pointer must move to generate button +press/release events in wheel emulation mode. Default: 10. +.TP 7 +.BI "Option \*qXAxisMapping\*q \*q" "N1 N2" \*q +Specifies which buttons are mapped to motion in the X direction in wheel +emulation mode. Button number +.I N1 +is mapped to the negative X axis motion and button number +.I N2 +is mapped to the positive X axis motion. Default: no mapping. +.TP 7 +.BI "Option \*qYAxisMapping\*q \*q" "N1 N2" \*q +Specifies which buttons are mapped to motion in the Y direction in wheel +emulation mode. Button number +.I N1 +is mapped to the negative Y axis motion and button number +.I N2 +is mapped to the positive Y axis motion. Default: "4 5" .SH AUTHORS Kristian Høgsberg. .SH "SEE ALSO" diff --git a/src/Makefile.am b/src/Makefile.am index d0b9b63..e6bb64e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,4 +30,6 @@ @DRIVER_NAME@_drv_la_SOURCES = @DRIVER_NAME@.c \ @DRIVER_NAME@.h \ - emuMB.c + emuMB.c \ + emuWheel.c + diff --git a/src/emuWheel.c b/src/emuWheel.c new file mode 100644 index 0000000..e6c7f6e --- /dev/null +++ b/src/emuWheel.c @@ -0,0 +1,257 @@ +/* +* Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. +* Copyright 1993 by David Dawes +* 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 +* +* 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" + +#define MSE_MAXBUTTONS 32 +#define WHEEL_NOT_CONFIGURED 0 + +/* Local Funciton Prototypes */ +static BOOL EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name); +static void 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; + + /* 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; + + 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; + 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*/ + if (pEvdev->emulateWheel.button_state) { + /* We don't want to intercept real mouse wheel events */ + switch(pEv->code) { + case REL_X: + pAxis = &(pEvdev->emulateWheel.X); + break; + + case REL_Y: + pAxis = &(pEvdev->emulateWheel.Y); + break; + + default: + break; + } + + /* If we found REL_X or REL_Y, emulate a mouse wheel */ + if (pAxis) + EvdevWheelEmuInertia(pInfo, pAxis, value); + + /* Eat motion events while emulateWheel button pressed. */ + return TRUE; + } + + return FALSE; +} + +/* Simulate inertia for our emulated mouse wheel */ +static void +EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + int button; + int inertia; + + /* if this axis has not been configured, just eat the motion */ + 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; + } + + /* Produce button press events for wheel motion */ + 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); + } +} + +/* Handle button mapping here to avoid code duplication, +returns true if a button mapping was found. */ +static BOOL +EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, 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 <= MSE_MAXBUTTONS)) && + ((down_button > 0) && (down_button <= MSE_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->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); + + } + + /* Clean up and log what happened */ + if (msg) { + xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg); + xfree(msg); + return TRUE; + } + } + return FALSE; +} + +/* Setup the basic configuration options used by mouse wheel emulation */ +void +EvdevWheelEmuPreInit(InputInfoPtr pInfo) +{ + EvdevPtr pEvdev = (EvdevPtr)pInfo->private; + 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 > MSE_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; + + /* 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->buttons) + pEvdev->buttons = 5; + + /* Display default Configuration */ + xf86Msg(X_CONFIG, "%s: YAxisMapping: buttons %d and %d\n", + pInfo->name, 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; + + xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, EmulateWheelInertia: %d\n", + pInfo->name, pEvdev->emulateWheel.button, inertia); + } +} + diff --git a/src/evdev.c b/src/evdev.c index bbabb56..5bc562a 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -217,11 +217,16 @@ EvdevReadInput(InputInfoPtr pInfo) break; } + /* Get the signed value, earlier kernels had this as unsigned */ value = ev.value; switch (ev.type) { case EV_REL: + /* Handle mouse wheel emulation */ + if (EvdevWheelEmuFilterMotion(pInfo, &ev)) + break; + switch (ev.code) { case REL_X: dx += value; @@ -286,6 +291,9 @@ EvdevReadInput(InputInfoPtr pInfo) if (EvdevMBEmuFilterEvent(pInfo, button, value)) break; + if (EvdevWheelEmuFilterButton(pInfo, button, value)) + break; + if (button) xf86PostButtonEvent(pInfo->dev, 0, button, value, 0, 0); else @@ -943,7 +951,10 @@ EvdevProc(DeviceIntPtr device, int what) { xf86AddEnabledDevice(pInfo); if (pEvdev->flags & EVDEV_BUTTON_EVENTS) + { EvdevMBEmuPreInit(pInfo); + EvdevWheelEmuPreInit(pInfo); + } device->public.on = TRUE; } break; diff --git a/src/evdev.h b/src/evdev.h index 56983c2..83f03f4 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -40,6 +40,13 @@ #include #endif +/* axis specific data for wheel emulation */ +typedef struct { + int up_button; + int down_button; + int traveled_distance; +} WheelAxis, *WheelAxisPtr; + typedef struct { int kernel24; int screen; @@ -68,6 +75,14 @@ typedef struct { Time expires; /* time of expiry */ Time timeout; } emulateMB; + struct { + BOOL enabled; + int button; + int button_state; + int inertia; + WheelAxis X; + WheelAxis Y; + } emulateWheel; unsigned char btnmap[32]; /* config-file specified button mapping */ } EvdevRec, *EvdevPtr; @@ -89,4 +104,9 @@ Atom EvdevMBEmuInitPropertyTimeout(DeviceIntPtr, char*); BOOL EvdevMBEmuSetProperty(DeviceIntPtr, Atom, XIPropertyValuePtr); #endif +/* Mouse Wheel emulation */ +void EvdevWheelEmuPreInit(InputInfoPtr pInfo); +BOOL EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value); +BOOL EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv); + #endif -- cgit v1.2.3