Clean up spec file for packaging.
[profile/ivi/xorg-drv-evdev.git] / src / emuWheel.c
1 /*
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.
10 *
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
20 * warranty.
21 *
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.
29 *
30 */
31
32 /* Mouse wheel emulation code. */
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include <X11/Xatom.h>
38 #include <xf86.h>
39 #include <xf86Xinput.h>
40 #include <exevents.h>
41
42 #include <evdev-properties.h>
43 #include "evdev.h"
44
45 #define WHEEL_NOT_CONFIGURED 0
46
47 #ifdef HAVE_PROPERTIES
48 static Atom prop_wheel_emu      = 0;
49 static Atom prop_wheel_axismap  = 0;
50 static Atom prop_wheel_inertia  = 0;
51 static Atom prop_wheel_timeout  = 0;
52 static Atom prop_wheel_button   = 0;
53 #endif
54
55 /* Local Funciton Prototypes */
56 static BOOL EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char *axis_name);
57 static int EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value);
58
59 /* Filter mouse button events */
60 BOOL
61 EvdevWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value)
62 {
63     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
64     int ms;
65
66     /* Has wheel emulation been configured to be enabled? */
67     if (!pEvdev->emulateWheel.enabled)
68         return FALSE;
69
70     /* Check for EmulateWheelButton */
71     if (pEvdev->emulateWheel.button == button) {
72         pEvdev->emulateWheel.button_state = value;
73
74         if (value)
75             /* Start the timer when the button is pressed */
76             pEvdev->emulateWheel.expires = pEvdev->emulateWheel.timeout +
77                                            GetTimeInMillis();
78         else {
79             ms = pEvdev->emulateWheel.expires - GetTimeInMillis();
80             if (ms > 0) {
81                 /*
82                  * If the button is released early enough emit the button
83                  * press/release events
84                  */
85                 EvdevQueueButtonClicks(pInfo, button, 1);
86             }
87         }
88
89         return TRUE;
90     }
91
92     /* Don't care about this event */
93     return FALSE;
94 }
95
96 /* Filter mouse wheel events */
97 BOOL
98 EvdevWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
99 {
100     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
101     WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
102     int value = pEv->value;
103
104     /* Has wheel emulation been configured to be enabled? */
105     if (!pEvdev->emulateWheel.enabled)
106         return FALSE;
107
108     /* Handle our motion events if the emuWheel button is pressed
109      * wheel button of 0 means always emulate wheel.
110      */
111     if (pEvdev->emulateWheel.button_state || !pEvdev->emulateWheel.button) {
112         /* Just return if the timeout hasn't expired yet */
113         if (pEvdev->emulateWheel.button)
114         {
115             int ms = pEvdev->emulateWheel.expires - GetTimeInMillis();
116             if (ms > 0)
117                 return TRUE;
118         }
119
120         /* We don't want to intercept real mouse wheel events */
121         switch(pEv->code) {
122         case REL_X:
123             pAxis = &(pEvdev->emulateWheel.X);
124             pOtherAxis = &(pEvdev->emulateWheel.Y);
125             break;
126
127         case REL_Y:
128             pAxis = &(pEvdev->emulateWheel.Y);
129             pOtherAxis = &(pEvdev->emulateWheel.X);
130             break;
131
132         default:
133             break;
134         }
135
136         /* If we found REL_X or REL_Y, emulate a mouse wheel.
137            Reset the inertia of the other axis when a scroll event was sent
138            to avoid the buildup of erroneous scroll events if the user
139            doesn't move in a perfectly straight line.
140          */
141         if (pAxis)
142         {
143             if (EvdevWheelEmuInertia(pInfo, pAxis, value))
144                 pOtherAxis->traveled_distance = 0;
145         }
146
147         /* Eat motion events while emulateWheel button pressed. */
148         return TRUE;
149     }
150
151     return FALSE;
152 }
153
154 /* Simulate inertia for our emulated mouse wheel.
155    Returns the number of wheel events generated.
156  */
157 static int
158 EvdevWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
159 {
160     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
161     int button;
162     int inertia;
163     int rc = 0;
164
165     /* if this axis has not been configured, just eat the motion */
166     if (!axis->up_button)
167         return rc;
168
169     axis->traveled_distance += value;
170
171     if (axis->traveled_distance < 0) {
172         button = axis->up_button;
173         inertia = -pEvdev->emulateWheel.inertia;
174     } else {
175         button = axis->down_button;
176         inertia = pEvdev->emulateWheel.inertia;
177     }
178
179     /* Produce button press events for wheel motion */
180     while(abs(axis->traveled_distance) > pEvdev->emulateWheel.inertia) {
181         axis->traveled_distance -= inertia;
182         EvdevQueueButtonClicks(pInfo, button, 1);
183         rc++;
184     }
185     return rc;
186 }
187
188 /* Handle button mapping here to avoid code duplication,
189 returns true if a button mapping was found. */
190 static BOOL
191 EvdevWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name)
192 {
193     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
194     char *option_string;
195
196     pAxis->up_button = WHEEL_NOT_CONFIGURED;
197
198     /* Check to see if there is configuration for this axis */
199     option_string = xf86SetStrOption(pInfo->options, axis_name, NULL);
200     if (option_string) {
201         int up_button = 0;
202         int down_button = 0;
203         char *msg = NULL;
204
205         if ((sscanf(option_string, "%d %d", &up_button, &down_button) == 2) &&
206             ((up_button > 0) && (up_button <= EVDEV_MAXBUTTONS)) &&
207             ((down_button > 0) && (down_button <= EVDEV_MAXBUTTONS))) {
208
209             /* Use xstrdup to allocate a string for us */
210             msg = xstrdup("buttons XX and YY");
211
212             if (msg)
213                 sprintf(msg, "buttons %d and %d", up_button, down_button);
214
215             pAxis->up_button = up_button;
216             pAxis->down_button = down_button;
217
218             /* Update the number of buttons if needed */
219             if (up_button > pEvdev->num_buttons) pEvdev->num_buttons = up_button;
220             if (down_button > pEvdev->num_buttons) pEvdev->num_buttons = down_button;
221
222         } else {
223             xf86Msg(X_WARNING, "%s: Invalid %s value:\"%s\"\n",
224                     pInfo->name, axis_name, option_string);
225
226         }
227
228         /* Clean up and log what happened */
229         if (msg) {
230             xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg);
231             free(msg);
232             return TRUE;
233         }
234     }
235     return FALSE;
236 }
237
238 /* Setup the basic configuration options used by mouse wheel emulation */
239 void
240 EvdevWheelEmuPreInit(InputInfoPtr pInfo)
241 {
242     EvdevPtr pEvdev = (EvdevPtr)pInfo->private;
243     char val[4];
244     int wheelButton;
245     int inertia;
246     int timeout;
247
248     val[0] = 0;
249     val[1] = 0;
250
251     if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) {
252         pEvdev->emulateWheel.enabled = TRUE;
253     } else
254         pEvdev->emulateWheel.enabled = FALSE;
255
256     wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4);
257
258     if ((wheelButton < 0) || (wheelButton > EVDEV_MAXBUTTONS)) {
259         xf86Msg(X_WARNING, "%s: Invalid EmulateWheelButton value: %d\n",
260                 pInfo->name, wheelButton);
261         xf86Msg(X_WARNING, "%s: Wheel emulation disabled.\n", pInfo->name);
262
263         pEvdev->emulateWheel.enabled = FALSE;
264     }
265
266     pEvdev->emulateWheel.button = wheelButton;
267
268     inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
269
270     if (inertia <= 0) {
271         xf86Msg(X_WARNING, "%s: Invalid EmulateWheelInertia value: %d\n",
272                 pInfo->name, inertia);
273         xf86Msg(X_WARNING, "%s: Using built-in inertia value.\n",
274                 pInfo->name);
275
276         inertia = 10;
277     }
278
279     pEvdev->emulateWheel.inertia = inertia;
280
281     timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200);
282
283     if (timeout < 0) {
284         xf86Msg(X_WARNING, "%s: Invalid EmulateWheelTimeout value: %d\n",
285                 pInfo->name, timeout);
286         xf86Msg(X_WARNING, "%s: Using built-in timeout value.\n",
287                 pInfo->name);
288
289         timeout = 200;
290     }
291
292     pEvdev->emulateWheel.timeout = timeout;
293
294     /* Configure the Y axis or default it */
295     if (!EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.Y),
296                 "YAxisMapping")) {
297         /* Default the Y axis to sane values */
298         pEvdev->emulateWheel.Y.up_button = 4;
299         pEvdev->emulateWheel.Y.down_button = 5;
300
301         /* Simpler to check just the largest value in this case */
302         /* XXX: we should post this to the server */
303         if (5 > pEvdev->num_buttons)
304             pEvdev->num_buttons = 5;
305
306         /* Display default Configuration */
307         xf86Msg(X_CONFIG, "%s: YAxisMapping: buttons %d and %d\n",
308                 pInfo->name, pEvdev->emulateWheel.Y.up_button,
309                 pEvdev->emulateWheel.Y.down_button);
310     }
311
312
313     /* This axis should default to an unconfigured state as most people
314        are not going to expect a Horizontal wheel. */
315     EvdevWheelEmuHandleButtonMap(pInfo, &(pEvdev->emulateWheel.X),
316             "XAxisMapping");
317
318     /* Used by the inertia code */
319     pEvdev->emulateWheel.X.traveled_distance = 0;
320     pEvdev->emulateWheel.Y.traveled_distance = 0;
321
322     xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, "
323             "EmulateWheelInertia: %d, "
324             "EmulateWheelTimeout: %d\n",
325             pInfo->name, pEvdev->emulateWheel.button, inertia, timeout);
326 }
327
328 #ifdef HAVE_PROPERTIES
329 static int
330 EvdevWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
331                          BOOL checkonly)
332 {
333     InputInfoPtr pInfo  = dev->public.devicePrivate;
334     EvdevPtr     pEvdev = pInfo->private;
335
336     if (atom == prop_wheel_emu)
337     {
338         if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
339             return BadMatch;
340
341         if (!checkonly)
342         {
343             pEvdev->emulateWheel.enabled = *((BOOL*)val->data);
344             /* Don't enable with zero inertia, otherwise we may get stuck in an
345              * infinite loop */
346             if (pEvdev->emulateWheel.inertia <= 0)
347             {
348                 pEvdev->emulateWheel.inertia = 10;
349                 /* We may get here before the property is actually enabled */
350                 if (prop_wheel_inertia)
351                     XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER,
352                             16, PropModeReplace, 1,
353                             &pEvdev->emulateWheel.inertia, TRUE);
354             }
355         }
356     }
357     else if (atom == prop_wheel_button)
358     {
359         int bt;
360
361         if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
362             return BadMatch;
363
364         bt = *((CARD8*)val->data);
365
366         if (bt < 0 || bt >= EVDEV_MAXBUTTONS)
367             return BadValue;
368
369         if (!checkonly)
370             pEvdev->emulateWheel.button = bt;
371     } else if (atom == prop_wheel_axismap)
372     {
373         if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER)
374             return BadMatch;
375
376         if (!checkonly)
377         {
378             pEvdev->emulateWheel.X.up_button = *((CARD8*)val->data);
379             pEvdev->emulateWheel.X.down_button = *(((CARD8*)val->data) + 1);
380             pEvdev->emulateWheel.Y.up_button = *(((CARD8*)val->data) + 2);
381             pEvdev->emulateWheel.Y.down_button = *(((CARD8*)val->data) + 3);
382         }
383     } else if (atom == prop_wheel_inertia)
384     {
385         int inertia;
386
387         if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
388             return BadMatch;
389
390         inertia = *((CARD16*)val->data);
391
392         if (inertia < 0)
393             return BadValue;
394
395         if (!checkonly)
396             pEvdev->emulateWheel.inertia = inertia;
397     } else if (atom == prop_wheel_timeout)
398     {
399         int timeout;
400
401         if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
402             return BadMatch;
403
404         timeout = *((CARD16*)val->data);
405
406         if (timeout < 0)
407             return BadValue;
408
409         if (!checkonly)
410             pEvdev->emulateWheel.timeout = timeout;
411     }
412     return Success;
413 }
414
415 void
416 EvdevWheelEmuInitProperty(DeviceIntPtr dev)
417 {
418     InputInfoPtr pInfo  = dev->public.devicePrivate;
419     EvdevPtr     pEvdev = pInfo->private;
420     int          rc     = TRUE;
421     char         vals[4];
422
423     if (!dev->button) /* don't init prop for keyboards */
424         return;
425
426     prop_wheel_emu = MakeAtom(EVDEV_PROP_WHEEL, strlen(EVDEV_PROP_WHEEL), TRUE);
427     rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
428                                 PropModeReplace, 1,
429                                 &pEvdev->emulateWheel.enabled, FALSE);
430     if (rc != Success)
431         return;
432
433     XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE);
434
435     vals[0] = pEvdev->emulateWheel.X.up_button;
436     vals[1] = pEvdev->emulateWheel.X.down_button;
437     vals[2] = pEvdev->emulateWheel.Y.up_button;
438     vals[3] = pEvdev->emulateWheel.Y.down_button;
439
440     prop_wheel_axismap = MakeAtom(EVDEV_PROP_WHEEL_AXES, strlen(EVDEV_PROP_WHEEL_AXES), TRUE);
441     rc = XIChangeDeviceProperty(dev, prop_wheel_axismap, XA_INTEGER, 8,
442                                 PropModeReplace, 4, vals, FALSE);
443
444     if (rc != Success)
445         return;
446
447     XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE);
448
449     prop_wheel_inertia = MakeAtom(EVDEV_PROP_WHEEL_INERTIA, strlen(EVDEV_PROP_WHEEL_INERTIA), TRUE);
450     rc = XIChangeDeviceProperty(dev, prop_wheel_inertia, XA_INTEGER, 16,
451                                 PropModeReplace, 1,
452                                 &pEvdev->emulateWheel.inertia, FALSE);
453     if (rc != Success)
454         return;
455
456     XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE);
457
458     prop_wheel_timeout = MakeAtom(EVDEV_PROP_WHEEL_TIMEOUT, strlen(EVDEV_PROP_WHEEL_TIMEOUT), TRUE);
459     rc = XIChangeDeviceProperty(dev, prop_wheel_timeout, XA_INTEGER, 16,
460                                 PropModeReplace, 1,
461                                 &pEvdev->emulateWheel.timeout, FALSE);
462     if (rc != Success)
463         return;
464
465     XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE);
466
467     prop_wheel_button = MakeAtom(EVDEV_PROP_WHEEL_BUTTON, strlen(EVDEV_PROP_WHEEL_BUTTON), TRUE);
468     rc = XIChangeDeviceProperty(dev, prop_wheel_button, XA_INTEGER, 8,
469                                 PropModeReplace, 1,
470                                 &pEvdev->emulateWheel.button, FALSE);
471     if (rc != Success)
472         return;
473
474     XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE);
475
476     XIRegisterPropertyHandler(dev, EvdevWheelEmuSetProperty, NULL, NULL);
477 }
478 #endif