2 * xserver-xorg-input-evdev-multitouch
4 * Contact: Sung-Jin Park <sj76.park@samsung.com>
5 * Sangjin LEE <lsj119@samsung.com>
7 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
9 * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
10 * Copyright 1993 by David Dawes <dawes@xfree86.org>
11 * Copyright 2002 by SuSE Linux AG, Author: Egbert Eich
12 * Copyright 1994-2002 by The XFree86 Project, Inc.
13 * Copyright 2002 by Paul Elliott
14 * (Ported from xf86-input-mouse, above copyrights taken from there)
15 * Copyright 2008 by Chris Salch
16 * Copyright © 2008 Red Hat, Inc.
18 * Permission to use, copy, modify, distribute, and sell this software
19 * and its documentation for any purpose is hereby granted without
20 * fee, provided that the above copyright notice appear in all copies
21 * and that both that copyright notice and this permission notice
22 * appear in supporting documentation, and that the name of the authors
23 * not be used in advertising or publicity pertaining to distribution of the
24 * software without specific, written prior permission. The authors make no
25 * representations about the suitability of this software for any
26 * purpose. It is provided "as is" without express or implied
29 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
30 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
31 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
32 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
34 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
35 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
39 /* Mouse wheel emulation code. */
44 #include <X11/Xatom.h>
46 #include <xf86Xinput.h>
49 #include <evdevmultitouch-properties.h>
50 #include "evdevmultitouch.h"
52 #define WHEEL_NOT_CONFIGURED 0
54 #ifdef HAVE_PROPERTIES
55 static Atom prop_wheel_emu = 0;
56 static Atom prop_wheel_axismap = 0;
57 static Atom prop_wheel_inertia = 0;
58 static Atom prop_wheel_timeout = 0;
59 static Atom prop_wheel_button = 0;
62 /* Local Funciton Prototypes */
63 static BOOL EvdevMultitouchWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name);
64 static int EvdevMultitouchWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value);
66 /* Filter mouse button events */
68 EvdevMultitouchWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value)
70 EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
73 /* Has wheel emulation been configured to be enabled? */
74 if (!pEvdevMultitouch->emulateWheel.enabled)
77 /* Check for EmulateWheelButton */
78 if (pEvdevMultitouch->emulateWheel.button == button) {
79 pEvdevMultitouch->emulateWheel.button_state = value;
82 /* Start the timer when the button is pressed */
83 pEvdevMultitouch->emulateWheel.expires = pEvdevMultitouch->emulateWheel.timeout +
86 ms = pEvdevMultitouch->emulateWheel.expires - GetTimeInMillis();
89 * If the button is released early enough emit the button
90 * press/release events
92 EvdevMultitouchQueueButtonClicks(pInfo, button, 1);
99 /* Don't care about this event */
103 /* Filter mouse wheel events */
105 EvdevMultitouchWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
107 EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
108 WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
109 int value = pEv->value;
111 /* Has wheel emulation been configured to be enabled? */
112 if (!pEvdevMultitouch->emulateWheel.enabled)
115 /* Handle our motion events if the emuWheel button is pressed
116 * wheel button of 0 means always emulate wheel.
118 if (pEvdevMultitouch->emulateWheel.button_state || !pEvdevMultitouch->emulateWheel.button) {
119 /* Just return if the timeout hasn't expired yet */
120 if (pEvdevMultitouch->emulateWheel.button)
122 int ms = pEvdevMultitouch->emulateWheel.expires - GetTimeInMillis();
127 /* We don't want to intercept real mouse wheel events */
130 pAxis = &(pEvdevMultitouch->emulateWheel.X);
131 pOtherAxis = &(pEvdevMultitouch->emulateWheel.Y);
135 pAxis = &(pEvdevMultitouch->emulateWheel.Y);
136 pOtherAxis = &(pEvdevMultitouch->emulateWheel.X);
143 /* If we found REL_X or REL_Y, emulate a mouse wheel.
144 Reset the inertia of the other axis when a scroll event was sent
145 to avoid the buildup of erroneous scroll events if the user
146 doesn't move in a perfectly straight line.
150 if (EvdevMultitouchWheelEmuInertia(pInfo, pAxis, value))
151 pOtherAxis->traveled_distance = 0;
154 /* Eat motion events while emulateWheel button pressed. */
161 /* Simulate inertia for our emulated mouse wheel.
162 Returns the number of wheel events generated.
165 EvdevMultitouchWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
167 EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
172 /* if this axis has not been configured, just eat the motion */
173 if (!axis->up_button)
176 axis->traveled_distance += value;
178 if (axis->traveled_distance < 0) {
179 button = axis->up_button;
180 inertia = -pEvdevMultitouch->emulateWheel.inertia;
182 button = axis->down_button;
183 inertia = pEvdevMultitouch->emulateWheel.inertia;
186 /* Produce button press events for wheel motion */
187 while(abs(axis->traveled_distance) > pEvdevMultitouch->emulateWheel.inertia) {
188 axis->traveled_distance -= inertia;
189 EvdevMultitouchQueueButtonClicks(pInfo, button, 1);
195 /* Handle button mapping here to avoid code duplication,
196 returns true if a button mapping was found. */
198 EvdevMultitouchWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name)
200 EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
203 pAxis->up_button = WHEEL_NOT_CONFIGURED;
205 /* Check to see if there is configuration for this axis */
206 option_string = xf86SetStrOption(pInfo->options, axis_name, NULL);
212 if ((sscanf(option_string, "%d %d", &up_button, &down_button) == 2) &&
213 ((up_button > 0) && (up_button <= EVDEVMULTITOUCH_MAXBUTTONS)) &&
214 ((down_button > 0) && (down_button <= EVDEVMULTITOUCH_MAXBUTTONS))) {
216 /* Use xstrdup to allocate a string for us */
217 msg = xstrdup("buttons XX and YY");
220 sprintf(msg, "buttons %d and %d", up_button, down_button);
222 pAxis->up_button = up_button;
223 pAxis->down_button = down_button;
225 /* Update the number of buttons if needed */
226 if (up_button > pEvdevMultitouch->num_buttons) pEvdevMultitouch->num_buttons = up_button;
227 if (down_button > pEvdevMultitouch->num_buttons) pEvdevMultitouch->num_buttons = down_button;
230 xf86Msg(X_WARNING, "%s: Invalid %s value:\"%s\"\n",
231 pInfo->name, axis_name, option_string);
235 /* Clean up and log what happened */
237 xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg);
245 /* Setup the basic configuration options used by mouse wheel emulation */
247 EvdevMultitouchWheelEmuPreInit(InputInfoPtr pInfo)
249 EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
258 if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) {
259 pEvdevMultitouch->emulateWheel.enabled = TRUE;
261 pEvdevMultitouch->emulateWheel.enabled = FALSE;
263 wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4);
265 if ((wheelButton < 0) || (wheelButton > EVDEVMULTITOUCH_MAXBUTTONS)) {
266 xf86Msg(X_WARNING, "%s: Invalid EmulateWheelButton value: %d\n",
267 pInfo->name, wheelButton);
268 xf86Msg(X_WARNING, "%s: Wheel emulation disabled.\n", pInfo->name);
270 pEvdevMultitouch->emulateWheel.enabled = FALSE;
273 pEvdevMultitouch->emulateWheel.button = wheelButton;
275 inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
278 xf86Msg(X_WARNING, "%s: Invalid EmulateWheelInertia value: %d\n",
279 pInfo->name, inertia);
280 xf86Msg(X_WARNING, "%s: Using built-in inertia value.\n",
286 pEvdevMultitouch->emulateWheel.inertia = inertia;
288 timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200);
291 xf86Msg(X_WARNING, "%s: Invalid EmulateWheelTimeout value: %d\n",
292 pInfo->name, timeout);
293 xf86Msg(X_WARNING, "%s: Using built-in timeout value.\n",
299 pEvdevMultitouch->emulateWheel.timeout = timeout;
301 /* Configure the Y axis or default it */
302 if (!EvdevMultitouchWheelEmuHandleButtonMap(pInfo, &(pEvdevMultitouch->emulateWheel.Y),
304 /* Default the Y axis to sane values */
305 pEvdevMultitouch->emulateWheel.Y.up_button = 4;
306 pEvdevMultitouch->emulateWheel.Y.down_button = 5;
308 /* Simpler to check just the largest value in this case */
309 /* XXX: we should post this to the server */
310 if (5 > pEvdevMultitouch->num_buttons)
311 pEvdevMultitouch->num_buttons = 5;
313 /* Display default Configuration */
314 xf86Msg(X_CONFIG, "%s: YAxisMapping: buttons %d and %d\n",
315 pInfo->name, pEvdevMultitouch->emulateWheel.Y.up_button,
316 pEvdevMultitouch->emulateWheel.Y.down_button);
320 /* This axis should default to an unconfigured state as most people
321 are not going to expect a Horizontal wheel. */
322 EvdevMultitouchWheelEmuHandleButtonMap(pInfo, &(pEvdevMultitouch->emulateWheel.X),
325 /* Used by the inertia code */
326 pEvdevMultitouch->emulateWheel.X.traveled_distance = 0;
327 pEvdevMultitouch->emulateWheel.Y.traveled_distance = 0;
329 xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, "
330 "EmulateWheelInertia: %d, "
331 "EmulateWheelTimeout: %d\n",
332 pInfo->name, pEvdevMultitouch->emulateWheel.button, inertia, timeout);
335 #ifdef HAVE_PROPERTIES
337 EvdevMultitouchWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
340 InputInfoPtr pInfo = dev->public.devicePrivate;
341 EvdevMultitouchPtr pEvdevMultitouch = pInfo->private;
343 if (atom == prop_wheel_emu)
345 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
350 pEvdevMultitouch->emulateWheel.enabled = *((BOOL*)val->data);
351 /* Don't enable with zero inertia, otherwise we may get stuck in an
353 if (pEvdevMultitouch->emulateWheel.inertia <= 0)
355 pEvdevMultitouch->emulateWheel.inertia = 10;
356 /* We may get here before the property is actually enabled */
357 if (prop_wheel_inertia)
358 XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER,
359 16, PropModeReplace, 1,
360 &pEvdevMultitouch->emulateWheel.inertia, TRUE);
364 else if (atom == prop_wheel_button)
368 if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
371 bt = *((CARD8*)val->data);
373 if (bt < 0 || bt >= EVDEVMULTITOUCH_MAXBUTTONS)
377 pEvdevMultitouch->emulateWheel.button = bt;
378 } else if (atom == prop_wheel_axismap)
380 if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER)
385 pEvdevMultitouch->emulateWheel.X.up_button = *((CARD8*)val->data);
386 pEvdevMultitouch->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1);
387 pEvdevMultitouch->emulateWheel.Y.up_button = *(((CARD8*)val->data) + 2);
388 pEvdevMultitouch->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 3);
390 } else if (atom == prop_wheel_inertia)
394 if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
397 inertia = *((CARD16*)val->data);
403 pEvdevMultitouch->emulateWheel.inertia = inertia;
404 } else if (atom == prop_wheel_timeout)
408 if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
411 timeout = *((CARD16*)val->data);
417 pEvdevMultitouch->emulateWheel.timeout = timeout;
423 EvdevMultitouchWheelEmuInitProperty(DeviceIntPtr dev)
425 InputInfoPtr pInfo = dev->public.devicePrivate;
426 EvdevMultitouchPtr pEvdevMultitouch = pInfo->private;
430 if (!dev->button) /* don't init prop for keyboards */
433 prop_wheel_emu = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL, strlen(EVDEVMULTITOUCH_PROP_WHEEL), TRUE);
434 rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
436 &pEvdevMultitouch->emulateWheel.enabled, FALSE);
440 XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE);
442 vals[0] = pEvdevMultitouch->emulateWheel.X.up_button;
443 vals[1] = pEvdevMultitouch->emulateWheel.X.down_button;
444 vals[2] = pEvdevMultitouch->emulateWheel.Y.up_button;
445 vals[3] = pEvdevMultitouch->emulateWheel.Y.down_button;
447 prop_wheel_axismap = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL_AXES, strlen(EVDEVMULTITOUCH_PROP_WHEEL_AXES), TRUE);
448 rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8,
449 PropModeReplace, 4, vals, FALSE);
454 XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE);
456 prop_wheel_inertia = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL_INERTIA, strlen(EVDEVMULTITOUCH_PROP_WHEEL_INERTIA), TRUE);
457 rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
459 &pEvdevMultitouch->emulateWheel.inertia, FALSE);
463 XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE);
465 prop_wheel_timeout = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL_TIMEOUT, strlen(EVDEVMULTITOUCH_PROP_WHEEL_TIMEOUT), TRUE);
466 rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 16,
468 &pEvdevMultitouch->emulateWheel.timeout, FALSE);
472 XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE);
474 prop_wheel_button = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL_BUTTON, strlen(EVDEVMULTITOUCH_PROP_WHEEL_BUTTON), TRUE);
475 rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8,
477 &pEvdevMultitouch->emulateWheel.button, FALSE);
481 XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE);
483 XIRegisterPropertyHandler(dev, EvdevMultitouchWheelEmuSetProperty, NULL, NULL);