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