Tizen 2.0 Release
[profile/ivi/xserver-xorg-input-evdev-multitouch.git] / src / emuWheel.c
1 /*
2 * xserver-xorg-input-evdev-multitouch
3 *
4 * Contact: Sung-Jin Park <sj76.park@samsung.com>
5 *          Sangjin LEE <lsj119@samsung.com>
6 *
7 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
8 *
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.
17 *
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
27 * warranty.
28 *
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.
36 *
37 */
38
39 /* Mouse wheel emulation code. */
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <X11/Xatom.h>
45 #include <xf86.h>
46 #include <xf86Xinput.h>
47 #include <exevents.h>
48
49 #include <evdevmultitouch-properties.h>
50 #include "evdevmultitouch.h"
51
52 #define WHEEL_NOT_CONFIGURED 0
53
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;
60 #endif
61
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);
65
66 /* Filter mouse button events */
67 BOOL
68 EvdevMultitouchWheelEmuFilterButton(InputInfoPtr pInfo, unsigned int button, int value)
69 {
70     EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
71     int ms;
72
73     /* Has wheel emulation been configured to be enabled? */
74     if (!pEvdevMultitouch->emulateWheel.enabled)
75         return FALSE;
76
77     /* Check for EmulateWheelButton */
78     if (pEvdevMultitouch->emulateWheel.button == button) {
79         pEvdevMultitouch->emulateWheel.button_state = value;
80
81         if (value)
82             /* Start the timer when the button is pressed */
83             pEvdevMultitouch->emulateWheel.expires = pEvdevMultitouch->emulateWheel.timeout +
84                                            GetTimeInMillis();
85         else {
86             ms = pEvdevMultitouch->emulateWheel.expires - GetTimeInMillis();
87             if (ms > 0) {
88                 /*
89                  * If the button is released early enough emit the button
90                  * press/release events
91                  */
92                 EvdevMultitouchQueueButtonClicks(pInfo, button, 1);
93             }
94         }
95
96         return TRUE;
97     }
98
99     /* Don't care about this event */
100     return FALSE;
101 }
102
103 /* Filter mouse wheel events */
104 BOOL
105 EvdevMultitouchWheelEmuFilterMotion(InputInfoPtr pInfo, struct input_event *pEv)
106 {
107     EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
108     WheelAxisPtr pAxis = NULL, pOtherAxis = NULL;
109     int value = pEv->value;
110
111     /* Has wheel emulation been configured to be enabled? */
112     if (!pEvdevMultitouch->emulateWheel.enabled)
113         return FALSE;
114
115     /* Handle our motion events if the emuWheel button is pressed
116      * wheel button of 0 means always emulate wheel.
117      */
118     if (pEvdevMultitouch->emulateWheel.button_state || !pEvdevMultitouch->emulateWheel.button) {
119         /* Just return if the timeout hasn't expired yet */
120         if (pEvdevMultitouch->emulateWheel.button)
121         {
122             int ms = pEvdevMultitouch->emulateWheel.expires - GetTimeInMillis();
123             if (ms > 0)
124                 return TRUE;
125         }
126
127         /* We don't want to intercept real mouse wheel events */
128         switch(pEv->code) {
129         case REL_X:
130             pAxis = &(pEvdevMultitouch->emulateWheel.X);
131             pOtherAxis = &(pEvdevMultitouch->emulateWheel.Y);
132             break;
133
134         case REL_Y:
135             pAxis = &(pEvdevMultitouch->emulateWheel.Y);
136             pOtherAxis = &(pEvdevMultitouch->emulateWheel.X);
137             break;
138
139         default:
140             break;
141         }
142
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.
147          */
148         if (pAxis)
149         {
150             if (EvdevMultitouchWheelEmuInertia(pInfo, pAxis, value))
151                 pOtherAxis->traveled_distance = 0;
152         }
153
154         /* Eat motion events while emulateWheel button pressed. */
155         return TRUE;
156     }
157
158     return FALSE;
159 }
160
161 /* Simulate inertia for our emulated mouse wheel.
162    Returns the number of wheel events generated.
163  */
164 static int
165 EvdevMultitouchWheelEmuInertia(InputInfoPtr pInfo, WheelAxisPtr axis, int value)
166 {
167     EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
168     int button;
169     int inertia;
170     int rc = 0;
171
172     /* if this axis has not been configured, just eat the motion */
173     if (!axis->up_button)
174         return rc;
175
176     axis->traveled_distance += value;
177
178     if (axis->traveled_distance < 0) {
179         button = axis->up_button;
180         inertia = -pEvdevMultitouch->emulateWheel.inertia;
181     } else {
182         button = axis->down_button;
183         inertia = pEvdevMultitouch->emulateWheel.inertia;
184     }
185
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);
190         rc++;
191     }
192     return rc;
193 }
194
195 /* Handle button mapping here to avoid code duplication,
196 returns true if a button mapping was found. */
197 static BOOL
198 EvdevMultitouchWheelEmuHandleButtonMap(InputInfoPtr pInfo, WheelAxisPtr pAxis, char* axis_name)
199 {
200     EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
201     char *option_string;
202
203     pAxis->up_button = WHEEL_NOT_CONFIGURED;
204
205     /* Check to see if there is configuration for this axis */
206     option_string = xf86SetStrOption(pInfo->options, axis_name, NULL);
207     if (option_string) {
208         int up_button = 0;
209         int down_button = 0;
210         char *msg = NULL;
211
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))) {
215
216             /* Use xstrdup to allocate a string for us */
217             msg = xstrdup("buttons XX and YY");
218
219             if (msg)
220                 sprintf(msg, "buttons %d and %d", up_button, down_button);
221
222             pAxis->up_button = up_button;
223             pAxis->down_button = down_button;
224
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;
228
229         } else {
230             xf86Msg(X_WARNING, "%s: Invalid %s value:\"%s\"\n",
231                     pInfo->name, axis_name, option_string);
232
233         }
234
235         /* Clean up and log what happened */
236         if (msg) {
237             xf86Msg(X_CONFIG, "%s: %s: %s\n",pInfo->name, axis_name, msg);
238             free(msg);
239             return TRUE;
240         }
241     }
242     return FALSE;
243 }
244
245 /* Setup the basic configuration options used by mouse wheel emulation */
246 void
247 EvdevMultitouchWheelEmuPreInit(InputInfoPtr pInfo)
248 {
249     EvdevMultitouchPtr pEvdevMultitouch = (EvdevMultitouchPtr)pInfo->private;
250     char val[4];
251     int wheelButton;
252     int inertia;
253     int timeout;
254
255     val[0] = 0;
256     val[1] = 0;
257
258     if (xf86SetBoolOption(pInfo->options, "EmulateWheel", FALSE)) {
259         pEvdevMultitouch->emulateWheel.enabled = TRUE;
260     } else
261         pEvdevMultitouch->emulateWheel.enabled = FALSE;
262
263     wheelButton = xf86SetIntOption(pInfo->options, "EmulateWheelButton", 4);
264
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);
269
270         pEvdevMultitouch->emulateWheel.enabled = FALSE;
271     }
272
273     pEvdevMultitouch->emulateWheel.button = wheelButton;
274
275     inertia = xf86SetIntOption(pInfo->options, "EmulateWheelInertia", 10);
276
277     if (inertia <= 0) {
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",
281                 pInfo->name);
282
283         inertia = 10;
284     }
285
286     pEvdevMultitouch->emulateWheel.inertia = inertia;
287
288     timeout = xf86SetIntOption(pInfo->options, "EmulateWheelTimeout", 200);
289
290     if (timeout < 0) {
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",
294                 pInfo->name);
295
296         timeout = 200;
297     }
298
299     pEvdevMultitouch->emulateWheel.timeout = timeout;
300
301     /* Configure the Y axis or default it */
302     if (!EvdevMultitouchWheelEmuHandleButtonMap(pInfo, &(pEvdevMultitouch->emulateWheel.Y),
303                 "YAxisMapping")) {
304         /* Default the Y axis to sane values */
305         pEvdevMultitouch->emulateWheel.Y.up_button = 4;
306         pEvdevMultitouch->emulateWheel.Y.down_button = 5;
307
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;
312
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);
317     }
318
319
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),
323             "XAxisMapping");
324
325     /* Used by the inertia code */
326     pEvdevMultitouch->emulateWheel.X.traveled_distance = 0;
327     pEvdevMultitouch->emulateWheel.Y.traveled_distance = 0;
328
329     xf86Msg(X_CONFIG, "%s: EmulateWheelButton: %d, "
330             "EmulateWheelInertia: %d, "
331             "EmulateWheelTimeout: %d\n",
332             pInfo->name, pEvdevMultitouch->emulateWheel.button, inertia, timeout);
333 }
334
335 #ifdef HAVE_PROPERTIES
336 static int
337 EvdevMultitouchWheelEmuSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
338                          BOOL checkonly)
339 {
340     InputInfoPtr pInfo  = dev->public.devicePrivate;
341     EvdevMultitouchPtr     pEvdevMultitouch = pInfo->private;
342
343     if (atom == prop_wheel_emu)
344     {
345         if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
346             return BadMatch;
347
348         if (!checkonly)
349         {
350             pEvdevMultitouch->emulateWheel.enabled = *((BOOL*)val->data);
351             /* Don't enable with zero inertia, otherwise we may get stuck in an
352              * infinite loop */
353             if (pEvdevMultitouch->emulateWheel.inertia <= 0)
354             {
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);
361             }
362         }
363     }
364     else if (atom == prop_wheel_button)
365     {
366         int bt;
367
368         if (val->format != 8 || val->size != 1 || val->type != XA_INTEGER)
369             return BadMatch;
370
371         bt = *((CARD8*)val->data);
372
373         if (bt < 0 || bt >= EVDEVMULTITOUCH_MAXBUTTONS)
374             return BadValue;
375
376         if (!checkonly)
377             pEvdevMultitouch->emulateWheel.button = bt;
378     } else if (atom == prop_wheel_axismap)
379     {
380         if (val->format != 8 || val->size != 4 || val->type != XA_INTEGER)
381             return BadMatch;
382
383         if (!checkonly)
384         {
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);
389         }
390     } else if (atom == prop_wheel_inertia)
391     {
392         int inertia;
393
394         if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
395             return BadMatch;
396
397         inertia = *((CARD16*)val->data);
398
399         if (inertia < 0)
400             return BadValue;
401
402         if (!checkonly)
403             pEvdevMultitouch->emulateWheel.inertia = inertia;
404     } else if (atom == prop_wheel_timeout)
405     {
406         int timeout;
407
408         if (val->format != 16 || val->size != 1 || val->type != XA_INTEGER)
409             return BadMatch;
410
411         timeout = *((CARD16*)val->data);
412
413         if (timeout < 0)
414             return BadValue;
415
416         if (!checkonly)
417             pEvdevMultitouch->emulateWheel.timeout = timeout;
418     }
419     return Success;
420 }
421
422 void
423 EvdevMultitouchWheelEmuInitProperty(DeviceIntPtr dev)
424 {
425     InputInfoPtr pInfo  = dev->public.devicePrivate;
426     EvdevMultitouchPtr     pEvdevMultitouch = pInfo->private;
427     int          rc     = TRUE;
428     char         vals[4];
429
430     if (!dev->button) /* don't init prop for keyboards */
431         return;
432
433     prop_wheel_emu = MakeAtom(EVDEVMULTITOUCH_PROP_WHEEL, strlen(EVDEVMULTITOUCH_PROP_WHEEL), TRUE);
434     rc = XIChangeDeviceProperty(dev, prop_wheel_emu, XA_INTEGER, 8,
435                                 PropModeReplace, 1,
436                                 &pEvdevMultitouch->emulateWheel.enabled, FALSE);
437     if (rc != Success)
438         return;
439
440     XISetDevicePropertyDeletable(dev, prop_wheel_emu, FALSE);
441
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;
446
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);
450
451     if (rc != Success)
452         return;
453
454     XISetDevicePropertyDeletable(dev, prop_wheel_axismap, FALSE);
455
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,
458                                 PropModeReplace, 1,
459                                 &pEvdevMultitouch->emulateWheel.inertia, FALSE);
460     if (rc != Success)
461         return;
462
463     XISetDevicePropertyDeletable(dev, prop_wheel_inertia, FALSE);
464
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,
467                                 PropModeReplace, 1,
468                                 &pEvdevMultitouch->emulateWheel.timeout, FALSE);
469     if (rc != Success)
470         return;
471
472     XISetDevicePropertyDeletable(dev, prop_wheel_timeout, FALSE);
473
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,
476                                 PropModeReplace, 1,
477                                 &pEvdevMultitouch->emulateWheel.button, FALSE);
478     if (rc != Success)
479         return;
480
481     XISetDevicePropertyDeletable(dev, prop_wheel_button, FALSE);
482
483     XIRegisterPropertyHandler(dev, EvdevMultitouchWheelEmuSetProperty, NULL, NULL);
484 }
485 #endif