2 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
3 * Copyright 1993 by David Dawes <dawes@xfree86.org>
4 * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich
5 * Copyright 1994-2002 by The XFree86 Project, Inc.
6 * Copyright 2002 by Paul Elliott
7 * (Ported from xf86-input-mouse, above copyrights taken from there)
8 * Copyright 2008 by Chris Salch
9 * Copyright © 2008 Red Hat, Inc.
11 * Permission to use, copy, modify, distribute, and sell this software
12 * and its documentation for any purpose is hereby granted without
13 * fee, provided that the above copyright notice appear in all copies
14 * and that both that copyright notice and this permission notice
15 * appear in supporting documentation, and that the name of the authors
16 * not be used in advertising or publicity pertaining to distribution of the
17 * software without specific, written prior permission. The authors make no
18 * representations about the suitability of this software for any
19 * purpose. It is provided "as is" without express or implied
22 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
23 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
24 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
25 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
26 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
27 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
28 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
32 /* Mouse wheel emulation code. */
38 #include <X11/Xatom.h>
40 #include <xf86Xinput.h>
43 #include <evdev-properties.h>
45 #define WHEEL_NOT_CONFIGURED 0
47 static Atom prop_wheel_emu = 0;
48 static Atom prop_wheel_axismap = 0;
49 static Atom prop_wheel_inertia = 0;
50 static Atom prop_wheel_timeout = 0;
51 static Atom prop_wheel_button = 0;
53 /* Local Funciton Prototypes */
54 static BOOL EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name);
55 static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value);
57 /* Filter mouse button events */
59 EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value)
61 EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
64 /* Has wheel emulation been configured to be enabled? */
65 if (!pEvdev->emulateWheel.enabled)
68 /* Check for EmulateWheelButton */
69 if (pEvdev->emulateWheel.button == button) {
70 pEvdev->emulateWheel.button_state = value;
73 /* Start the timer when the button is pressed */
74 pEvdev->emulateWheel.expires = pEvdev->emulateWheel.timeout +
77 ms = pEvdev->emulateWheel.expires - GetTimeInMillis();
80 * If the button is released early enough emit the button
81 * press/release events
83 EvdevQueueButtonClicks(pInfo, button, 1);
90 /* Don't care about this event */
94 /* Filter mouse wheel events */
96 EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
98 EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
99 WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
100 int value = pEv->value;
103 /* Has wheel emulation been configured to be enabled? */
104 if (!pEvdev->emulateWheel.enabled)
107 /* Handle our motion events if the emuWheel button is pressed
108 * wheel button of 0 means always emulate wheel.
110 if (pEvdev->emulateWheel.button_state || !pEvdev->emulateWheel.button) {
111 /* Just return if the timeout hasn't expired yet */
112 if (pEvdev->emulateWheel.button)
114 int ms = pEvdev->emulateWheel.expires - GetTimeInMillis();
119 /* We don't want to intercept real mouse wheel events */
120 if(pEv->type == EV_ABS) {
121 int axis = pEvdev->axis_map[pEv->code];
122 oldValue = valuator_mask_get(pEvdev->vals, axis);
123 valuator_mask_set(pEvdev->vals, axis, value);
124 value -= oldValue; /* make value into a differential measurement */
129 /* ABS_X has the same value as REL_X, so this case catches both */
131 pAxis = &(pEvdev->emulateWheel.X);
132 pOtherAxis = &(pEvdev->emulateWheel.Y);
135 /* ABS_Y has the same value as REL_Y, so this case catches both */
137 pAxis = &(pEvdev->emulateWheel.Y);
138 pOtherAxis = &(pEvdev->emulateWheel.X);
145 /* If we found REL_X, REL_Y, ABS_X or ABS_Y then emulate a mouse
146 wheel. Reset the inertia of the other axis when a scroll event
147 was sent to avoid the buildup of erroneous scroll events if the
148 user doesn't move in a perfectly straight line.
152 if (EvdevWheelEmuInertia(pInfo, pAxis, value))
153 pOtherAxis->traveled_distance = 0;
156 /* Eat motion events while emulateWheel button pressed. */
163 /* Simulate inertia for our emulated mouse wheel.
164 Returns the number of wheel events generated.
167 EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
169 EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
174 /* if this axis has not been configured, just eat the motion */
175 if (!axis->up_button)
178 axis->traveled_distance += value;
180 if (axis->traveled_distance < 0) {
181 button = axis->up_button;
182 inertia = -pEvdev->emulateWheel.inertia;
184 button = axis->down_button;
185 inertia = pEvdev->emulateWheel.inertia;
188 /* Produce button press events for wheel motion */
189 while(abs(axis->traveled_distance) > pEvdev->emulateWheel.inertia) {
190 axis->traveled_distance -= inertia;
191 EvdevQueueButtonClicks(pInfo, button, 1);
197 /* Handle button mapping here to avoid code duplication,
198 returns true if a button mapping was found. */
200 EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name)
202 EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
205 pAxis->up_button = WHEEL_NOT_CONFIGURED;
207 /* Check to see if there is configuration for this axis */
208 option_string = xf86SetStrOption(pInfo->options, axis_name, NULL);
214 if ((sscanf(option_string, "%d %d", &up_button, &down_button) == 2) &&
215 ((up_button > 0) && (up_button <= EVDEV_MAXBUTTONS)) &&
216 ((down_button > 0) && (down_button <= EVDEV_MAXBUTTONS))) {
218 /* Use xstrdup to allocate a string for us */
219 msg = xstrdup("buttons XX and YY");
222 sprintf(msg, "buttons %d and %d", up_button, down_button);
224 pAxis->up_button = up_button;
225 pAxis->down_button = down_button;
227 /* Update the number of buttons if needed */
228 if (up_button > pEvdev->num_buttons) pEvdev->num_buttons = up_button;
229 if (down_button > pEvdev->num_buttons) pEvdev->num_buttons = down_button;
232 xf86IDrvMsg(pInfo, X_WARNING, "Invalid %s value:\"%s\"\n",
233 axis_name, option_string);
237 /* Clean up and log what happened */
239 xf86IDrvMsg(pInfo, X_CONFIG, "%s: %s\n", axis_name, msg);
247 /* Setup the basic configuration options used by mouse wheel emulation */
249 EvdevWheelEmuPreInit(InputInfoPtr pInfo)
251 EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
256 if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) {
257 pEvdev->emulateWheel.enabled = TRUE;
259 pEvdev->emulateWheel.enabled = FALSE;
261 wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4);
263 if ((wheelButton < 0) || (wheelButton > EVDEV_MAXBUTTONS)) {
264 xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelButton value: %d\n",
266 xf86IDrvMsg(pInfo, X_WARNING, "Wheel emulation disabled.\n");
268 pEvdev->emulateWheel.enabled = FALSE;
271 pEvdev->emulateWheel.button = wheelButton;
273 inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
276 xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelInertia value: %d\n",
278 xf86IDrvMsg(pInfo, X_WARNING, "Using built-in inertia value.\n");
283 pEvdev->emulateWheel.inertia = inertia;
285 timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200);
288 xf86IDrvMsg(pInfo, X_WARNING, "Invalid EmulateWheelTimeout value: %d\n",
290 xf86IDrvMsg(pInfo, X_WARNING, "Using built-in timeout value.\n");
295 pEvdev->emulateWheel.timeout = timeout;
297 /* Configure the Y axis or default it */
298 if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y),
300 /* Default the Y axis to sane values */
301 pEvdev->emulateWheel.Y.up_button = 4;
302 pEvdev->emulateWheel.Y.down_button = 5;
304 /* Simpler to check just the largest value in this case */
305 /* XXX: we should post this to the server */
306 if (5 > pEvdev->num_buttons)
307 pEvdev->num_buttons = 5;
309 /* Display default Configuration */
310 xf86IDrvMsg(pInfo, X_CONFIG, "YAxisMapping: buttons %d and %d\n",
311 pEvdev->emulateWheel.Y.up_button,
312 pEvdev->emulateWheel.Y.down_button);
316 /* This axis should default to an unconfigured state as most people
317 are not going to expect a Horizontal wheel. */
318 EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.X),
321 /* Used by the inertia code */
322 pEvdev->emulateWheel.X.traveled_distance = 0;
323 pEvdev->emulateWheel.Y.traveled_distance = 0;
325 xf86IDrvMsg(pInfo, X_CONFIG,
326 "EmulateWheelButton: %d, "
327 "EmulateWheelInertia: %d, "
328 "EmulateWheelTimeout: %d\n",
329 pEvdev->emulateWheel.button, inertia, timeout);
333 EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
336 InputInfoPtr pInfo = dev->public.devicePrivate;
337 EvdevPtr pEvdev = pInfo->private;
339 if (atom == prop_wheel_emu)
341 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
346 pEvdev->emulateWheel.enabled = *((BOOL*)val->data);
347 /* Don't enable with zero inertia, otherwise we may get stuck in an
349 if (pEvdev->emulateWheel.inertia <= 0)
351 pEvdev->emulateWheel.inertia = 10;
352 /* We may get here before the property is actually enabled */
353 if (prop_wheel_inertia)
354 XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER,
355 16, PropModeReplace, 1,
356 &pEvdev->emulateWheel.inertia, TRUE);
360 else if (atom == prop_wheel_button)
364 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
367 bt = *((CARD8*)val->data);
369 if (bt < 0 || bt >= EVDEV_MAXBUTTONS)
373 pEvdev->emulateWheel.button = bt;
374 } else if (atom == prop_wheel_axismap)
376 if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER)
381 pEvdev->emulateWheel.X.up_button = *((CARD8*)val->data);
382 pEvdev->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1);
383 pEvdev->emulateWheel.Y.up_button = *(((CARD8*)val->data) + 2);
384 pEvdev->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 3);
386 } else if (atom == prop_wheel_inertia)
390 if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
393 inertia = *((CARD16*)val->data);
399 pEvdev->emulateWheel.inertia = inertia;
400 } else if (atom == prop_wheel_timeout)
404 if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
407 timeout = *((CARD16*)val->data);
413 pEvdev->emulateWheel.timeout = timeout;
419 EvdevWheelEmuInitProperty(DeviceIntPtr dev)
421 InputInfoPtr pInfo = dev->public.devicePrivate;
422 EvdevPtr pEvdev = pInfo->private;
426 if (!dev->button) /* don't init prop for keyboards */
429 prop_wheel_emu = MakeAtom(EVDEV_PROP_WHEEL, strlen(EVDEV_PROP_WHEEL), TRUE);
430 rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
432 &pEvdev->emulateWheel.enabled, FALSE);
436 XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE);
438 vals[0] = pEvdev->emulateWheel.X.up_button;
439 vals[1] = pEvdev->emulateWheel.X.down_button;
440 vals[2] = pEvdev->emulateWheel.Y.up_button;
441 vals[3] = pEvdev->emulateWheel.Y.down_button;
443 prop_wheel_axismap = MakeAtom(EVDEV_PROP_WHEEL_AXES, strlen(EVDEV_PROP_WHEEL_AXES), TRUE);
444 rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8,
445 PropModeReplace, 4, vals, FALSE);
450 XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE);
452 prop_wheel_inertia = MakeAtom(EVDEV_PROP_WHEEL_INERTIA, strlen(EVDEV_PROP_WHEEL_INERTIA), TRUE);
453 rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
455 &pEvdev->emulateWheel.inertia, FALSE);
459 XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE);
461 prop_wheel_timeout = MakeAtom(EVDEV_PROP_WHEEL_TIMEOUT, strlen(EVDEV_PROP_WHEEL_TIMEOUT), TRUE);
462 rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 16,
464 &pEvdev->emulateWheel.timeout, FALSE);
468 XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE);
470 prop_wheel_button = MakeAtom(EVDEV_PROP_WHEEL_BUTTON, strlen(EVDEV_PROP_WHEEL_BUTTON), TRUE);
471 rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8,
473 &pEvdev->emulateWheel.button, FALSE);
477 XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE);
479 XIRegisterPropertyHandler(dev, EvdevWheelEmuSetProperty, NULL, NULL);