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