2014-02-06 15:05:36 +10:00
|
|
|
/*
|
|
|
|
|
* Copyright © 2014 Red Hat, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Permission to use, copy, modify, distribute, and sell this software and
|
|
|
|
|
* its documentation for any purpose is hereby granted without fee, provided
|
|
|
|
|
* that the above copyright notice appear in all copies and that both that
|
|
|
|
|
* copyright notice and this permission notice appear in supporting
|
|
|
|
|
* documentation, and that the name of the copyright holders not be used in
|
|
|
|
|
* advertising or publicity pertaining to distribution of the software
|
|
|
|
|
* without specific, written prior permission. The copyright holders make
|
|
|
|
|
* no representations about the suitability of this software for any
|
|
|
|
|
* purpose. It is provided "as is" without express or implied warranty.
|
|
|
|
|
*
|
|
|
|
|
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
|
|
|
|
|
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
|
|
|
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
|
|
|
|
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
|
|
|
|
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
|
|
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
#include <assert.h>
|
2014-02-06 18:57:10 +10:00
|
|
|
#include <math.h>
|
2014-02-06 15:32:32 +10:00
|
|
|
#include <stdbool.h>
|
2014-07-15 14:01:00 +10:00
|
|
|
#include <limits.h>
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-02-07 13:39:27 +10:00
|
|
|
#include "evdev-mt-touchpad.h"
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2014-07-01 14:53:18 +02:00
|
|
|
#define DEFAULT_ACCEL_NUMERATOR 1200.0
|
2014-02-06 18:57:10 +10:00
|
|
|
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
|
2014-09-28 13:21:08 +02:00
|
|
|
#define DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT 500 /* ms */
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2014-02-06 18:57:10 +10:00
|
|
|
static inline int
|
|
|
|
|
tp_hysteresis(int in, int center, int margin)
|
|
|
|
|
{
|
|
|
|
|
int diff = in - center;
|
|
|
|
|
if (abs(diff) <= margin)
|
|
|
|
|
return center;
|
|
|
|
|
|
|
|
|
|
if (diff > margin)
|
|
|
|
|
return center + diff - margin;
|
2014-06-30 14:27:18 +02:00
|
|
|
else
|
2014-02-06 18:57:10 +10:00
|
|
|
return center + diff + margin;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline struct tp_motion *
|
|
|
|
|
tp_motion_history_offset(struct tp_touch *t, int offset)
|
|
|
|
|
{
|
|
|
|
|
int offset_index =
|
|
|
|
|
(t->history.index - offset + TOUCHPAD_HISTORY_LENGTH) %
|
|
|
|
|
TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
|
|
|
|
|
|
return &t->history.samples[offset_index];
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
void
|
2014-02-06 19:43:48 +10:00
|
|
|
tp_filter_motion(struct tp_dispatch *tp,
|
2014-12-04 11:44:09 +08:00
|
|
|
double *dx, double *dy,
|
|
|
|
|
double *dx_unaccel, double *dy_unaccel,
|
|
|
|
|
uint64_t time)
|
2014-02-06 19:43:48 +10:00
|
|
|
{
|
|
|
|
|
struct motion_params motion;
|
|
|
|
|
|
2014-05-24 16:53:45 +02:00
|
|
|
motion.dx = *dx * tp->accel.x_scale_coeff;
|
|
|
|
|
motion.dy = *dy * tp->accel.y_scale_coeff;
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2014-12-04 11:44:09 +08:00
|
|
|
if (dx_unaccel)
|
|
|
|
|
*dx_unaccel = motion.dx;
|
|
|
|
|
if (dy_unaccel)
|
|
|
|
|
*dy_unaccel = motion.dy;
|
|
|
|
|
|
2014-07-08 13:02:31 +10:00
|
|
|
if (motion.dx != 0.0 || motion.dy != 0.0)
|
2014-09-22 09:35:53 +10:00
|
|
|
filter_dispatch(tp->device->pointer.filter, &motion, tp, time);
|
2014-02-06 19:43:48 +10:00
|
|
|
|
|
|
|
|
*dx = motion.dx;
|
|
|
|
|
*dy = motion.dy;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_history_push(struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
int motion_index = (t->history.index + 1) % TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
|
|
|
|
|
|
if (t->history.count < TOUCHPAD_HISTORY_LENGTH)
|
|
|
|
|
t->history.count++;
|
|
|
|
|
|
|
|
|
|
t->history.samples[motion_index].x = t->x;
|
|
|
|
|
t->history.samples[motion_index].y = t->y;
|
|
|
|
|
t->history.index = motion_index;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 18:57:10 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_hysteresis(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
int x = t->x,
|
|
|
|
|
y = t->y;
|
|
|
|
|
|
|
|
|
|
if (t->history.count == 0) {
|
|
|
|
|
t->hysteresis.center_x = t->x;
|
|
|
|
|
t->hysteresis.center_y = t->y;
|
|
|
|
|
} else {
|
|
|
|
|
x = tp_hysteresis(x,
|
|
|
|
|
t->hysteresis.center_x,
|
|
|
|
|
tp->hysteresis.margin_x);
|
|
|
|
|
y = tp_hysteresis(y,
|
|
|
|
|
t->hysteresis.center_y,
|
|
|
|
|
tp->hysteresis.margin_y);
|
|
|
|
|
t->hysteresis.center_x = x;
|
|
|
|
|
t->hysteresis.center_y = y;
|
|
|
|
|
t->x = x;
|
|
|
|
|
t->y = y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_motion_history_reset(struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
t->history.count = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline struct tp_touch *
|
|
|
|
|
tp_current_touch(struct tp_dispatch *tp)
|
|
|
|
|
{
|
2014-06-18 14:22:24 +02:00
|
|
|
return &tp->touches[min(tp->slot, tp->ntouches - 1)];
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-14 15:12:22 +10:00
|
|
|
static inline struct tp_touch *
|
|
|
|
|
tp_get_touch(struct tp_dispatch *tp, unsigned int slot)
|
|
|
|
|
{
|
|
|
|
|
assert(slot < tp->ntouches);
|
|
|
|
|
return &tp->touches[slot];
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-12 10:31:12 +10:00
|
|
|
static inline unsigned int
|
|
|
|
|
tp_fake_finger_count(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
unsigned int fake_touches, nfake_touches;
|
|
|
|
|
|
|
|
|
|
/* don't count BTN_TOUCH */
|
|
|
|
|
fake_touches = tp->fake_touches >> 1;
|
|
|
|
|
nfake_touches = 0;
|
|
|
|
|
while (fake_touches) {
|
|
|
|
|
nfake_touches++;
|
|
|
|
|
fake_touches >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nfake_touches;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline bool
|
|
|
|
|
tp_fake_finger_is_touching(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
return tp->fake_touches & 0x1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
tp_fake_finger_set(struct tp_dispatch *tp,
|
|
|
|
|
unsigned int code,
|
|
|
|
|
bool is_press)
|
|
|
|
|
{
|
|
|
|
|
unsigned int shift;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
|
case BTN_TOUCH:
|
|
|
|
|
shift = 0;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_TOOL_FINGER:
|
|
|
|
|
shift = 1;
|
|
|
|
|
break;
|
|
|
|
|
case BTN_TOOL_DOUBLETAP:
|
|
|
|
|
case BTN_TOOL_TRIPLETAP:
|
|
|
|
|
case BTN_TOOL_QUADTAP:
|
|
|
|
|
shift = code - BTN_TOOL_DOUBLETAP + 2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_press)
|
|
|
|
|
tp->fake_touches |= 1 << shift;
|
|
|
|
|
else
|
|
|
|
|
tp->fake_touches &= ~(0x1 << shift);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static inline void
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2014-07-18 11:06:36 +02:00
|
|
|
if (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
t->state = TOUCH_BEGIN;
|
|
|
|
|
t->pinned.is_pinned = false;
|
|
|
|
|
t->millis = time;
|
|
|
|
|
tp->nfingers_down++;
|
|
|
|
|
assert(tp->nfingers_down >= 1);
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
2014-07-18 11:06:36 +02:00
|
|
|
if (t->state == TOUCH_END || t->state == TOUCH_NONE)
|
2014-02-06 15:32:32 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
t->dirty = true;
|
2014-02-14 13:59:41 +10:00
|
|
|
t->is_pointer = false;
|
2014-07-14 16:06:51 +10:00
|
|
|
t->palm.is_palm = false;
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_END;
|
2014-04-15 14:27:59 +02:00
|
|
|
t->pinned.is_pinned = false;
|
2014-07-18 11:06:36 +02:00
|
|
|
t->millis = time;
|
2014-02-06 15:32:32 +10:00
|
|
|
assert(tp->nfingers_down >= 1);
|
|
|
|
|
tp->nfingers_down--;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static double
|
|
|
|
|
tp_estimate_delta(int x0, int x1, int x2, int x3)
|
|
|
|
|
{
|
2014-07-18 11:06:39 +02:00
|
|
|
return (x0 + x1 - x2 - x3) / 4.0;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-07 14:39:21 +10:00
|
|
|
void
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_get_delta(struct tp_touch *t, double *dx, double *dy)
|
2014-02-06 15:05:36 +10:00
|
|
|
{
|
2014-12-09 12:47:11 +01:00
|
|
|
if (t->history.count < TOUCHPAD_MIN_SAMPLES) {
|
2014-02-06 15:32:32 +10:00
|
|
|
*dx = 0;
|
|
|
|
|
*dy = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*dx = tp_estimate_delta(tp_motion_history_offset(t, 0)->x,
|
|
|
|
|
tp_motion_history_offset(t, 1)->x,
|
|
|
|
|
tp_motion_history_offset(t, 2)->x,
|
|
|
|
|
tp_motion_history_offset(t, 3)->x);
|
|
|
|
|
*dy = tp_estimate_delta(tp_motion_history_offset(t, 0)->y,
|
|
|
|
|
tp_motion_history_offset(t, 1)->y,
|
|
|
|
|
tp_motion_history_offset(t, 2)->y,
|
|
|
|
|
tp_motion_history_offset(t, 3)->y);
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_process_absolute(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 15:05:36 +10:00
|
|
|
{
|
2014-02-06 15:32:32 +10:00
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
|
|
|
|
|
switch(e->code) {
|
|
|
|
|
case ABS_MT_POSITION_X:
|
|
|
|
|
t->x = e->value;
|
|
|
|
|
t->millis = time;
|
|
|
|
|
t->dirty = true;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_MT_POSITION_Y:
|
|
|
|
|
t->y = e->value;
|
|
|
|
|
t->millis = time;
|
|
|
|
|
t->dirty = true;
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_MT_SLOT:
|
|
|
|
|
tp->slot = e->value;
|
|
|
|
|
break;
|
|
|
|
|
case ABS_MT_TRACKING_ID:
|
|
|
|
|
if (e->value != -1)
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_begin_touch(tp, t, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
else
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_end_touch(tp, t, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-14 14:18:27 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_absolute_st(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-14 14:18:27 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
|
|
|
|
|
switch(e->code) {
|
|
|
|
|
case ABS_X:
|
|
|
|
|
t->x = e->value;
|
|
|
|
|
t->millis = time;
|
|
|
|
|
t->dirty = true;
|
2014-03-25 11:30:42 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-14 14:18:27 +10:00
|
|
|
break;
|
|
|
|
|
case ABS_Y:
|
|
|
|
|
t->y = e->value;
|
|
|
|
|
t->millis = time;
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 15:12:22 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_fake_touch(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-14 15:12:22 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
unsigned int nfake_touches;
|
2014-07-18 11:06:38 +02:00
|
|
|
unsigned int i, start;
|
2014-02-14 15:12:22 +10:00
|
|
|
|
2014-12-12 10:31:12 +10:00
|
|
|
tp_fake_finger_set(tp, e->code, e->value != 0);
|
2014-02-14 15:12:22 +10:00
|
|
|
|
2014-12-12 10:31:12 +10:00
|
|
|
nfake_touches = tp_fake_finger_count(tp);
|
2014-02-14 15:12:22 +10:00
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
start = tp->has_mt ? tp->real_touches : 0;
|
|
|
|
|
for (i = start; i < tp->ntouches; i++) {
|
2014-02-14 15:12:22 +10:00
|
|
|
t = tp_get_touch(tp, i);
|
2014-07-18 11:06:38 +02:00
|
|
|
if (i < nfake_touches)
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_begin_touch(tp, t, time);
|
2014-07-18 11:06:38 +02:00
|
|
|
else
|
2014-07-18 11:06:36 +02:00
|
|
|
tp_end_touch(tp, t, time);
|
2014-02-14 15:12:22 +10:00
|
|
|
}
|
|
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
/* On mt the actual touch info may arrive after BTN_TOOL_FOO */
|
|
|
|
|
assert(tp->has_mt || tp->nfingers_down == nfake_touches);
|
2014-02-14 15:12:22 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
|
|
|
|
tp_process_key(struct tp_dispatch *tp,
|
|
|
|
|
const struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
switch (e->code) {
|
|
|
|
|
case BTN_LEFT:
|
|
|
|
|
case BTN_MIDDLE:
|
|
|
|
|
case BTN_RIGHT:
|
2014-03-27 13:09:06 +10:00
|
|
|
tp_process_button(tp, e, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
2014-02-14 14:18:27 +10:00
|
|
|
case BTN_TOUCH:
|
2014-02-14 15:12:22 +10:00
|
|
|
case BTN_TOOL_DOUBLETAP:
|
|
|
|
|
case BTN_TOOL_TRIPLETAP:
|
|
|
|
|
case BTN_TOOL_QUADTAP:
|
2014-07-18 11:06:38 +02:00
|
|
|
tp_process_fake_touch(tp, e, time);
|
2014-02-14 14:18:27 +10:00
|
|
|
break;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-17 14:24:20 +10:00
|
|
|
static void
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_unpin_finger(struct tp_dispatch *tp, struct tp_touch *t)
|
2014-02-17 14:24:20 +10:00
|
|
|
{
|
2014-04-15 14:27:59 +02:00
|
|
|
unsigned int xdist, ydist;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
if (!t->pinned.is_pinned)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
xdist = abs(t->x - t->pinned.center_x);
|
|
|
|
|
ydist = abs(t->y - t->pinned.center_y);
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-04 17:22:02 +02:00
|
|
|
if (xdist * xdist + ydist * ydist >=
|
|
|
|
|
tp->buttons.motion_dist * tp->buttons.motion_dist) {
|
|
|
|
|
t->pinned.is_pinned = false;
|
|
|
|
|
tp_set_pointer(tp, t);
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_pin_fingers(struct tp_dispatch *tp)
|
2014-02-17 14:24:20 +10:00
|
|
|
{
|
2014-04-15 14:27:59 +02:00
|
|
|
struct tp_touch *t;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-04-15 14:27:59 +02:00
|
|
|
t->is_pointer = false;
|
|
|
|
|
t->pinned.is_pinned = true;
|
|
|
|
|
t->pinned.center_x = t->x;
|
|
|
|
|
t->pinned.center_y = t->y;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-04 17:22:02 +02:00
|
|
|
static int
|
|
|
|
|
tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
|
2014-07-14 16:06:51 +10:00
|
|
|
!t->palm.is_palm &&
|
2014-11-24 12:16:06 +01:00
|
|
|
!t->pinned.is_pinned &&
|
|
|
|
|
tp_button_touch_active(tp, t) &&
|
|
|
|
|
tp_edge_scroll_touch_active(tp, t);
|
2014-04-04 17:22:02 +02:00
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-04 17:22:02 +02:00
|
|
|
void
|
|
|
|
|
tp_set_pointer(struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *tmp = NULL;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-04 17:22:02 +02:00
|
|
|
/* Only set the touch as pointer if we don't have one yet */
|
|
|
|
|
tp_for_each_touch(tp, tmp) {
|
|
|
|
|
if (tmp->is_pointer)
|
|
|
|
|
return;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
|
2014-04-04 17:22:02 +02:00
|
|
|
if (tp_touch_active(tp, t))
|
|
|
|
|
t->is_pointer = true;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
|
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
static void
|
2014-07-14 16:06:51 +10:00
|
|
|
tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
2014-07-10 17:34:08 +10:00
|
|
|
{
|
2014-07-14 16:06:51 +10:00
|
|
|
const int PALM_TIMEOUT = 200; /* ms */
|
2014-07-14 16:38:46 +10:00
|
|
|
const int DIRECTIONS = NE|E|SE|SW|W|NW;
|
2014-07-14 16:06:51 +10:00
|
|
|
|
|
|
|
|
/* If labelled a touch as palm, we unlabel as palm when
|
2014-07-14 16:38:46 +10:00
|
|
|
we move out of the palm edge zone within the timeout, provided
|
|
|
|
|
the direction is within 45 degrees of the horizontal.
|
2014-07-14 16:06:51 +10:00
|
|
|
*/
|
|
|
|
|
if (t->palm.is_palm) {
|
|
|
|
|
if (time < t->palm.time + PALM_TIMEOUT &&
|
|
|
|
|
(t->x > tp->palm.left_edge && t->x < tp->palm.right_edge)) {
|
2014-07-14 16:38:46 +10:00
|
|
|
int dirs = vector_get_direction(t->x - t->palm.x, t->y - t->palm.y);
|
|
|
|
|
if ((dirs & DIRECTIONS) && !(dirs & ~DIRECTIONS)) {
|
|
|
|
|
t->palm.is_palm = false;
|
|
|
|
|
tp_set_pointer(tp, t);
|
|
|
|
|
}
|
2014-07-14 16:06:51 +10:00
|
|
|
}
|
2014-07-10 17:34:08 +10:00
|
|
|
return;
|
2014-07-14 16:06:51 +10:00
|
|
|
}
|
2014-07-10 17:34:08 +10:00
|
|
|
|
|
|
|
|
/* palm must start in exclusion zone, it's ok to move into
|
|
|
|
|
the zone without being a palm */
|
|
|
|
|
if (t->state != TOUCH_BEGIN ||
|
|
|
|
|
(t->x > tp->palm.left_edge && t->x < tp->palm.right_edge))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* don't detect palm in software button areas, it's
|
|
|
|
|
likely that legitimate touches start in the area
|
|
|
|
|
covered by the exclusion zone */
|
|
|
|
|
if (tp->buttons.is_clickpad &&
|
|
|
|
|
tp_button_is_inside_softbutton_area(tp, t))
|
|
|
|
|
return;
|
|
|
|
|
|
2014-07-14 16:06:51 +10:00
|
|
|
t->palm.is_palm = true;
|
|
|
|
|
t->palm.time = time;
|
2014-07-14 16:38:46 +10:00
|
|
|
t->palm.x = t->x;
|
|
|
|
|
t->palm.y = t->y;
|
2014-07-10 17:34:08 +10:00
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:04 +01:00
|
|
|
static void
|
|
|
|
|
tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
int nchanged = 0;
|
|
|
|
|
double dx = 0, dy =0;
|
|
|
|
|
double tmpx, tmpy;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (tp_touch_active(tp, t) && t->dirty) {
|
|
|
|
|
nchanged++;
|
|
|
|
|
tp_get_delta(t, &tmpx, &tmpy);
|
|
|
|
|
|
|
|
|
|
dx += tmpx;
|
|
|
|
|
dy += tmpy;
|
|
|
|
|
}
|
|
|
|
|
/* Stop spurious MOTION events at the end of scrolling */
|
|
|
|
|
t->is_pointer = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nchanged == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dx /= nchanged;
|
|
|
|
|
dy /= nchanged;
|
|
|
|
|
|
2014-12-04 11:44:09 +08:00
|
|
|
tp_filter_motion(tp, &dx, &dy, NULL, NULL, time);
|
2014-11-24 12:16:04 +01:00
|
|
|
|
Add pointer axis sources to the API
For a caller to implement/provide kinetic scrolling ("inertial scrolling",
"fling scrolling"), it needs to know how the scrolling motion was implemented,
and what to expect in the future. Add this information to the pointer axis
event.
The three scroll sources we have are:
* wheels: scrolling is in discreet steps, you don't know when it ends, the
wheel will just stop sending events
* fingers: scrolling is continuous coordinate space, we know when it stops and
we can tell the caller
* continuous: scrolling is in continuous coordinate space but we may or may not
know when it stops. if scroll lock is used, the device may never technically
get out of scroll mode even if it doesn't send events at any given moment
Use case: trackpoint/trackball scroll emulation on button press
The stop event is now codified in the API documentation, so callers can use
that for kinetic scrolling. libinput does not implement kinetic scrolling
itself.
Not covered by this patch:
* The wheel event is currently defined as "typical mouse wheel step", this is
different to Qt where the step value is 1/8 of a degree. Some better
definition here may help.
* It is unclear how an absolute device would map into relative motion if the
device itself is not controlling absolute motion.
* For diagonal scrolling, the vertical/horizontal terminator events would come
in separately. The caller would have to deal with that somehow.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Original patch, before the rebase onto today's master:
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2014-11-05 16:22:07 +10:00
|
|
|
evdev_post_scroll(tp->device,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_SOURCE_FINGER,
|
|
|
|
|
dx, dy);
|
2014-12-18 10:37:38 +10:00
|
|
|
tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_ACTIVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_twofinger_stop_scroll(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t, *ptr = NULL;
|
|
|
|
|
int nfingers_down = 0;
|
|
|
|
|
|
Add pointer axis sources to the API
For a caller to implement/provide kinetic scrolling ("inertial scrolling",
"fling scrolling"), it needs to know how the scrolling motion was implemented,
and what to expect in the future. Add this information to the pointer axis
event.
The three scroll sources we have are:
* wheels: scrolling is in discreet steps, you don't know when it ends, the
wheel will just stop sending events
* fingers: scrolling is continuous coordinate space, we know when it stops and
we can tell the caller
* continuous: scrolling is in continuous coordinate space but we may or may not
know when it stops. if scroll lock is used, the device may never technically
get out of scroll mode even if it doesn't send events at any given moment
Use case: trackpoint/trackball scroll emulation on button press
The stop event is now codified in the API documentation, so callers can use
that for kinetic scrolling. libinput does not implement kinetic scrolling
itself.
Not covered by this patch:
* The wheel event is currently defined as "typical mouse wheel step", this is
different to Qt where the step value is 1/8 of a degree. Some better
definition here may help.
* It is unclear how an absolute device would map into relative motion if the
device itself is not controlling absolute motion.
* For diagonal scrolling, the vertical/horizontal terminator events would come
in separately. The caller would have to deal with that somehow.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Original patch, before the rebase onto today's master:
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2014-11-05 16:22:07 +10:00
|
|
|
evdev_stop_scroll(tp->device,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
|
2014-12-18 10:37:38 +10:00
|
|
|
|
|
|
|
|
/* If we were scrolling and now there's exactly 1 active finger,
|
|
|
|
|
switch back to pointer movement */
|
|
|
|
|
if (tp->scroll.twofinger_state == TWOFINGER_SCROLL_STATE_ACTIVE) {
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (tp_touch_active(tp, t)) {
|
|
|
|
|
nfingers_down++;
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
|
ptr = t;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nfingers_down == 1)
|
|
|
|
|
tp_set_pointer(tp, ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp->scroll.twofinger_state = TWOFINGER_SCROLL_STATE_NONE;
|
2014-11-24 12:16:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2014-11-24 12:16:06 +01:00
|
|
|
tp_twofinger_scroll_post_events(struct tp_dispatch *tp, uint64_t time)
|
2014-11-24 12:16:04 +01:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
int nfingers_down = 0;
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
/* No 2fg scrolling during tap-n-drag */
|
2014-11-24 12:16:04 +01:00
|
|
|
if (tp_tap_dragging(tp))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-12-09 12:47:10 +01:00
|
|
|
/* No 2fg scrolling while a clickpad is clicked */
|
|
|
|
|
if (tp->buttons.is_clickpad && tp->buttons.state)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2014-11-24 12:16:04 +01:00
|
|
|
/* Only count active touches for 2 finger scrolling */
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (tp_touch_active(tp, t))
|
|
|
|
|
nfingers_down++;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-18 10:37:38 +10:00
|
|
|
if (nfingers_down == 2) {
|
|
|
|
|
tp_post_twofinger_scroll(tp, time);
|
|
|
|
|
return 1;
|
2014-11-24 12:16:04 +01:00
|
|
|
}
|
|
|
|
|
|
2014-12-18 10:37:38 +10:00
|
|
|
tp_twofinger_stop_scroll(tp, time);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2014-11-24 12:16:04 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
static void
|
|
|
|
|
tp_scroll_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
/* Note this must be always called, so that it knows the state of
|
|
|
|
|
* touches when the scroll-mode changes.
|
|
|
|
|
*/
|
|
|
|
|
tp_edge_scroll_handle_state(tp, time);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct libinput *libinput = tp->device->base.seat->libinput;
|
|
|
|
|
|
|
|
|
|
switch (tp->scroll.method) {
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_2FG:
|
|
|
|
|
return tp_twofinger_scroll_post_events(tp, time);
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_EDGE:
|
|
|
|
|
return tp_edge_scroll_post_events(tp, time);
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
|
|
|
|
|
log_bug_libinput(libinput, "Unexpected scroll mode\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
struct libinput *libinput = tp->device->base.seat->libinput;
|
|
|
|
|
|
|
|
|
|
switch (tp->scroll.method) {
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_NO_SCROLL:
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_2FG:
|
2014-12-18 10:37:38 +10:00
|
|
|
tp_twofinger_stop_scroll(tp, time);
|
2014-11-24 12:16:06 +01:00
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_EDGE:
|
|
|
|
|
tp_edge_scroll_stop_events(tp, time);
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN:
|
|
|
|
|
log_bug_libinput(libinput, "Unexpected scroll mode\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove_scroll(struct tp_dispatch *tp)
|
2014-11-24 12:16:06 +01:00
|
|
|
{
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove_edge_scroll(tp);
|
2014-11-24 12:16:06 +01:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_process_state(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
2014-02-14 15:12:22 +10:00
|
|
|
struct tp_touch *first = tp_get_touch(tp, 0);
|
2014-07-18 11:06:38 +02:00
|
|
|
unsigned int i;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
for (i = 0; i < tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
2014-07-21 15:25:47 +02:00
|
|
|
|
|
|
|
|
/* semi-mt finger postions may "jump" when nfingers changes */
|
|
|
|
|
if (tp->semi_mt && tp->nfingers_down != tp->old_nfingers_down)
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
if (i >= tp->real_touches && t->state != TOUCH_NONE) {
|
2014-02-14 15:12:22 +10:00
|
|
|
t->x = first->x;
|
|
|
|
|
t->y = first->y;
|
|
|
|
|
if (!t->dirty)
|
|
|
|
|
t->dirty = first->dirty;
|
2014-07-10 17:17:53 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-07-18 11:06:37 +02:00
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-07-14 16:06:51 +10:00
|
|
|
tp_palm_detect(tp, t, time);
|
2014-07-10 17:34:08 +10:00
|
|
|
|
2014-02-06 18:57:10 +10:00
|
|
|
tp_motion_hysteresis(tp, t);
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_motion_history_push(t);
|
2014-04-15 14:27:59 +02:00
|
|
|
|
|
|
|
|
tp_unpin_finger(tp, t);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
tp_button_handle_state(tp, time);
|
2014-11-24 12:16:06 +01:00
|
|
|
tp_scroll_handle_state(tp, time);
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
|
2014-04-15 14:27:59 +02:00
|
|
|
/*
|
|
|
|
|
* We have a physical button down event on a clickpad. To avoid
|
|
|
|
|
* spurious pointer moves by the clicking finger we pin all fingers.
|
|
|
|
|
* We unpin fingers when they move more then a certain threshold to
|
|
|
|
|
* to allow drag and drop.
|
2014-02-17 14:24:20 +10:00
|
|
|
*/
|
|
|
|
|
if ((tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS) &&
|
2014-04-07 13:44:07 +02:00
|
|
|
tp->buttons.is_clickpad)
|
2014-04-15 14:27:59 +02:00
|
|
|
tp_pin_fingers(tp);
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (!t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
if (t->state == TOUCH_END)
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_NONE;
|
2014-07-18 11:06:38 +02:00
|
|
|
else if (t->state == TOUCH_BEGIN)
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_UPDATE;
|
|
|
|
|
|
|
|
|
|
t->dirty = false;
|
|
|
|
|
}
|
2014-02-07 13:48:06 +10:00
|
|
|
|
2014-07-21 15:25:47 +02:00
|
|
|
tp->old_nfingers_down = tp->nfingers_down;
|
2014-02-07 13:59:38 +10:00
|
|
|
tp->buttons.old_state = tp->buttons.state;
|
|
|
|
|
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued = TOUCHPAD_EVENT_NONE;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-12-09 12:47:10 +01:00
|
|
|
tp_get_pointer_delta(struct tp_dispatch *tp, double *dx, double *dy)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
|
2014-07-15 16:01:49 +10:00
|
|
|
if (!t->is_pointer) {
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (t->is_pointer)
|
|
|
|
|
break;
|
2014-02-17 14:24:20 +10:00
|
|
|
}
|
2014-07-15 16:01:49 +10:00
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-12-09 12:47:11 +01:00
|
|
|
if (!t->is_pointer || !t->dirty)
|
2014-07-15 16:01:49 +10:00
|
|
|
return;
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-12-09 12:47:10 +01:00
|
|
|
tp_get_delta(t, dx, dy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_get_active_touches_delta(struct tp_dispatch *tp, double *dx, double *dy)
|
|
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
double tdx, tdy;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tp->real_touches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
|
|
|
|
|
if (!tp_touch_active(tp, t) || !t->dirty)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
tp_get_delta(t, &tdx, &tdy);
|
|
|
|
|
*dx += tdx;
|
|
|
|
|
*dy += tdy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_post_pointer_motion(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
double dx = 0.0, dy = 0.0;
|
|
|
|
|
double dx_unaccel, dy_unaccel;
|
|
|
|
|
|
|
|
|
|
/* When a clickpad is clicked, combine motion of all active touches */
|
|
|
|
|
if (tp->buttons.is_clickpad && tp->buttons.state)
|
|
|
|
|
tp_get_active_touches_delta(tp, &dx, &dy);
|
|
|
|
|
else
|
|
|
|
|
tp_get_pointer_delta(tp, &dx, &dy);
|
|
|
|
|
|
2014-12-04 11:44:09 +08:00
|
|
|
tp_filter_motion(tp, &dx, &dy, &dx_unaccel, &dy_unaccel, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-12-04 11:44:09 +08:00
|
|
|
if (dx != 0.0 || dy != 0.0 || dx_unaccel != 0.0 || dy_unaccel != 0.0) {
|
|
|
|
|
pointer_notify_motion(&tp->device->base, time,
|
|
|
|
|
dx, dy, dx_unaccel, dy_unaccel);
|
|
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
|
2014-12-09 12:47:09 +01:00
|
|
|
static void
|
|
|
|
|
tp_post_events(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
int filter_motion = 0;
|
|
|
|
|
|
|
|
|
|
/* Only post (top) button events while suspended */
|
|
|
|
|
if (tp->device->suspended) {
|
|
|
|
|
tp_post_button_events(tp, time);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filter_motion |= tp_tap_handle_state(tp, time);
|
|
|
|
|
filter_motion |= tp_post_button_events(tp, time);
|
|
|
|
|
|
|
|
|
|
if (filter_motion || tp->sendevents.trackpoint_active) {
|
|
|
|
|
tp_stop_scroll_events(tp, time);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tp_post_scroll_events(tp, time) != 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
tp_post_pointer_motion(tp, time);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:05:49 +10:00
|
|
|
static void
|
|
|
|
|
tp_handle_state(struct tp_dispatch *tp,
|
|
|
|
|
uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
tp_process_state(tp, time);
|
|
|
|
|
tp_post_events(tp, time);
|
|
|
|
|
tp_post_process_state(tp, time);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
|
|
|
|
tp_process(struct evdev_dispatch *dispatch,
|
|
|
|
|
struct evdev_device *device,
|
|
|
|
|
struct input_event *e,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch *)dispatch;
|
|
|
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
|
case EV_ABS:
|
2014-02-14 14:18:27 +10:00
|
|
|
if (tp->has_mt)
|
|
|
|
|
tp_process_absolute(tp, e, time);
|
|
|
|
|
else
|
|
|
|
|
tp_process_absolute_st(tp, e, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
case EV_KEY:
|
|
|
|
|
tp_process_key(tp, e, time);
|
|
|
|
|
break;
|
|
|
|
|
case EV_SYN:
|
2014-09-01 17:05:49 +10:00
|
|
|
tp_handle_state(tp, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
static void
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove_sendevents(struct tp_dispatch *tp)
|
2014-09-28 13:21:08 +02:00
|
|
|
{
|
|
|
|
|
libinput_timer_cancel(&tp->sendevents.trackpoint_timer);
|
|
|
|
|
|
|
|
|
|
if (tp->buttons.trackpoint)
|
|
|
|
|
libinput_device_remove_event_listener(
|
|
|
|
|
&tp->sendevents.trackpoint_listener);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-05 12:50:39 +01:00
|
|
|
static void
|
|
|
|
|
tp_remove(struct evdev_dispatch *dispatch)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch*)dispatch;
|
|
|
|
|
|
|
|
|
|
tp_remove_tap(tp);
|
|
|
|
|
tp_remove_buttons(tp);
|
|
|
|
|
tp_remove_sendevents(tp);
|
|
|
|
|
tp_remove_scroll(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
|
|
|
|
tp_destroy(struct evdev_dispatch *dispatch)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch*)dispatch;
|
|
|
|
|
|
2014-03-25 12:14:39 +10:00
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
free(tp->touches);
|
|
|
|
|
free(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static void
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(struct tp_dispatch *tp)
|
2014-09-01 17:17:18 +10:00
|
|
|
{
|
|
|
|
|
uint64_t now = libinput_now(tp->device->base.seat->libinput);
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
|
|
|
|
|
/* Unroll the touchpad state.
|
|
|
|
|
* Release buttons first. If tp is a clickpad, the button event
|
|
|
|
|
* must come before the touch up. If it isn't, the order doesn't
|
|
|
|
|
* matter anyway
|
|
|
|
|
*
|
|
|
|
|
* Then cancel all timeouts on the taps, triggering the last set
|
|
|
|
|
* of events.
|
|
|
|
|
*
|
|
|
|
|
* Then lift all touches so the touchpad is in a neutral state.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
tp_release_all_buttons(tp, now);
|
|
|
|
|
tp_release_all_taps(tp, now);
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
tp_end_touch(tp, t, now);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp_handle_state(tp, now);
|
2014-09-16 16:22:40 +02:00
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
|
2014-09-16 16:22:40 +02:00
|
|
|
static void
|
|
|
|
|
tp_suspend(struct tp_dispatch *tp, struct evdev_device *device)
|
|
|
|
|
{
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(tp);
|
2014-09-16 16:22:41 +02:00
|
|
|
|
|
|
|
|
/* On devices with top softwarebuttons we don't actually suspend the
|
|
|
|
|
* device, to keep the "trackpoint" buttons working. tp_post_events()
|
|
|
|
|
* will only send events for the trackpoint while suspended.
|
|
|
|
|
*/
|
|
|
|
|
if (tp->buttons.has_topbuttons) {
|
|
|
|
|
evdev_notify_suspended_device(device);
|
2014-09-17 15:35:30 +02:00
|
|
|
/* Enlarge topbutton area while suspended */
|
|
|
|
|
tp_init_softbuttons(tp, device, 1.5);
|
2014-09-16 16:22:41 +02:00
|
|
|
} else {
|
|
|
|
|
evdev_device_suspend(device);
|
|
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_resume(struct tp_dispatch *tp, struct evdev_device *device)
|
|
|
|
|
{
|
2014-09-16 16:22:41 +02:00
|
|
|
if (tp->buttons.has_topbuttons) {
|
|
|
|
|
/* tap state-machine is offline while suspended, reset state */
|
2014-12-04 11:08:01 +10:00
|
|
|
tp_clear_state(tp);
|
2014-09-17 15:35:30 +02:00
|
|
|
/* restore original topbutton area size */
|
|
|
|
|
tp_init_softbuttons(tp, device, 1.0);
|
2014-09-16 16:22:41 +02:00
|
|
|
evdev_notify_resumed_device(device);
|
|
|
|
|
} else {
|
|
|
|
|
evdev_device_resume(device);
|
|
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
static void
|
|
|
|
|
tp_trackpoint_timeout(uint64_t now, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
|
|
|
|
tp_tap_resume(tp, now);
|
|
|
|
|
tp->sendevents.trackpoint_active = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_trackpoint_event(uint64_t time, struct libinput_event *event, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = data;
|
|
|
|
|
|
|
|
|
|
/* Buttons do not count as trackpad activity, as people may use
|
|
|
|
|
the trackpoint buttons in combination with the touchpad. */
|
|
|
|
|
if (event->type == LIBINPUT_EVENT_POINTER_BUTTON)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!tp->sendevents.trackpoint_active) {
|
Add pointer axis sources to the API
For a caller to implement/provide kinetic scrolling ("inertial scrolling",
"fling scrolling"), it needs to know how the scrolling motion was implemented,
and what to expect in the future. Add this information to the pointer axis
event.
The three scroll sources we have are:
* wheels: scrolling is in discreet steps, you don't know when it ends, the
wheel will just stop sending events
* fingers: scrolling is continuous coordinate space, we know when it stops and
we can tell the caller
* continuous: scrolling is in continuous coordinate space but we may or may not
know when it stops. if scroll lock is used, the device may never technically
get out of scroll mode even if it doesn't send events at any given moment
Use case: trackpoint/trackball scroll emulation on button press
The stop event is now codified in the API documentation, so callers can use
that for kinetic scrolling. libinput does not implement kinetic scrolling
itself.
Not covered by this patch:
* The wheel event is currently defined as "typical mouse wheel step", this is
different to Qt where the step value is 1/8 of a degree. Some better
definition here may help.
* It is unclear how an absolute device would map into relative motion if the
device itself is not controlling absolute motion.
* For diagonal scrolling, the vertical/horizontal terminator events would come
in separately. The caller would have to deal with that somehow.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Original patch, before the rebase onto today's master:
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
2014-11-05 16:22:07 +10:00
|
|
|
evdev_stop_scroll(tp->device,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_SOURCE_FINGER);
|
2014-09-28 13:21:08 +02:00
|
|
|
tp_tap_suspend(tp, time);
|
|
|
|
|
tp->sendevents.trackpoint_active = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libinput_timer_set(&tp->sendevents.trackpoint_timer,
|
|
|
|
|
time + DEFAULT_TRACKPOINT_ACTIVITY_TIMEOUT);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
static void
|
|
|
|
|
tp_device_added(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *added_device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
|
|
|
|
|
|
2014-09-16 16:22:38 +02:00
|
|
|
if (tp->buttons.trackpoint == NULL &&
|
2014-09-16 16:22:39 +02:00
|
|
|
(added_device->tags & EVDEV_TAG_TRACKPOINT)) {
|
|
|
|
|
/* Don't send any pending releases to the new trackpoint */
|
|
|
|
|
tp->buttons.active_is_topbutton = false;
|
2014-09-16 16:22:38 +02:00
|
|
|
tp->buttons.trackpoint = added_device;
|
2014-09-28 13:21:08 +02:00
|
|
|
libinput_device_add_event_listener(&added_device->base,
|
|
|
|
|
&tp->sendevents.trackpoint_listener,
|
|
|
|
|
tp_trackpoint_event, tp);
|
2014-09-16 16:22:39 +02:00
|
|
|
}
|
2014-09-16 16:22:38 +02:00
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
if (tp->sendevents.current_mode !=
|
|
|
|
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (added_device->tags & EVDEV_TAG_EXTERNAL_MOUSE)
|
|
|
|
|
tp_suspend(tp, device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_device_removed(struct evdev_device *device,
|
|
|
|
|
struct evdev_device *removed_device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)device->dispatch;
|
|
|
|
|
struct libinput_device *dev;
|
|
|
|
|
|
2014-09-16 16:22:39 +02:00
|
|
|
if (removed_device == tp->buttons.trackpoint) {
|
|
|
|
|
/* Clear any pending releases for the trackpoint */
|
|
|
|
|
if (tp->buttons.active && tp->buttons.active_is_topbutton) {
|
|
|
|
|
tp->buttons.active = 0;
|
|
|
|
|
tp->buttons.active_is_topbutton = false;
|
|
|
|
|
}
|
2014-09-28 13:21:08 +02:00
|
|
|
libinput_device_remove_event_listener(
|
|
|
|
|
&tp->sendevents.trackpoint_listener);
|
2014-09-16 16:22:38 +02:00
|
|
|
tp->buttons.trackpoint = NULL;
|
2014-09-16 16:22:39 +02:00
|
|
|
}
|
2014-09-16 16:22:38 +02:00
|
|
|
|
2014-09-03 15:45:57 +10:00
|
|
|
if (tp->sendevents.current_mode !=
|
|
|
|
|
LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
list_for_each(dev, &device->base.seat->devices_list, link) {
|
|
|
|
|
struct evdev_device *d = (struct evdev_device*)dev;
|
|
|
|
|
if (d != removed_device &&
|
|
|
|
|
(d->tags & EVDEV_TAG_EXTERNAL_MOUSE)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp_resume(tp, device);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_tag_device(struct evdev_device *device,
|
|
|
|
|
struct udev_device *udev_device)
|
|
|
|
|
{
|
|
|
|
|
int bustype;
|
|
|
|
|
|
|
|
|
|
/* simple approach: touchpads on USB or Bluetooth are considered
|
|
|
|
|
* external, anything else is internal. Exception is Apple -
|
|
|
|
|
* internal touchpads are connected over USB and it doesn't have
|
|
|
|
|
* external USB touchpads anyway.
|
|
|
|
|
*/
|
|
|
|
|
bustype = libevdev_get_id_bustype(device->evdev);
|
|
|
|
|
if (bustype == BUS_USB) {
|
|
|
|
|
if (libevdev_get_id_vendor(device->evdev) == VENDOR_ID_APPLE)
|
|
|
|
|
device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
|
|
|
|
|
} else if (bustype != BUS_BLUETOOTH)
|
|
|
|
|
device->tags |= EVDEV_TAG_INTERNAL_TOUCHPAD;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static struct evdev_dispatch_interface tp_interface = {
|
|
|
|
|
tp_process,
|
2014-12-05 12:50:39 +01:00
|
|
|
tp_remove,
|
2014-09-03 14:34:52 +10:00
|
|
|
tp_destroy,
|
2014-09-03 15:45:57 +10:00
|
|
|
tp_device_added,
|
|
|
|
|
tp_device_removed,
|
2014-09-16 16:22:39 +02:00
|
|
|
tp_device_removed, /* device_suspended, treat as remove */
|
|
|
|
|
tp_device_added, /* device_resumed, treat as add */
|
2014-09-03 15:45:57 +10:00
|
|
|
tp_tag_device,
|
2014-02-06 15:05:36 +10:00
|
|
|
};
|
|
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
static void
|
|
|
|
|
tp_init_touch(struct tp_dispatch *tp,
|
|
|
|
|
struct tp_touch *t)
|
|
|
|
|
{
|
2014-06-06 17:01:06 +02:00
|
|
|
t->tp = tp;
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:05:36 +10:00
|
|
|
static int
|
2014-02-06 15:32:32 +10:00
|
|
|
tp_init_slots(struct tp_dispatch *tp,
|
2014-02-06 15:05:36 +10:00
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-14 14:18:27 +10:00
|
|
|
const struct input_absinfo *absinfo;
|
2014-07-18 11:06:38 +02:00
|
|
|
struct map {
|
|
|
|
|
unsigned int code;
|
|
|
|
|
int ntouches;
|
|
|
|
|
} max_touches[] = {
|
|
|
|
|
{ BTN_TOOL_QUINTTAP, 5 },
|
|
|
|
|
{ BTN_TOOL_QUADTAP, 4 },
|
|
|
|
|
{ BTN_TOOL_TRIPLETAP, 3 },
|
|
|
|
|
{ BTN_TOOL_DOUBLETAP, 2 },
|
|
|
|
|
};
|
|
|
|
|
struct map *m;
|
|
|
|
|
unsigned int i, n_btn_tool_touches = 1;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-02-14 14:18:27 +10:00
|
|
|
absinfo = libevdev_get_abs_info(device->evdev, ABS_MT_SLOT);
|
|
|
|
|
if (absinfo) {
|
2014-07-18 11:06:38 +02:00
|
|
|
tp->real_touches = absinfo->maximum + 1;
|
2014-02-14 14:18:27 +10:00
|
|
|
tp->slot = absinfo->value;
|
|
|
|
|
tp->has_mt = true;
|
|
|
|
|
} else {
|
2014-07-18 11:06:38 +02:00
|
|
|
tp->real_touches = 1;
|
2014-02-14 14:18:27 +10:00
|
|
|
tp->slot = 0;
|
|
|
|
|
tp->has_mt = false;
|
2014-07-18 11:06:38 +02:00
|
|
|
}
|
2014-04-15 14:27:58 +02:00
|
|
|
|
2014-07-21 15:25:47 +02:00
|
|
|
tp->semi_mt = libevdev_has_property(device->evdev, INPUT_PROP_SEMI_MT);
|
|
|
|
|
|
2014-07-18 11:06:38 +02:00
|
|
|
ARRAY_FOR_EACH(max_touches, m) {
|
|
|
|
|
if (libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_KEY,
|
|
|
|
|
m->code)) {
|
|
|
|
|
n_btn_tool_touches = m->ntouches;
|
|
|
|
|
break;
|
2014-04-15 14:27:58 +02:00
|
|
|
}
|
2014-02-14 14:18:27 +10:00
|
|
|
}
|
2014-07-18 11:06:38 +02:00
|
|
|
|
|
|
|
|
tp->ntouches = max(tp->real_touches, n_btn_tool_touches);
|
|
|
|
|
tp->touches = calloc(tp->ntouches, sizeof(struct tp_touch));
|
2014-04-29 01:26:28 +02:00
|
|
|
if (!tp->touches)
|
|
|
|
|
return -1;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
touchpad: Add clickpad-style software buttons
Almost all non Apple touchpads have visible markings for software button areas,
so limit clickfinger behavior to Apple clickpads, and implement software button
areas for others.
This is a slightly fancier implementation than the simplest model and ported
over from libtouchpad. It implements a state machine for the software buttons
with left and right buttons currently implemented. Buttons are oriented
left-to-right, in a horizontal bar. No random button placement allowed.
In general, the procedure is:
- if a finger sets down in the left button area, a click is a left click
- if a finger sets down in the right button area, a click is a right click
- if a finger leaves the button area, a click is a left click
- if a finger starts outside the button area, a click is a left click
Two timeouts are used to handle buttons more smoothly:
- if a finger sets down in a button area but "immediately" moves over
to a different area, that area takes effect on a click.
- if a finger leaves a button area and "immediately" clicks or moves back into
the area, the button still takes effect on a click.
- if a finger changes between areas and stays there for a timeout, that area
takes effect on a click.
Note the button area states are named BOTTOM_foo to make it easier to later
add support for a top button area such as can be found on the Thinkpad [2-5]40
series.
Co-authored-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Jonas Ådahl <jadahl@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2014-03-28 09:44:11 +10:00
|
|
|
for (i = 0; i < tp->ntouches; i++)
|
|
|
|
|
tp_init_touch(tp, &tp->touches[i]);
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-01 14:53:18 +02:00
|
|
|
static int
|
|
|
|
|
tp_init_accel(struct tp_dispatch *tp, double diagonal)
|
2014-05-24 16:53:45 +02:00
|
|
|
{
|
|
|
|
|
int res_x, res_y;
|
|
|
|
|
|
|
|
|
|
if (tp->has_mt) {
|
|
|
|
|
res_x = libevdev_get_abs_resolution(tp->device->evdev,
|
|
|
|
|
ABS_MT_POSITION_X);
|
|
|
|
|
res_y = libevdev_get_abs_resolution(tp->device->evdev,
|
|
|
|
|
ABS_MT_POSITION_Y);
|
|
|
|
|
} else {
|
|
|
|
|
res_x = libevdev_get_abs_resolution(tp->device->evdev,
|
|
|
|
|
ABS_X);
|
|
|
|
|
res_y = libevdev_get_abs_resolution(tp->device->evdev,
|
|
|
|
|
ABS_Y);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-01 14:53:18 +02:00
|
|
|
/*
|
|
|
|
|
* Not all touchpads report the same amount of units/mm (resolution).
|
2014-11-28 10:08:02 +10:00
|
|
|
* Normalize motion events to the default mouse DPI as base
|
|
|
|
|
* (unaccelerated) speed. This also evens out any differences in x
|
|
|
|
|
* and y resolution, so that a circle on the
|
2014-07-08 13:43:45 +10:00
|
|
|
* touchpad does not turn into an elipse on the screen.
|
2014-07-01 14:53:18 +02:00
|
|
|
*/
|
|
|
|
|
if (res_x > 1 && res_y > 1) {
|
2014-11-28 10:08:02 +10:00
|
|
|
tp->accel.x_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_x;
|
|
|
|
|
tp->accel.y_scale_coeff = (DEFAULT_MOUSE_DPI/25.4) / res_y;
|
2014-11-28 10:09:21 +10:00
|
|
|
|
|
|
|
|
/* FIXME: once normalized, touchpads see the same
|
|
|
|
|
acceleration as mice. that is technically correct but
|
|
|
|
|
subjectively wrong, we expect a touchpad to be a lot
|
|
|
|
|
slower than a mouse.
|
|
|
|
|
For now, apply a magic factor here until this is
|
|
|
|
|
fixed in the actual filter code.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
const double MAGIC = 0.4;
|
|
|
|
|
tp->accel.x_scale_coeff *= MAGIC;
|
|
|
|
|
tp->accel.y_scale_coeff *= MAGIC;
|
|
|
|
|
}
|
2014-05-24 16:53:45 +02:00
|
|
|
} else {
|
2014-07-01 14:53:18 +02:00
|
|
|
/*
|
|
|
|
|
* For touchpads where the driver does not provide resolution, fall
|
|
|
|
|
* back to scaling motion events based on the diagonal size in units.
|
|
|
|
|
*/
|
|
|
|
|
tp->accel.x_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
|
|
|
|
|
tp->accel.y_scale_coeff = DEFAULT_ACCEL_NUMERATOR / diagonal;
|
2014-05-24 16:53:45 +02:00
|
|
|
}
|
2014-02-06 19:43:48 +10:00
|
|
|
|
2014-07-04 09:44:43 +10:00
|
|
|
if (evdev_device_init_pointer_acceleration(tp->device) == -1)
|
2014-02-06 19:43:48 +10:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-11-04 10:43:39 +01:00
|
|
|
static uint32_t
|
2014-11-19 12:16:28 +10:00
|
|
|
tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
2014-11-19 12:16:28 +10:00
|
|
|
uint32_t methods = LIBINPUT_CONFIG_SCROLL_NO_SCROLL;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
|
|
|
|
if (tp->ntouches >= 2)
|
2014-11-19 12:16:28 +10:00
|
|
|
methods |= LIBINPUT_CONFIG_SCROLL_2FG;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
if (!tp->buttons.is_clickpad)
|
|
|
|
|
methods |= LIBINPUT_CONFIG_SCROLL_EDGE;
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
return methods;
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
2014-11-19 12:16:28 +10:00
|
|
|
tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
|
|
|
|
|
enum libinput_config_scroll_method method)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
if (method == tp->scroll.method)
|
2014-11-04 10:43:39 +01:00
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
tp_stop_scroll_events(tp, libinput_now(device->seat->libinput));
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->scroll.method = method;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_config_scroll_method_get_method(struct libinput_device *device)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
return tp->scroll.method;
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:06 +01:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_get_default_method(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
if (tp->ntouches >= 2)
|
|
|
|
|
return LIBINPUT_CONFIG_SCROLL_2FG;
|
|
|
|
|
else
|
|
|
|
|
return LIBINPUT_CONFIG_SCROLL_EDGE;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
static enum libinput_config_scroll_method
|
|
|
|
|
tp_scroll_config_scroll_method_get_default_method(struct libinput_device *device)
|
2014-11-04 10:43:39 +01:00
|
|
|
{
|
2014-11-24 12:16:06 +01:00
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return tp_scroll_get_default_method(tp);
|
2014-11-04 10:43:39 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-18 15:51:53 +10:00
|
|
|
static int
|
2014-11-18 11:01:10 +10:00
|
|
|
tp_init_scroll(struct tp_dispatch *tp, struct evdev_device *device)
|
2014-09-18 15:51:53 +10:00
|
|
|
{
|
2014-11-24 12:16:06 +01:00
|
|
|
if (tp_edge_scroll_init(tp, device) != 0)
|
|
|
|
|
return -1;
|
2014-09-18 15:51:53 +10:00
|
|
|
|
2014-11-18 11:01:10 +10:00
|
|
|
evdev_init_natural_scroll(device);
|
2014-09-18 15:51:53 +10:00
|
|
|
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->scroll.config_method.get_methods = tp_scroll_config_scroll_method_get_methods;
|
|
|
|
|
tp->scroll.config_method.set_method = tp_scroll_config_scroll_method_set_method;
|
|
|
|
|
tp->scroll.config_method.get_method = tp_scroll_config_scroll_method_get_method;
|
|
|
|
|
tp->scroll.config_method.get_default_method = tp_scroll_config_scroll_method_get_default_method;
|
2014-11-24 12:16:06 +01:00
|
|
|
tp->scroll.method = tp_scroll_get_default_method(tp);
|
2014-11-19 12:16:28 +10:00
|
|
|
tp->device->base.config.scroll_method = &tp->scroll.config_method;
|
2014-11-04 10:43:39 +01:00
|
|
|
|
2014-11-10 09:24:37 +10:00
|
|
|
/* In mm for touchpads with valid resolution, see tp_init_accel() */
|
|
|
|
|
tp->device->scroll.threshold = 5.0;
|
|
|
|
|
|
2014-09-18 15:51:53 +10:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
static int
|
|
|
|
|
tp_init_palmdetect(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
int width;
|
|
|
|
|
|
2014-07-21 11:27:21 +10:00
|
|
|
tp->palm.right_edge = INT_MAX;
|
|
|
|
|
tp->palm.left_edge = INT_MIN;
|
|
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
width = abs(device->abs.absinfo_x->maximum -
|
|
|
|
|
device->abs.absinfo_x->minimum);
|
|
|
|
|
|
2014-07-21 14:20:35 +10:00
|
|
|
/* Apple touchpads are always big enough to warrant palm detection */
|
|
|
|
|
if (evdev_device_get_id_vendor(device) != VENDOR_ID_APPLE) {
|
|
|
|
|
/* We don't know how big the touchpad is */
|
|
|
|
|
if (device->abs.absinfo_x->resolution == 1)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
/* Enable palm detection on touchpads >= 80 mm. Anything smaller
|
|
|
|
|
probably won't need it, until we find out it does */
|
|
|
|
|
if (width/device->abs.absinfo_x->resolution < 80)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-07-15 14:01:00 +10:00
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
/* palm edges are 5% of the width on each side */
|
|
|
|
|
tp->palm.right_edge = device->abs.absinfo_x->maximum - width * 0.05;
|
|
|
|
|
tp->palm.left_edge = device->abs.absinfo_x->minimum + width * 0.05;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
static int
|
|
|
|
|
tp_init_sendevents(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
libinput_timer_init(&tp->sendevents.trackpoint_timer,
|
|
|
|
|
tp->device->base.seat->libinput,
|
|
|
|
|
tp_trackpoint_timeout, tp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static int
|
|
|
|
|
tp_init(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-06 18:57:10 +10:00
|
|
|
int width, height;
|
|
|
|
|
double diagonal;
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
tp->base.interface = &tp_interface;
|
|
|
|
|
tp->device = device;
|
|
|
|
|
|
|
|
|
|
if (tp_init_slots(tp, device) != 0)
|
|
|
|
|
return -1;
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2014-06-19 11:11:36 +10:00
|
|
|
width = abs(device->abs.absinfo_x->maximum -
|
|
|
|
|
device->abs.absinfo_x->minimum);
|
|
|
|
|
height = abs(device->abs.absinfo_y->maximum -
|
|
|
|
|
device->abs.absinfo_y->minimum);
|
2014-02-06 18:57:10 +10:00
|
|
|
diagonal = sqrt(width*width + height*height);
|
|
|
|
|
|
|
|
|
|
tp->hysteresis.margin_x =
|
|
|
|
|
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
|
|
|
|
|
tp->hysteresis.margin_y =
|
|
|
|
|
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
|
|
|
|
|
|
2014-02-06 19:43:48 +10:00
|
|
|
if (tp_init_accel(tp, diagonal) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2014-02-07 15:18:17 +10:00
|
|
|
if (tp_init_tap(tp) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2014-03-27 13:09:06 +10:00
|
|
|
if (tp_init_buttons(tp, device) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2014-07-10 17:34:08 +10:00
|
|
|
if (tp_init_palmdetect(tp, device) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2014-09-28 13:21:08 +02:00
|
|
|
if (tp_init_sendevents(tp, device) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
2014-11-18 11:01:10 +10:00
|
|
|
if (tp_init_scroll(tp, device) != 0)
|
2014-11-04 10:43:39 +01:00
|
|
|
return -1;
|
|
|
|
|
|
2014-07-26 21:34:47 +02:00
|
|
|
device->seat_caps |= EVDEV_DEVICE_POINTER;
|
|
|
|
|
|
2014-02-06 15:05:36 +10:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
static uint32_t
|
|
|
|
|
tp_sendevents_get_modes(struct libinput_device *device)
|
|
|
|
|
{
|
2014-09-03 15:45:57 +10:00
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
2014-10-30 15:36:52 +10:00
|
|
|
uint32_t modes = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
|
2014-09-03 15:45:57 +10:00
|
|
|
|
|
|
|
|
if (evdev->tags & EVDEV_TAG_INTERNAL_TOUCHPAD)
|
|
|
|
|
modes |= LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
|
|
|
|
|
|
|
|
|
|
return modes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_suspend_conditional(struct tp_dispatch *tp,
|
|
|
|
|
struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct libinput_device *dev;
|
|
|
|
|
|
|
|
|
|
list_for_each(dev, &device->base.seat->devices_list, link) {
|
|
|
|
|
struct evdev_device *d = (struct evdev_device*)dev;
|
|
|
|
|
if (d->tags & EVDEV_TAG_EXTERNAL_MOUSE) {
|
|
|
|
|
tp_suspend(tp, device);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-01 17:17:18 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_status
|
|
|
|
|
tp_sendevents_set_mode(struct libinput_device *device,
|
|
|
|
|
enum libinput_config_send_events_mode mode)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
2014-10-30 15:36:52 +10:00
|
|
|
/* DISABLED overrides any DISABLED_ON_ */
|
|
|
|
|
if ((mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED) &&
|
|
|
|
|
(mode & LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE))
|
|
|
|
|
mode &= ~LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
if (mode == tp->sendevents.current_mode)
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
|
|
|
|
|
switch(mode) {
|
|
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED:
|
|
|
|
|
tp_resume(tp, evdev);
|
|
|
|
|
break;
|
|
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED:
|
|
|
|
|
tp_suspend(tp, evdev);
|
|
|
|
|
break;
|
2014-09-03 15:45:57 +10:00
|
|
|
case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE:
|
|
|
|
|
tp_suspend_conditional(tp, evdev);
|
|
|
|
|
break;
|
2014-09-01 17:17:18 +10:00
|
|
|
default:
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_UNSUPPORTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tp->sendevents.current_mode = mode;
|
|
|
|
|
|
|
|
|
|
return LIBINPUT_CONFIG_STATUS_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_send_events_mode
|
|
|
|
|
tp_sendevents_get_mode(struct libinput_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct evdev_device *evdev = (struct evdev_device*)device;
|
|
|
|
|
struct tp_dispatch *dispatch = (struct tp_dispatch*)evdev->dispatch;
|
|
|
|
|
|
|
|
|
|
return dispatch->sendevents.current_mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum libinput_config_send_events_mode
|
|
|
|
|
tp_sendevents_get_default_mode(struct libinput_device *device)
|
|
|
|
|
{
|
|
|
|
|
return LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-23 14:35:42 +10:00
|
|
|
static void
|
|
|
|
|
tp_change_to_left_handed(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp = (struct tp_dispatch *)device->dispatch;
|
|
|
|
|
|
2015-01-06 21:20:22 -05:00
|
|
|
if (device->left_handed.want_enabled == device->left_handed.enabled)
|
2014-09-23 14:35:42 +10:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (tp->buttons.state & 0x3) /* BTN_LEFT|BTN_RIGHT */
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* tapping and clickfinger aren't affected by left-handed config,
|
|
|
|
|
* so checking physical buttons is enough */
|
|
|
|
|
|
2015-01-06 21:20:22 -05:00
|
|
|
device->left_handed.enabled = device->left_handed.want_enabled;
|
2014-09-23 14:35:42 +10:00
|
|
|
}
|
|
|
|
|
|
2014-11-24 12:16:05 +01:00
|
|
|
struct model_lookup_t {
|
|
|
|
|
uint16_t vendor;
|
|
|
|
|
uint16_t product_start;
|
|
|
|
|
uint16_t product_end;
|
|
|
|
|
enum touchpad_model model;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct model_lookup_t model_lookup_table[] = {
|
|
|
|
|
{ 0x0002, 0x0007, 0x0007, MODEL_SYNAPTICS },
|
|
|
|
|
{ 0x0002, 0x0008, 0x0008, MODEL_ALPS },
|
|
|
|
|
{ 0x0002, 0x000e, 0x000e, MODEL_ELANTECH },
|
|
|
|
|
{ 0x05ac, 0, 0x0222, MODEL_APPLETOUCH },
|
|
|
|
|
{ 0x05ac, 0x0223, 0x0228, MODEL_UNIBODY_MACBOOK },
|
|
|
|
|
{ 0x05ac, 0x0229, 0x022b, MODEL_APPLETOUCH },
|
|
|
|
|
{ 0x05ac, 0x022c, 0xffff, MODEL_UNIBODY_MACBOOK },
|
|
|
|
|
{ 0, 0, 0, 0 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static enum touchpad_model
|
|
|
|
|
tp_get_model(struct evdev_device *device)
|
|
|
|
|
{
|
|
|
|
|
struct model_lookup_t *lookup;
|
|
|
|
|
uint16_t vendor = libevdev_get_id_vendor(device->evdev);
|
|
|
|
|
uint16_t product = libevdev_get_id_product(device->evdev);
|
|
|
|
|
|
|
|
|
|
for (lookup = model_lookup_table; lookup->vendor; lookup++) {
|
|
|
|
|
if (lookup->vendor == vendor &&
|
|
|
|
|
lookup->product_start <= product &&
|
|
|
|
|
product <= lookup->product_end)
|
|
|
|
|
return lookup->model;
|
|
|
|
|
}
|
|
|
|
|
return MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:05:36 +10:00
|
|
|
struct evdev_dispatch *
|
|
|
|
|
evdev_mt_touchpad_create(struct evdev_device *device)
|
|
|
|
|
{
|
2014-02-06 15:32:32 +10:00
|
|
|
struct tp_dispatch *tp;
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
tp = zalloc(sizeof *tp);
|
|
|
|
|
if (!tp)
|
2014-02-06 15:05:36 +10:00
|
|
|
return NULL;
|
|
|
|
|
|
2014-11-24 12:16:05 +01:00
|
|
|
tp->model = tp_get_model(device);
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
if (tp_init(tp, device) != 0) {
|
|
|
|
|
tp_destroy(&tp->base);
|
2014-02-06 15:05:36 +10:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 17:17:18 +10:00
|
|
|
device->base.config.sendevents = &tp->sendevents.config;
|
|
|
|
|
|
|
|
|
|
tp->sendevents.current_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
|
|
|
|
|
tp->sendevents.config.get_modes = tp_sendevents_get_modes;
|
|
|
|
|
tp->sendevents.config.set_mode = tp_sendevents_set_mode;
|
|
|
|
|
tp->sendevents.config.get_mode = tp_sendevents_get_mode;
|
|
|
|
|
tp->sendevents.config.get_default_mode = tp_sendevents_get_default_mode;
|
|
|
|
|
|
2014-09-23 14:35:42 +10:00
|
|
|
evdev_init_left_handed(device, tp_change_to_left_handed);
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
return &tp->base;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|