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-02-07 13:39:27 +10:00
|
|
|
#include "evdev-mt-touchpad.h"
|
2014-02-06 15:05:36 +10:00
|
|
|
|
2014-05-20 23:12:28 +02:00
|
|
|
#define DEFAULT_CONSTANT_ACCEL_NUMERATOR 100
|
|
|
|
|
#define DEFAULT_MIN_ACCEL_FACTOR 0.20
|
|
|
|
|
#define DEFAULT_MAX_ACCEL_FACTOR 0.40
|
2014-02-06 18:57:10 +10:00
|
|
|
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
|
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;
|
|
|
|
|
else if (diff < -margin)
|
|
|
|
|
return center + diff + margin;
|
|
|
|
|
return center + diff;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 19:43:48 +10:00
|
|
|
static double
|
|
|
|
|
tp_accel_profile(struct motion_filter *filter,
|
|
|
|
|
void *data,
|
|
|
|
|
double velocity,
|
2014-04-08 12:29:45 +02:00
|
|
|
uint64_t time)
|
2014-02-06 19:43:48 +10:00
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch *) data;
|
|
|
|
|
|
|
|
|
|
double accel_factor;
|
|
|
|
|
|
|
|
|
|
accel_factor = velocity * tp->accel.constant_factor;
|
|
|
|
|
|
|
|
|
|
if (accel_factor > tp->accel.max_factor)
|
|
|
|
|
accel_factor = tp->accel.max_factor;
|
|
|
|
|
else if (accel_factor < tp->accel.min_factor)
|
|
|
|
|
accel_factor = tp->accel.min_factor;
|
|
|
|
|
|
|
|
|
|
return accel_factor;
|
|
|
|
|
}
|
|
|
|
|
|
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-02-06 19:43:48 +10:00
|
|
|
static void
|
|
|
|
|
tp_filter_motion(struct tp_dispatch *tp,
|
2014-04-08 12:29:45 +02:00
|
|
|
double *dx, double *dy, 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
|
|
|
|
|
|
|
|
filter_dispatch(tp->filter, &motion, tp, time);
|
|
|
|
|
|
|
|
|
|
*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-02-06 15:32:32 +10:00
|
|
|
static inline void
|
|
|
|
|
tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
if (t->state != TOUCH_UPDATE) {
|
|
|
|
|
tp_motion_history_reset(t);
|
|
|
|
|
t->dirty = true;
|
|
|
|
|
t->state = TOUCH_BEGIN;
|
2014-04-15 14:27:59 +02:00
|
|
|
t->pinned.is_pinned = false;
|
2014-02-06 15:32:32 +10:00
|
|
|
tp->nfingers_down++;
|
|
|
|
|
assert(tp->nfingers_down >= 1);
|
2014-02-07 13:48:06 +10:00
|
|
|
tp->queued |= TOUCHPAD_EVENT_MOTION;
|
2014-02-06 15:32:32 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
|
tp_end_touch(struct tp_dispatch *tp, struct tp_touch *t)
|
|
|
|
|
{
|
|
|
|
|
if (t->state == TOUCH_NONE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
t->dirty = true;
|
2014-02-14 13:59:41 +10:00
|
|
|
t->is_pointer = 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-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)
|
|
|
|
|
{
|
|
|
|
|
return (x0 + x1 - x2 - x3) / 4;
|
|
|
|
|
}
|
|
|
|
|
|
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-02-06 15:32:32 +10:00
|
|
|
if (t->history.count < 4) {
|
|
|
|
|
*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:
|
|
|
|
|
t->millis = time;
|
|
|
|
|
if (e->value != -1)
|
|
|
|
|
tp_begin_touch(tp, t);
|
|
|
|
|
else
|
|
|
|
|
tp_end_touch(tp, t);
|
|
|
|
|
}
|
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 fake_touches;
|
|
|
|
|
unsigned int nfake_touches;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int shift;
|
|
|
|
|
|
|
|
|
|
if (e->code != BTN_TOUCH &&
|
|
|
|
|
(e->code < BTN_TOOL_DOUBLETAP || e->code > BTN_TOOL_QUADTAP))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
shift = e->code == BTN_TOUCH ? 0 : (e->code - BTN_TOOL_DOUBLETAP + 1);
|
|
|
|
|
|
|
|
|
|
if (e->value)
|
|
|
|
|
tp->fake_touches |= 1 << shift;
|
|
|
|
|
else
|
|
|
|
|
tp->fake_touches &= ~(0x1 << shift);
|
|
|
|
|
|
|
|
|
|
fake_touches = tp->fake_touches;
|
|
|
|
|
nfake_touches = 0;
|
|
|
|
|
while (fake_touches) {
|
|
|
|
|
nfake_touches++;
|
|
|
|
|
fake_touches >>= 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < tp->ntouches; i++) {
|
|
|
|
|
t = tp_get_touch(tp, i);
|
|
|
|
|
if (i >= nfake_touches) {
|
|
|
|
|
if (t->state != TOUCH_NONE) {
|
|
|
|
|
tp_end_touch(tp, t);
|
|
|
|
|
t->millis = time;
|
|
|
|
|
}
|
|
|
|
|
} else if (t->state != TOUCH_UPDATE &&
|
|
|
|
|
t->state != TOUCH_BEGIN) {
|
|
|
|
|
t->state = TOUCH_NONE;
|
|
|
|
|
tp_begin_touch(tp, t);
|
|
|
|
|
t->millis = time;
|
|
|
|
|
t->fake =true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(tp->nfingers_down == nfake_touches);
|
|
|
|
|
}
|
|
|
|
|
|
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:
|
|
|
|
|
if (!tp->has_mt)
|
|
|
|
|
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) &&
|
|
|
|
|
!t->pinned.is_pinned && tp_button_touch_active(tp, t);
|
|
|
|
|
}
|
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-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-02-06 15:32:32 +10:00
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-02-14 15:12:22 +10:00
|
|
|
if (!tp->has_mt && t != first && first->fake) {
|
|
|
|
|
t->x = first->x;
|
|
|
|
|
t->y = first->y;
|
|
|
|
|
if (!t->dirty)
|
|
|
|
|
t->dirty = first->dirty;
|
|
|
|
|
} else if (!t->dirty)
|
2014-02-06 15:32:32 +10:00
|
|
|
continue;
|
|
|
|
|
|
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-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-02-14 14:18:27 +10:00
|
|
|
if (t->state == TOUCH_END) {
|
2014-02-06 15:32:32 +10:00
|
|
|
t->state = TOUCH_NONE;
|
2014-02-14 14:18:27 +10:00
|
|
|
t->fake = false;
|
|
|
|
|
} 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-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
|
|
|
}
|
|
|
|
|
|
2014-02-06 19:17:22 +10:00
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_post_twofinger_scroll(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 19:17:22 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t;
|
|
|
|
|
int nchanged = 0;
|
|
|
|
|
double dx = 0, dy =0;
|
|
|
|
|
double tmpx, tmpy;
|
|
|
|
|
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
2014-04-08 08:34:20 +02:00
|
|
|
if (tp_touch_active(tp, t) && t->dirty) {
|
2014-02-06 19:17:22 +10:00
|
|
|
nchanged++;
|
|
|
|
|
tp_get_delta(t, &tmpx, &tmpy);
|
|
|
|
|
|
|
|
|
|
dx += tmpx;
|
|
|
|
|
dy += tmpy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nchanged == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dx /= nchanged;
|
|
|
|
|
dy /= nchanged;
|
|
|
|
|
|
|
|
|
|
tp_filter_motion(tp, &dx, &dy, time);
|
|
|
|
|
|
2014-05-23 16:06:26 +02:00
|
|
|
/* Require at least three px scrolling to start */
|
|
|
|
|
if (dy <= -3.0 || dy >= 3.0) {
|
|
|
|
|
tp->scroll.state = SCROLL_STATE_SCROLLING;
|
|
|
|
|
tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL);
|
|
|
|
|
}
|
|
|
|
|
if (dx <= -3.0 || dx >= 3.0) {
|
|
|
|
|
tp->scroll.state = SCROLL_STATE_SCROLLING;
|
|
|
|
|
tp->scroll.direction |= (1 << LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL);
|
2014-02-14 12:31:26 +10:00
|
|
|
}
|
|
|
|
|
|
2014-05-23 16:06:26 +02:00
|
|
|
if (tp->scroll.state == SCROLL_STATE_NONE)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-05-23 16:06:22 +02:00
|
|
|
/* Stop spurious MOTION events at the end of scrolling */
|
|
|
|
|
tp_for_each_touch(tp, t)
|
|
|
|
|
t->is_pointer = false;
|
|
|
|
|
|
2014-02-14 12:31:26 +10:00
|
|
|
if (dy != 0.0 &&
|
|
|
|
|
(tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL))) {
|
2014-02-06 19:17:22 +10:00
|
|
|
pointer_notify_axis(&tp->device->base,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL,
|
2014-06-02 23:09:27 +02:00
|
|
|
dy);
|
2014-02-14 12:31:26 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (dx != 0.0 &&
|
|
|
|
|
(tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL))) {
|
|
|
|
|
pointer_notify_axis(&tp->device->base,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL,
|
2014-06-02 23:09:27 +02:00
|
|
|
dx);
|
2014-02-14 12:31:26 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-23 16:06:23 +02:00
|
|
|
static void
|
|
|
|
|
tp_stop_scroll_events(struct tp_dispatch *tp, uint64_t time)
|
|
|
|
|
{
|
|
|
|
|
if (tp->scroll.state == SCROLL_STATE_NONE)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* terminate scrolling with a zero scroll event */
|
|
|
|
|
if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL))
|
|
|
|
|
pointer_notify_axis(&tp->device->base,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL,
|
|
|
|
|
0);
|
|
|
|
|
if (tp->scroll.direction & (1 << LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL))
|
|
|
|
|
pointer_notify_axis(&tp->device->base,
|
|
|
|
|
time,
|
|
|
|
|
LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL,
|
|
|
|
|
0);
|
|
|
|
|
|
|
|
|
|
tp->scroll.state = SCROLL_STATE_NONE;
|
|
|
|
|
tp->scroll.direction = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-14 12:31:26 +10:00
|
|
|
static int
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_post_scroll_events(struct tp_dispatch *tp, uint64_t time)
|
2014-02-14 12:31:26 +10:00
|
|
|
{
|
2014-04-08 08:34:20 +02:00
|
|
|
struct tp_touch *t;
|
|
|
|
|
int nfingers_down = 0;
|
|
|
|
|
|
|
|
|
|
/* Only count active touches for 2 finger scrolling */
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (tp_touch_active(tp, t))
|
|
|
|
|
nfingers_down++;
|
|
|
|
|
}
|
2014-02-17 14:24:20 +10:00
|
|
|
|
2014-04-08 08:34:20 +02:00
|
|
|
if (nfingers_down != 2) {
|
2014-05-23 16:06:23 +02:00
|
|
|
tp_stop_scroll_events(tp, time);
|
2014-05-23 16:06:24 +02:00
|
|
|
return 0;
|
2014-02-14 12:31:26 +10:00
|
|
|
}
|
2014-05-23 16:06:24 +02:00
|
|
|
|
|
|
|
|
tp_post_twofinger_scroll(tp, time);
|
|
|
|
|
return 1;
|
2014-02-06 19:17:22 +10:00
|
|
|
}
|
|
|
|
|
|
2014-02-06 15:32:32 +10:00
|
|
|
static void
|
2014-04-08 12:29:45 +02:00
|
|
|
tp_post_events(struct tp_dispatch *tp, uint64_t time)
|
2014-02-06 15:32:32 +10:00
|
|
|
{
|
|
|
|
|
struct tp_touch *t = tp_current_touch(tp);
|
|
|
|
|
double dx, dy;
|
2014-06-05 16:22:52 +10:00
|
|
|
int consumed = 0;
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-06-05 16:22:52 +10:00
|
|
|
consumed |= tp_tap_handle_state(tp, time);
|
|
|
|
|
consumed |= tp_post_button_events(tp, time);
|
2014-02-17 11:14:29 +10:00
|
|
|
|
2014-06-05 16:22:52 +10:00
|
|
|
if (consumed) {
|
2014-05-23 16:06:25 +02:00
|
|
|
tp_stop_scroll_events(tp, time);
|
2014-02-06 19:17:22 +10:00
|
|
|
return;
|
2014-05-23 16:06:25 +02:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-02-14 12:31:26 +10:00
|
|
|
if (tp_post_scroll_events(tp, time) != 0)
|
2014-02-06 19:43:48 +10:00
|
|
|
return;
|
|
|
|
|
|
2014-02-17 14:24:20 +10:00
|
|
|
if (t->history.count >= TOUCHPAD_MIN_SAMPLES) {
|
|
|
|
|
if (!t->is_pointer) {
|
|
|
|
|
tp_for_each_touch(tp, t) {
|
|
|
|
|
if (t->is_pointer)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!t->is_pointer)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-02-10 07:44:59 +10:00
|
|
|
tp_get_delta(t, &dx, &dy);
|
|
|
|
|
tp_filter_motion(tp, &dx, &dy, time);
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-06-02 23:09:27 +02:00
|
|
|
if (dx != 0.0 || dy != 0.0)
|
|
|
|
|
pointer_notify_motion(&tp->device->base, time, dx, dy);
|
2014-02-10 07:44:59 +10:00
|
|
|
}
|
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:
|
|
|
|
|
tp_process_state(tp, time);
|
|
|
|
|
tp_post_events(tp, time);
|
|
|
|
|
tp_post_process_state(tp, time);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tp_destroy(struct evdev_dispatch *dispatch)
|
|
|
|
|
{
|
|
|
|
|
struct tp_dispatch *tp =
|
|
|
|
|
(struct tp_dispatch*)dispatch;
|
|
|
|
|
|
2014-03-25 12:14:39 +10:00
|
|
|
tp_destroy_tap(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
|
|
|
tp_destroy_buttons(tp);
|
2014-03-25 12:14:39 +10:00
|
|
|
|
2014-05-26 23:20:42 +02:00
|
|
|
motion_filter_destroy(tp->filter);
|
2014-02-06 15:32:32 +10:00
|
|
|
free(tp->touches);
|
|
|
|
|
free(tp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct evdev_dispatch_interface tp_interface = {
|
|
|
|
|
tp_process,
|
|
|
|
|
tp_destroy
|
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)
|
|
|
|
|
{
|
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
|
|
|
size_t i;
|
2014-02-14 14:18:27 +10:00
|
|
|
const struct input_absinfo *absinfo;
|
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) {
|
|
|
|
|
tp->ntouches = absinfo->maximum + 1;
|
|
|
|
|
tp->slot = absinfo->value;
|
|
|
|
|
tp->has_mt = true;
|
|
|
|
|
} else {
|
2014-04-15 14:27:58 +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;
|
|
|
|
|
|
2014-02-14 14:18:27 +10:00
|
|
|
tp->slot = 0;
|
|
|
|
|
tp->has_mt = false;
|
2014-04-15 14:27:58 +02:00
|
|
|
tp->ntouches = 1;
|
|
|
|
|
|
|
|
|
|
ARRAY_FOR_EACH(max_touches, m) {
|
|
|
|
|
if (libevdev_has_event_code(device->evdev,
|
|
|
|
|
EV_KEY,
|
|
|
|
|
m->code)) {
|
|
|
|
|
tp->ntouches = m->ntouches;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-02-14 14:18:27 +10:00
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
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-05-24 16:53:45 +02:00
|
|
|
static void
|
|
|
|
|
calculate_scale_coefficients(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res_x <= 0 || res_y <= 0) {
|
|
|
|
|
tp->accel.x_scale_coeff = 1.0;
|
|
|
|
|
tp->accel.y_scale_coeff = 1.0;
|
|
|
|
|
} else if (res_x > res_y) {
|
|
|
|
|
tp->accel.x_scale_coeff = res_y / (double) res_x;
|
|
|
|
|
tp->accel.y_scale_coeff = 1.0f;
|
|
|
|
|
} else {
|
|
|
|
|
tp->accel.y_scale_coeff = res_x / (double) res_y;
|
|
|
|
|
tp->accel.x_scale_coeff = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-06 19:43:48 +10:00
|
|
|
static int
|
|
|
|
|
tp_init_accel(struct tp_dispatch *touchpad, double diagonal)
|
|
|
|
|
{
|
|
|
|
|
struct motion_filter *accel;
|
|
|
|
|
|
2014-05-24 16:53:45 +02:00
|
|
|
calculate_scale_coefficients(touchpad);
|
|
|
|
|
|
2014-02-06 19:43:48 +10:00
|
|
|
touchpad->accel.constant_factor =
|
|
|
|
|
DEFAULT_CONSTANT_ACCEL_NUMERATOR / diagonal;
|
|
|
|
|
touchpad->accel.min_factor = DEFAULT_MIN_ACCEL_FACTOR;
|
|
|
|
|
touchpad->accel.max_factor = DEFAULT_MAX_ACCEL_FACTOR;
|
|
|
|
|
|
|
|
|
|
accel = create_pointer_accelator_filter(tp_accel_profile);
|
|
|
|
|
if (accel == NULL)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
touchpad->filter = accel;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-02-06 15:32:32 +10:00
|
|
|
|
2014-02-14 12:31:26 +10:00
|
|
|
static int
|
|
|
|
|
tp_init_scroll(struct tp_dispatch *tp)
|
|
|
|
|
{
|
|
|
|
|
tp->scroll.direction = 0;
|
|
|
|
|
tp->scroll.state = SCROLL_STATE_NONE;
|
|
|
|
|
|
|
|
|
|
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-14 12:31:26 +10:00
|
|
|
if (tp_init_scroll(tp) != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
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-02-06 15:05:36 +10:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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-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-02-06 15:32:32 +10:00
|
|
|
return &tp->base;
|
2014-02-06 15:05:36 +10:00
|
|
|
}
|