From 2ddd47f233e70e87cfa4a686fbdbe63f721c2f38 Mon Sep 17 00:00:00 2001 From: Chris Salch Date: Mon, 4 Aug 2008 21:46:44 -0500 Subject: [PATCH] Adding in mouse wheel emulation using button number function --- man/evdev.man | 44 ++++++++++- src/Makefile.am | 4 +- src/emuWheel.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/evdev.c | 12 +++ src/evdev.h | 19 ++++ 5 files changed, 326 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..b08d4a8 --- /dev/null +++ b/src/emuWheel.c @@ -0,0 +1,249 @@ +/* + * 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 University of South Australia + * 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 wheel events */ +BOOL +EvdevWheelEmuFilterEvent(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 */ + switch (pEv->type) { + case EV_REL: + 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; + } + } + + break; + + case EV_ABS: + break; + + case EV_KEY: + /* Check for EmulateWheelButton */ + if (EvdevUtilButtonEventToButtonNumber(pEv->code) == pEvdev->emulateWheel.button) { + pEvdev->emulateWheel.button_state = value; + return TRUE; + } + + break; + } + + /* if we set an axis, the emulate button is pressed. */ + if (pAxis) { + EvdevWheelEmuInertia(pInfo, pAxis, value); + 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)) { + Bool yFromConfig = 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); + + wheelButton = 4; + } + + 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); + + 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 */ + 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 b149459..898bed8 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -206,11 +206,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 (EvdevWheelEmuFilterEvent(pInfo, &ev)) + break; + switch (ev.code) { case REL_X: dx += value; @@ -256,6 +261,10 @@ EvdevReadInput(InputInfoPtr pInfo) if (value == 2) break; + /* Handle mouse wheel emulation */ + if (EvdevWheelEmuFilterEvent(pInfo, &ev)) + break; + switch (ev.code) { /* swap here, pretend we're an X-conformant device. */ case BTN_LEFT: @@ -957,7 +966,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 692e381..d5c94ea 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; @@ -86,5 +101,9 @@ Atom EvdevMBEmuInitPropertyTimeout(DeviceIntPtr, char*); BOOL EvdevMBEmuSetProperty(DeviceIntPtr, Atom, XIPropertyValuePtr); #endif +/* Mouse Wheel emulation */ +void EvdevWheelEmuPreInit(InputInfoPtr pInfo); +BOOL EvdevWheelEmuFilterEvent(InputInfoPtr pInfo, struct input_event *pEv); + unsigned char EvdevUtilButtonEventToButtonNumber(int code); #endif -- 1.5.6.3