blob: f658081062095ee03bcf8a8f2079e0b7ce8aa3bb [file] [log] [blame]
Jonas Ådahl1df17af2012-05-17 12:18:17 +02001/*
2 * Copyright © 2012 Jonas Ådahl
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <math.h>
26#include <string.h>
27#include <linux/input.h>
28
29#include "filter.h"
30#include "evdev-private.h"
31
32/* Default values */
33#define DEFAULT_CONSTANT_ACCEL_NUMERATOR 50
34#define DEFAULT_MIN_ACCEL_FACTOR 0.16
35#define DEFAULT_MAX_ACCEL_FACTOR 1.0
36#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
37
38enum touchpad_model {
39 TOUCHPAD_MODEL_UNKNOWN = 0,
40 TOUCHPAD_MODEL_SYNAPTICS,
41 TOUCHPAD_MODEL_ALPS,
42 TOUCHPAD_MODEL_APPLETOUCH,
43 TOUCHPAD_MODEL_ELANTECH
44};
45
46#define TOUCHPAD_EVENT_NONE 0
47#define TOUCHPAD_EVENT_ABSOLUTE_ANY (1 << 0)
48#define TOUCHPAD_EVENT_ABSOLUTE_X (1 << 1)
49#define TOUCHPAD_EVENT_ABSOLUTE_Y (1 << 2)
50#define TOUCHPAD_EVENT_REPORT (1 << 3)
51
52struct touchpad_model_spec {
53 short vendor;
54 short product;
55 enum touchpad_model model;
56};
57
58static struct touchpad_model_spec touchpad_spec_table[] = {
59 {0x0002, 0x0007, TOUCHPAD_MODEL_SYNAPTICS},
60 {0x0002, 0x0008, TOUCHPAD_MODEL_ALPS},
61 {0x05ac, 0x0000, TOUCHPAD_MODEL_APPLETOUCH},
62 {0x0002, 0x000e, TOUCHPAD_MODEL_ELANTECH},
63 {0x0000, 0x0000, TOUCHPAD_MODEL_UNKNOWN}
64};
65
66enum touchpad_state {
67 TOUCHPAD_STATE_NONE = 0,
68 TOUCHPAD_STATE_TOUCH,
69 TOUCHPAD_STATE_PRESS
70};
71
72#define TOUCHPAD_HISTORY_LENGTH 4
73
74struct touchpad_motion {
75 int32_t x;
76 int32_t y;
77};
78
79enum touchpad_fingers_state {
80 TOUCHPAD_FINGERS_ONE = (1 << 0),
81 TOUCHPAD_FINGERS_TWO = (1 << 1),
82 TOUCHPAD_FINGERS_THREE = (1 << 2)
83};
84
85struct touchpad_dispatch {
86 struct evdev_dispatch base;
87 struct evdev_input_device *device;
88
89 enum touchpad_model model;
90 enum touchpad_state state;
91 int finger_state;
92 int last_finger_state;
93
94 double constant_accel_factor;
95 double min_accel_factor;
96 double max_accel_factor;
97
98 unsigned int event_mask;
99 unsigned int event_mask_filter;
100
101 int reset;
102
103 struct {
104 int32_t x;
105 int32_t y;
106 } hw_abs;
107
108 int has_pressure;
109 struct {
110 int32_t touch_low;
111 int32_t touch_high;
112 int32_t press;
113 } pressure;
114
115 struct {
116 int32_t margin_x;
117 int32_t margin_y;
118 int32_t center_x;
119 int32_t center_y;
120 } hysteresis;
121
122 struct touchpad_motion motion_history[TOUCHPAD_HISTORY_LENGTH];
123 int motion_index;
124 unsigned int motion_count;
125
126 struct wl_list motion_filters;
127};
128
129static enum touchpad_model
130get_touchpad_model(struct evdev_input_device *device)
131{
132 struct input_id id;
133 unsigned int i;
134
135 if (ioctl(device->fd, EVIOCGID, &id) < 0)
136 return TOUCHPAD_MODEL_UNKNOWN;
137
138 for (i = 0; i < sizeof touchpad_spec_table; i++)
139 if (touchpad_spec_table[i].vendor == id.vendor &&
140 (!touchpad_spec_table[i].product ||
141 touchpad_spec_table[i].product == id.product))
142 return touchpad_spec_table[i].model;
143
144 return TOUCHPAD_MODEL_UNKNOWN;
145}
146
147static void
148configure_touchpad_pressure(struct touchpad_dispatch *touchpad,
149 int32_t pressure_min, int32_t pressure_max)
150{
151 int32_t range = pressure_max - pressure_min + 1;
152
153 touchpad->has_pressure = 1;
154
155 /* Magic numbers from xf86-input-synaptics */
156 switch (touchpad->model) {
157 case TOUCHPAD_MODEL_ELANTECH:
158 touchpad->pressure.touch_low = pressure_min + 1;
159 touchpad->pressure.touch_high = pressure_min + 1;
160 break;
161 default:
162 touchpad->pressure.touch_low =
163 pressure_min + range * (25.0/256.0);
164 touchpad->pressure.touch_high =
165 pressure_min + range * (30.0/256.0);
166 }
167
168 touchpad->pressure.press = pressure_min + range;
169}
170
171static double
172touchpad_profile(struct weston_motion_filter *filter,
173 void *data,
174 double velocity,
175 uint32_t time)
176{
177 struct touchpad_dispatch *touchpad =
178 (struct touchpad_dispatch *) data;
179
180 double accel_factor;
181
182 accel_factor = velocity * touchpad->constant_accel_factor;
183
184 if (accel_factor > touchpad->max_accel_factor)
185 accel_factor = touchpad->max_accel_factor;
186 else if (accel_factor < touchpad->min_accel_factor)
187 accel_factor = touchpad->min_accel_factor;
188
189 return accel_factor;
190}
191
192static void
193configure_touchpad(struct touchpad_dispatch *touchpad,
194 struct evdev_input_device *device)
195{
196 struct weston_motion_filter *accel;
197
198 struct input_absinfo absinfo;
199 unsigned long abs_bits[NBITS(ABS_MAX)];
200
201 double width;
202 double height;
203 double diagonal;
204
205 /* Detect model */
206 touchpad->model = get_touchpad_model(device);
207
208 /* Configure pressure */
209 ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);
210 if (TEST_BIT(abs_bits, ABS_PRESSURE)) {
211 ioctl(device->fd, EVIOCGABS(ABS_PRESSURE), &absinfo);
212 configure_touchpad_pressure(touchpad,
213 absinfo.minimum,
214 absinfo.maximum);
215 }
216
217 /* Configure acceleration factor */
218 width = abs(device->abs.max_x - device->abs.min_x);
219 height = abs(device->abs.max_y - device->abs.min_y);
220 diagonal = sqrt(width*width + height*height);
221
222 touchpad->constant_accel_factor =
223 DEFAULT_CONSTANT_ACCEL_NUMERATOR / diagonal;
224
225 touchpad->min_accel_factor = DEFAULT_MIN_ACCEL_FACTOR;
226 touchpad->max_accel_factor = DEFAULT_MAX_ACCEL_FACTOR;
227
228 touchpad->hysteresis.margin_x =
229 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
230 touchpad->hysteresis.margin_y =
231 diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
232 touchpad->hysteresis.center_x = 0;
233 touchpad->hysteresis.center_y = 0;
234
235 /* Configure acceleration profile */
236 accel = create_pointer_accelator_filter(touchpad_profile);
237 wl_list_insert(&touchpad->motion_filters, &accel->link);
238
239 /* Setup initial state */
240 touchpad->reset = 1;
241
242 memset(touchpad->motion_history, 0, sizeof touchpad->motion_history);
243 touchpad->motion_index = 0;
244 touchpad->motion_count = 0;
245
246 touchpad->state = TOUCHPAD_STATE_NONE;
247 touchpad->last_finger_state = 0;
248 touchpad->finger_state = 0;
249}
250
251static inline struct touchpad_motion *
252motion_history_offset(struct touchpad_dispatch *touchpad, int offset)
253{
254 int offset_index =
255 (touchpad->motion_index - offset + TOUCHPAD_HISTORY_LENGTH) %
256 TOUCHPAD_HISTORY_LENGTH;
257
258 return &touchpad->motion_history[offset_index];
259}
260
261static double
262estimate_delta(int x0, int x1, int x2, int x3)
263{
264 return (x0 + x1 - x2 - x3) / 4;
265}
266
267static int
268hysteresis(int in, int center, int margin)
269{
270 int diff = in - center;
271 if (abs(diff) <= margin)
272 return center;
273
274 if (diff > margin)
275 return center + diff - margin;
276 else if (diff < -margin)
277 return center + diff + margin;
278 return center + diff;
279}
280
281static void
282touchpad_get_delta(struct touchpad_dispatch *touchpad, double *dx, double *dy)
283{
284 *dx = estimate_delta(motion_history_offset(touchpad, 0)->x,
285 motion_history_offset(touchpad, 1)->x,
286 motion_history_offset(touchpad, 2)->x,
287 motion_history_offset(touchpad, 3)->x);
288 *dy = estimate_delta(motion_history_offset(touchpad, 0)->y,
289 motion_history_offset(touchpad, 1)->y,
290 motion_history_offset(touchpad, 2)->y,
291 motion_history_offset(touchpad, 3)->y);
292}
293
294static void
295filter_motion(struct touchpad_dispatch *touchpad,
296 double *dx, double *dy, uint32_t time)
297{
298 struct weston_motion_filter *filter;
299 struct weston_motion_params motion;
300
301 motion.dx = *dx;
302 motion.dy = *dy;
303
304 wl_list_for_each(filter, &touchpad->motion_filters, link)
305 weston_filter_dispatch(filter, &motion, touchpad, time);
306
307 *dx = motion.dx;
308 *dy = motion.dy;
309}
310
311static void
312touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
313{
314 int motion_index;
315 int center_x, center_y;
316 double dx, dy;
317
318 if (touchpad->reset ||
319 touchpad->last_finger_state != touchpad->finger_state) {
320 touchpad->reset = 0;
321 touchpad->motion_count = 0;
322 touchpad->event_mask = TOUCHPAD_EVENT_NONE;
323 touchpad->event_mask_filter =
324 TOUCHPAD_EVENT_ABSOLUTE_X | TOUCHPAD_EVENT_ABSOLUTE_Y;
325
326 touchpad->last_finger_state = touchpad->finger_state;
327
328 return;
329 }
330 touchpad->last_finger_state = touchpad->finger_state;
331
332 if (!(touchpad->event_mask & TOUCHPAD_EVENT_REPORT))
333 return;
334 else
335 touchpad->event_mask &= ~TOUCHPAD_EVENT_REPORT;
336
337 if ((touchpad->event_mask & touchpad->event_mask_filter) !=
338 touchpad->event_mask_filter)
339 return;
340
341 touchpad->event_mask_filter = TOUCHPAD_EVENT_ABSOLUTE_ANY;
342 touchpad->event_mask = 0;
343
344 /* Avoid noice by moving center only when delta reaches a threshold
345 * distance from the old center. */
346 if (touchpad->motion_count > 0) {
347 center_x = hysteresis(touchpad->hw_abs.x,
348 touchpad->hysteresis.center_x,
349 touchpad->hysteresis.margin_x);
350 center_y = hysteresis(touchpad->hw_abs.y,
351 touchpad->hysteresis.center_y,
352 touchpad->hysteresis.margin_y);
353 }
354 else {
355 center_x = touchpad->hw_abs.x;
356 center_y = touchpad->hw_abs.y;
357 }
358 touchpad->hysteresis.center_x = center_x;
359 touchpad->hysteresis.center_y = center_y;
360 touchpad->hw_abs.x = center_x;
361 touchpad->hw_abs.y = center_y;
362
363 /* Update motion history tracker */
364 motion_index = (touchpad->motion_index + 1) % TOUCHPAD_HISTORY_LENGTH;
365 touchpad->motion_index = motion_index;
366 touchpad->motion_history[motion_index].x = touchpad->hw_abs.x;
367 touchpad->motion_history[motion_index].y = touchpad->hw_abs.y;
368 if (touchpad->motion_count < 4)
369 touchpad->motion_count++;
370
371 if (touchpad->motion_count >= 4) {
372 touchpad_get_delta(touchpad, &dx, &dy);
373
374 filter_motion(touchpad, &dx, &dy, time);
375
376 touchpad->device->rel.dx = wl_fixed_from_double(dx);
377 touchpad->device->rel.dy = wl_fixed_from_double(dy);
Daniel Stone1d637772012-05-30 16:31:47 +0100378 touchpad->device->pending_events |= EVDEV_RELATIVE_MOTION;
Jonas Ådahl1df17af2012-05-17 12:18:17 +0200379 }
380}
381
382static inline void
383process_absolute(struct touchpad_dispatch *touchpad,
384 struct evdev_input_device *device,
385 struct input_event *e)
386{
387 switch (e->code) {
388 case ABS_PRESSURE:
389 if (e->value > touchpad->pressure.press)
390 touchpad->state = TOUCHPAD_STATE_PRESS;
391 else if (e->value > touchpad->pressure.touch_high)
392 touchpad->state = TOUCHPAD_STATE_TOUCH;
393 else if (e->value < touchpad->pressure.touch_low) {
394 if (touchpad->state > TOUCHPAD_STATE_NONE)
395 touchpad->reset = 1;
396
397 touchpad->state = TOUCHPAD_STATE_NONE;
398 }
399
400 break;
401 case ABS_X:
402 if (touchpad->state >= TOUCHPAD_STATE_TOUCH) {
403 touchpad->hw_abs.x = e->value;
404 touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
405 touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_X;
406 }
407 break;
408 case ABS_Y:
409 if (touchpad->state >= TOUCHPAD_STATE_TOUCH) {
410 touchpad->hw_abs.y = e->value;
411 touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
412 touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_Y;
413 }
414 break;
415 }
416}
417
418static inline void
419process_key(struct touchpad_dispatch *touchpad,
420 struct evdev_input_device *device,
421 struct input_event *e,
422 uint32_t time)
423{
424 switch (e->code) {
425 case BTN_TOUCH:
426 if (!touchpad->has_pressure) {
427 if (!e->value) {
428 touchpad->state = TOUCHPAD_STATE_NONE;
429 touchpad->reset = 1;
430 } else {
431 touchpad->state =
432 e->value ?
433 TOUCHPAD_STATE_TOUCH :
434 TOUCHPAD_STATE_NONE;
435 }
436 }
437 break;
438 case BTN_LEFT:
439 case BTN_RIGHT:
440 case BTN_MIDDLE:
441 case BTN_SIDE:
442 case BTN_EXTRA:
443 case BTN_FORWARD:
444 case BTN_BACK:
445 case BTN_TASK:
446 notify_button(&device->master->base.seat,
447 time, e->code, e->value);
448 break;
449 case BTN_TOOL_PEN:
450 case BTN_TOOL_RUBBER:
451 case BTN_TOOL_BRUSH:
452 case BTN_TOOL_PENCIL:
453 case BTN_TOOL_AIRBRUSH:
454 case BTN_TOOL_MOUSE:
455 case BTN_TOOL_LENS:
456 touchpad->reset = 1;
457 break;
458 case BTN_TOOL_FINGER:
459 touchpad->finger_state =
460 ~TOUCHPAD_FINGERS_ONE | e->value ?
461 TOUCHPAD_FINGERS_ONE : 0;
462 break;
463 case BTN_TOOL_DOUBLETAP:
464 touchpad->finger_state =
465 ~TOUCHPAD_FINGERS_TWO | e->value ?
466 TOUCHPAD_FINGERS_TWO : 0;
467 break;
468 case BTN_TOOL_TRIPLETAP:
469 touchpad->finger_state =
470 ~TOUCHPAD_FINGERS_THREE | e->value ?
471 TOUCHPAD_FINGERS_THREE : 0;
472 break;
473 }
474}
475
476static void
477touchpad_process(struct evdev_dispatch *dispatch,
478 struct evdev_input_device *device,
479 struct input_event *e,
480 uint32_t time)
481{
482 struct touchpad_dispatch *touchpad =
483 (struct touchpad_dispatch *) dispatch;
484
485 switch (e->type) {
486 case EV_SYN:
487 if (e->code == SYN_REPORT)
488 touchpad->event_mask |= TOUCHPAD_EVENT_REPORT;
489 break;
490 case EV_ABS:
491 process_absolute(touchpad, device, e);
492 break;
493 case EV_KEY:
494 process_key(touchpad, device, e, time);
495 break;
496 }
497
498 touchpad_update_state(touchpad, time);
499}
500
501static void
502touchpad_destroy(struct evdev_dispatch *dispatch)
503{
504 struct touchpad_dispatch *touchpad =
505 (struct touchpad_dispatch *) dispatch;
506 struct weston_motion_filter *filter;
507 struct weston_motion_filter *next;
508
509 wl_list_for_each_safe(filter, next, &touchpad->motion_filters, link)
510 filter->interface->destroy(filter);
511
512 free(dispatch);
513}
514
515struct evdev_dispatch_interface touchpad_interface = {
516 touchpad_process,
517 touchpad_destroy
518};
519
520struct evdev_dispatch *
521evdev_touchpad_create(struct evdev_input_device *device)
522{
523 struct touchpad_dispatch *touchpad;
524
525 touchpad = malloc(sizeof *touchpad);
526 if (touchpad == NULL)
527 return NULL;
528
529 touchpad->base.interface = &touchpad_interface;
530
531 touchpad->device = device;
532 wl_list_init(&touchpad->motion_filters);
533
534 configure_touchpad(touchpad, device);
535
536 return &touchpad->base;
537}