|
|
|
/*
|
|
|
|
* Copyright © 2012 Jonas Ådahl
|
|
|
|
*
|
|
|
|
* 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 <stdlib.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <linux/input.h>
|
|
|
|
|
|
|
|
#include "filter.h"
|
|
|
|
#include "evdev.h"
|
|
|
|
|
|
|
|
/* Default values */
|
|
|
|
#define DEFAULT_CONSTANT_ACCEL_NUMERATOR 50
|
|
|
|
#define DEFAULT_MIN_ACCEL_FACTOR 0.16
|
|
|
|
#define DEFAULT_MAX_ACCEL_FACTOR 1.0
|
|
|
|
#define DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR 700.0
|
|
|
|
|
|
|
|
#define DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON BTN_LEFT
|
|
|
|
#define DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT 100
|
|
|
|
|
|
|
|
enum touchpad_model {
|
|
|
|
TOUCHPAD_MODEL_UNKNOWN = 0,
|
|
|
|
TOUCHPAD_MODEL_SYNAPTICS,
|
|
|
|
TOUCHPAD_MODEL_ALPS,
|
|
|
|
TOUCHPAD_MODEL_APPLETOUCH,
|
|
|
|
TOUCHPAD_MODEL_ELANTECH
|
|
|
|
};
|
|
|
|
|
|
|
|
enum touchpad_event {
|
|
|
|
TOUCHPAD_EVENT_NONE = 0,
|
|
|
|
TOUCHPAD_EVENT_ABSOLUTE_ANY = (1 << 0),
|
|
|
|
TOUCHPAD_EVENT_ABSOLUTE_X = (1 << 1),
|
|
|
|
TOUCHPAD_EVENT_ABSOLUTE_Y = (1 << 2),
|
|
|
|
TOUCHPAD_EVENT_REPORT = (1 << 3)
|
|
|
|
};
|
|
|
|
|
|
|
|
struct touchpad_model_spec {
|
|
|
|
short vendor;
|
|
|
|
short product;
|
|
|
|
enum touchpad_model model;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct touchpad_model_spec touchpad_spec_table[] = {
|
|
|
|
{0x0002, 0x0007, TOUCHPAD_MODEL_SYNAPTICS},
|
|
|
|
{0x0002, 0x0008, TOUCHPAD_MODEL_ALPS},
|
|
|
|
{0x05ac, 0x0000, TOUCHPAD_MODEL_APPLETOUCH},
|
|
|
|
{0x0002, 0x000e, TOUCHPAD_MODEL_ELANTECH},
|
|
|
|
{0x0000, 0x0000, TOUCHPAD_MODEL_UNKNOWN}
|
|
|
|
};
|
|
|
|
|
|
|
|
enum touchpad_state {
|
|
|
|
TOUCHPAD_STATE_NONE = 0,
|
|
|
|
TOUCHPAD_STATE_TOUCH = (1 << 0),
|
|
|
|
TOUCHPAD_STATE_MOVE = (1 << 1)
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TOUCHPAD_HISTORY_LENGTH 4
|
|
|
|
|
|
|
|
struct touchpad_motion {
|
|
|
|
int32_t x;
|
|
|
|
int32_t y;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum touchpad_fingers_state {
|
|
|
|
TOUCHPAD_FINGERS_ONE = (1 << 0),
|
|
|
|
TOUCHPAD_FINGERS_TWO = (1 << 1),
|
|
|
|
TOUCHPAD_FINGERS_THREE = (1 << 2)
|
|
|
|
};
|
|
|
|
|
|
|
|
enum fsm_event {
|
|
|
|
FSM_EVENT_TOUCH,
|
|
|
|
FSM_EVENT_RELEASE,
|
|
|
|
FSM_EVENT_MOTION,
|
|
|
|
FSM_EVENT_TIMEOUT
|
|
|
|
};
|
|
|
|
|
|
|
|
enum fsm_state {
|
|
|
|
FSM_IDLE,
|
|
|
|
FSM_TOUCH,
|
|
|
|
FSM_TAP,
|
|
|
|
FSM_TAP_2,
|
|
|
|
FSM_DRAG
|
|
|
|
};
|
|
|
|
|
|
|
|
struct touchpad_dispatch {
|
|
|
|
struct evdev_dispatch base;
|
|
|
|
struct evdev_device *device;
|
|
|
|
|
|
|
|
enum touchpad_model model;
|
|
|
|
unsigned int state;
|
|
|
|
int finger_state;
|
|
|
|
int last_finger_state;
|
|
|
|
|
|
|
|
double constant_accel_factor;
|
|
|
|
double min_accel_factor;
|
|
|
|
double max_accel_factor;
|
|
|
|
|
|
|
|
unsigned int event_mask;
|
|
|
|
unsigned int event_mask_filter;
|
|
|
|
|
|
|
|
int reset;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
bool enable;
|
|
|
|
|
|
|
|
struct wl_array events;
|
|
|
|
enum fsm_state state;
|
|
|
|
struct wl_event_source *timer_source;
|
|
|
|
} fsm;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int32_t x;
|
|
|
|
int32_t y;
|
|
|
|
} hw_abs;
|
|
|
|
|
|
|
|
int has_pressure;
|
|
|
|
struct {
|
|
|
|
int32_t touch_low;
|
|
|
|
int32_t touch_high;
|
|
|
|
} pressure;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
int32_t margin_x;
|
|
|
|
int32_t margin_y;
|
|
|
|
int32_t center_x;
|
|
|
|
int32_t center_y;
|
|
|
|
} hysteresis;
|
|
|
|
|
|
|
|
struct touchpad_motion motion_history[TOUCHPAD_HISTORY_LENGTH];
|
|
|
|
int motion_index;
|
|
|
|
unsigned int motion_count;
|
|
|
|
|
|
|
|
struct weston_motion_filter *filter;
|
|
|
|
};
|
|
|
|
|
|
|
|
static enum touchpad_model
|
|
|
|
get_touchpad_model(struct evdev_device *device)
|
|
|
|
{
|
|
|
|
struct input_id id;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
if (ioctl(device->fd, EVIOCGID, &id) < 0)
|
|
|
|
return TOUCHPAD_MODEL_UNKNOWN;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_LENGTH(touchpad_spec_table); i++)
|
|
|
|
if (touchpad_spec_table[i].vendor == id.vendor &&
|
|
|
|
(!touchpad_spec_table[i].product ||
|
|
|
|
touchpad_spec_table[i].product == id.product))
|
|
|
|
return touchpad_spec_table[i].model;
|
|
|
|
|
|
|
|
return TOUCHPAD_MODEL_UNKNOWN;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
configure_touchpad_pressure(struct touchpad_dispatch *touchpad,
|
|
|
|
int32_t pressure_min, int32_t pressure_max)
|
|
|
|
{
|
|
|
|
int32_t range = pressure_max - pressure_min + 1;
|
|
|
|
|
|
|
|
touchpad->has_pressure = 1;
|
|
|
|
|
|
|
|
/* Magic numbers from xf86-input-synaptics */
|
|
|
|
switch (touchpad->model) {
|
|
|
|
case TOUCHPAD_MODEL_ELANTECH:
|
|
|
|
touchpad->pressure.touch_low = pressure_min + 1;
|
|
|
|
touchpad->pressure.touch_high = pressure_min + 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
touchpad->pressure.touch_low =
|
|
|
|
pressure_min + range * (25.0/256.0);
|
|
|
|
touchpad->pressure.touch_high =
|
|
|
|
pressure_min + range * (30.0/256.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static double
|
|
|
|
touchpad_profile(struct weston_motion_filter *filter,
|
|
|
|
void *data,
|
|
|
|
double velocity,
|
|
|
|
uint32_t time)
|
|
|
|
{
|
|
|
|
struct touchpad_dispatch *touchpad =
|
|
|
|
(struct touchpad_dispatch *) data;
|
|
|
|
|
|
|
|
double accel_factor;
|
|
|
|
|
|
|
|
accel_factor = velocity * touchpad->constant_accel_factor;
|
|
|
|
|
|
|
|
if (accel_factor > touchpad->max_accel_factor)
|
|
|
|
accel_factor = touchpad->max_accel_factor;
|
|
|
|
else if (accel_factor < touchpad->min_accel_factor)
|
|
|
|
accel_factor = touchpad->min_accel_factor;
|
|
|
|
|
|
|
|
return accel_factor;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline struct touchpad_motion *
|
|
|
|
motion_history_offset(struct touchpad_dispatch *touchpad, int offset)
|
|
|
|
{
|
|
|
|
int offset_index =
|
|
|
|
(touchpad->motion_index - offset + TOUCHPAD_HISTORY_LENGTH) %
|
|
|
|
TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
|
|
|
|
return &touchpad->motion_history[offset_index];
|
|
|
|
}
|
|
|
|
|
|
|
|
static double
|
|
|
|
estimate_delta(int x0, int x1, int x2, int x3)
|
|
|
|
{
|
|
|
|
return (x0 + x1 - x2 - x3) / 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
touchpad_get_delta(struct touchpad_dispatch *touchpad, double *dx, double *dy)
|
|
|
|
{
|
|
|
|
*dx = estimate_delta(motion_history_offset(touchpad, 0)->x,
|
|
|
|
motion_history_offset(touchpad, 1)->x,
|
|
|
|
motion_history_offset(touchpad, 2)->x,
|
|
|
|
motion_history_offset(touchpad, 3)->x);
|
|
|
|
*dy = estimate_delta(motion_history_offset(touchpad, 0)->y,
|
|
|
|
motion_history_offset(touchpad, 1)->y,
|
|
|
|
motion_history_offset(touchpad, 2)->y,
|
|
|
|
motion_history_offset(touchpad, 3)->y);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
filter_motion(struct touchpad_dispatch *touchpad,
|
|
|
|
double *dx, double *dy, uint32_t time)
|
|
|
|
{
|
|
|
|
struct weston_motion_params motion;
|
|
|
|
|
|
|
|
motion.dx = *dx;
|
|
|
|
motion.dy = *dy;
|
|
|
|
|
|
|
|
weston_filter_dispatch(touchpad->filter, &motion, touchpad, time);
|
|
|
|
|
|
|
|
*dx = motion.dx;
|
|
|
|
*dy = motion.dy;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_button_pressed(struct touchpad_dispatch *touchpad, uint32_t time)
|
|
|
|
{
|
|
|
|
notify_button(touchpad->device->seat, time,
|
|
|
|
DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
|
|
|
|
WL_POINTER_BUTTON_STATE_PRESSED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
|
|
|
|
{
|
|
|
|
notify_button(touchpad->device->seat, time,
|
|
|
|
DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
|
|
|
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
notify_tap(struct touchpad_dispatch *touchpad, uint32_t time)
|
|
|
|
{
|
|
|
|
notify_button_pressed(touchpad, time);
|
|
|
|
notify_button_released(touchpad, time);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
process_fsm_events(struct touchpad_dispatch *touchpad, uint32_t time)
|
|
|
|
{
|
|
|
|
uint32_t timeout = UINT32_MAX;
|
|
|
|
enum fsm_event *pevent;
|
|
|
|
enum fsm_event event;
|
|
|
|
|
|
|
|
if (!touchpad->fsm.enable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (touchpad->fsm.events.size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
wl_array_for_each(pevent, &touchpad->fsm.events) {
|
|
|
|
event = *pevent;
|
|
|
|
timeout = 0;
|
|
|
|
|
|
|
|
switch (touchpad->fsm.state) {
|
|
|
|
case FSM_IDLE:
|
|
|
|
switch (event) {
|
|
|
|
case FSM_EVENT_TOUCH:
|
|
|
|
touchpad->fsm.state = FSM_TOUCH;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FSM_TOUCH:
|
|
|
|
switch (event) {
|
|
|
|
case FSM_EVENT_RELEASE:
|
|
|
|
timeout = DEFAULT_TOUCHPAD_SINGLE_TAP_TIMEOUT;
|
|
|
|
touchpad->fsm.state = FSM_TAP;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FSM_TAP:
|
|
|
|
switch (event) {
|
|
|
|
case FSM_EVENT_TIMEOUT:
|
|
|
|
notify_tap(touchpad, time);
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
case FSM_EVENT_TOUCH:
|
|
|
|
notify_button_pressed(touchpad, time);
|
|
|
|
touchpad->fsm.state = FSM_TAP_2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FSM_TAP_2:
|
|
|
|
switch (event) {
|
|
|
|
case FSM_EVENT_MOTION:
|
|
|
|
touchpad->fsm.state = FSM_DRAG;
|
|
|
|
break;
|
|
|
|
case FSM_EVENT_RELEASE:
|
|
|
|
notify_button_released(touchpad, time);
|
|
|
|
notify_tap(touchpad, time);
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case FSM_DRAG:
|
|
|
|
switch (event) {
|
|
|
|
case FSM_EVENT_RELEASE:
|
|
|
|
notify_button_released(touchpad, time);
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
weston_log("evdev-touchpad: Unknown state %d",
|
|
|
|
touchpad->fsm.state);
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timeout != UINT32_MAX)
|
|
|
|
wl_event_source_timer_update(touchpad->fsm.timer_source,
|
|
|
|
timeout);
|
|
|
|
|
|
|
|
wl_array_release(&touchpad->fsm.events);
|
|
|
|
wl_array_init(&touchpad->fsm.events);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
push_fsm_event(struct touchpad_dispatch *touchpad,
|
|
|
|
enum fsm_event event)
|
|
|
|
{
|
|
|
|
enum fsm_event *pevent;
|
|
|
|
|
|
|
|
if (!touchpad->fsm.enable)
|
|
|
|
return;
|
|
|
|
|
|
|
|
pevent = wl_array_add(&touchpad->fsm.events, sizeof event);
|
|
|
|
if (pevent)
|
|
|
|
*pevent = event;
|
|
|
|
else
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fsm_timout_handler(void *data)
|
|
|
|
{
|
|
|
|
struct touchpad_dispatch *touchpad = data;
|
|
|
|
|
|
|
|
if (touchpad->fsm.events.size == 0) {
|
|
|
|
push_fsm_event(touchpad, FSM_EVENT_TIMEOUT);
|
|
|
|
process_fsm_events(touchpad, weston_compositor_get_time());
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
touchpad_update_state(struct touchpad_dispatch *touchpad, uint32_t time)
|
|
|
|
{
|
|
|
|
int motion_index;
|
|
|
|
int center_x, center_y;
|
|
|
|
double dx = 0.0, dy = 0.0;
|
|
|
|
|
|
|
|
if (touchpad->reset ||
|
|
|
|
touchpad->last_finger_state != touchpad->finger_state) {
|
|
|
|
touchpad->reset = 0;
|
|
|
|
touchpad->motion_count = 0;
|
|
|
|
touchpad->event_mask = TOUCHPAD_EVENT_NONE;
|
|
|
|
touchpad->event_mask_filter =
|
|
|
|
TOUCHPAD_EVENT_ABSOLUTE_X | TOUCHPAD_EVENT_ABSOLUTE_Y;
|
|
|
|
|
|
|
|
touchpad->last_finger_state = touchpad->finger_state;
|
|
|
|
|
|
|
|
process_fsm_events(touchpad, time);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
touchpad->last_finger_state = touchpad->finger_state;
|
|
|
|
|
|
|
|
if (!(touchpad->event_mask & TOUCHPAD_EVENT_REPORT))
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
touchpad->event_mask &= ~TOUCHPAD_EVENT_REPORT;
|
|
|
|
|
|
|
|
if ((touchpad->event_mask & touchpad->event_mask_filter) !=
|
|
|
|
touchpad->event_mask_filter)
|
|
|
|
return;
|
|
|
|
|
|
|
|
touchpad->event_mask_filter = TOUCHPAD_EVENT_ABSOLUTE_ANY;
|
|
|
|
touchpad->event_mask = 0;
|
|
|
|
|
|
|
|
/* Avoid noice by moving center only when delta reaches a threshold
|
|
|
|
* distance from the old center. */
|
|
|
|
if (touchpad->motion_count > 0) {
|
|
|
|
center_x = hysteresis(touchpad->hw_abs.x,
|
|
|
|
touchpad->hysteresis.center_x,
|
|
|
|
touchpad->hysteresis.margin_x);
|
|
|
|
center_y = hysteresis(touchpad->hw_abs.y,
|
|
|
|
touchpad->hysteresis.center_y,
|
|
|
|
touchpad->hysteresis.margin_y);
|
|
|
|
} else {
|
|
|
|
center_x = touchpad->hw_abs.x;
|
|
|
|
center_y = touchpad->hw_abs.y;
|
|
|
|
}
|
|
|
|
touchpad->hysteresis.center_x = center_x;
|
|
|
|
touchpad->hysteresis.center_y = center_y;
|
|
|
|
touchpad->hw_abs.x = center_x;
|
|
|
|
touchpad->hw_abs.y = center_y;
|
|
|
|
|
|
|
|
/* Update motion history tracker */
|
|
|
|
motion_index = (touchpad->motion_index + 1) % TOUCHPAD_HISTORY_LENGTH;
|
|
|
|
touchpad->motion_index = motion_index;
|
|
|
|
touchpad->motion_history[motion_index].x = touchpad->hw_abs.x;
|
|
|
|
touchpad->motion_history[motion_index].y = touchpad->hw_abs.y;
|
|
|
|
if (touchpad->motion_count < 4)
|
|
|
|
touchpad->motion_count++;
|
|
|
|
|
|
|
|
if (touchpad->motion_count >= 4) {
|
|
|
|
touchpad_get_delta(touchpad, &dx, &dy);
|
|
|
|
|
|
|
|
filter_motion(touchpad, &dx, &dy, time);
|
|
|
|
|
|
|
|
if (touchpad->finger_state == TOUCHPAD_FINGERS_ONE) {
|
|
|
|
touchpad->device->rel.dx = wl_fixed_from_double(dx);
|
|
|
|
touchpad->device->rel.dy = wl_fixed_from_double(dy);
|
|
|
|
touchpad->device->pending_events |=
|
|
|
|
EVDEV_RELATIVE_MOTION | EVDEV_SYN;
|
|
|
|
} else if (touchpad->finger_state == TOUCHPAD_FINGERS_TWO) {
|
|
|
|
if (dx != 0.0)
|
|
|
|
notify_axis(touchpad->device->seat,
|
|
|
|
time,
|
|
|
|
WL_POINTER_AXIS_HORIZONTAL_SCROLL,
|
|
|
|
wl_fixed_from_double(dx));
|
|
|
|
if (dy != 0.0)
|
|
|
|
notify_axis(touchpad->device->seat,
|
|
|
|
time,
|
|
|
|
WL_POINTER_AXIS_VERTICAL_SCROLL,
|
|
|
|
wl_fixed_from_double(dy));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(touchpad->state & TOUCHPAD_STATE_MOVE) &&
|
|
|
|
((int)dx || (int)dy)) {
|
|
|
|
touchpad->state |= TOUCHPAD_STATE_MOVE;
|
|
|
|
push_fsm_event(touchpad, FSM_EVENT_MOTION);
|
|
|
|
}
|
|
|
|
|
|
|
|
process_fsm_events(touchpad, time);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_touch(struct touchpad_dispatch *touchpad)
|
|
|
|
{
|
|
|
|
touchpad->state |= TOUCHPAD_STATE_TOUCH;
|
|
|
|
|
|
|
|
push_fsm_event(touchpad, FSM_EVENT_TOUCH);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
on_release(struct touchpad_dispatch *touchpad)
|
|
|
|
{
|
|
|
|
|
|
|
|
touchpad->reset = 1;
|
|
|
|
touchpad->state &= ~(TOUCHPAD_STATE_MOVE | TOUCHPAD_STATE_TOUCH);
|
|
|
|
|
|
|
|
push_fsm_event(touchpad, FSM_EVENT_RELEASE);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
process_absolute(struct touchpad_dispatch *touchpad,
|
|
|
|
struct evdev_device *device,
|
|
|
|
struct input_event *e)
|
|
|
|
{
|
|
|
|
switch (e->code) {
|
|
|
|
case ABS_PRESSURE:
|
|
|
|
if (e->value > touchpad->pressure.touch_high &&
|
|
|
|
!(touchpad->state & TOUCHPAD_STATE_TOUCH))
|
|
|
|
on_touch(touchpad);
|
|
|
|
else if (e->value < touchpad->pressure.touch_low &&
|
|
|
|
touchpad->state & TOUCHPAD_STATE_TOUCH)
|
|
|
|
on_release(touchpad);
|
|
|
|
|
|
|
|
break;
|
|
|
|
case ABS_X:
|
|
|
|
if (touchpad->state & TOUCHPAD_STATE_TOUCH) {
|
|
|
|
touchpad->hw_abs.x = e->value;
|
|
|
|
touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
|
|
|
|
touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_X;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ABS_Y:
|
|
|
|
if (touchpad->state & TOUCHPAD_STATE_TOUCH) {
|
|
|
|
touchpad->hw_abs.y = e->value;
|
|
|
|
touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_ANY;
|
|
|
|
touchpad->event_mask |= TOUCHPAD_EVENT_ABSOLUTE_Y;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
process_key(struct touchpad_dispatch *touchpad,
|
|
|
|
struct evdev_device *device,
|
|
|
|
struct input_event *e,
|
|
|
|
uint32_t time)
|
|
|
|
{
|
|
|
|
switch (e->code) {
|
|
|
|
case BTN_TOUCH:
|
|
|
|
if (!touchpad->has_pressure) {
|
|
|
|
if (e->value && !(touchpad->state & TOUCHPAD_STATE_TOUCH))
|
|
|
|
on_touch(touchpad);
|
|
|
|
else if (!e->value)
|
|
|
|
on_release(touchpad);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTN_LEFT:
|
|
|
|
case BTN_RIGHT:
|
|
|
|
case BTN_MIDDLE:
|
|
|
|
case BTN_SIDE:
|
|
|
|
case BTN_EXTRA:
|
|
|
|
case BTN_FORWARD:
|
|
|
|
case BTN_BACK:
|
|
|
|
case BTN_TASK:
|
|
|
|
notify_button(device->seat,
|
|
|
|
time, e->code,
|
|
|
|
e->value ? WL_POINTER_BUTTON_STATE_PRESSED :
|
|
|
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
|
|
|
break;
|
|
|
|
case BTN_TOOL_PEN:
|
|
|
|
case BTN_TOOL_RUBBER:
|
|
|
|
case BTN_TOOL_BRUSH:
|
|
|
|
case BTN_TOOL_PENCIL:
|
|
|
|
case BTN_TOOL_AIRBRUSH:
|
|
|
|
case BTN_TOOL_MOUSE:
|
|
|
|
case BTN_TOOL_LENS:
|
|
|
|
touchpad->reset = 1;
|
|
|
|
break;
|
|
|
|
case BTN_TOOL_FINGER:
|
|
|
|
if (e->value)
|
|
|
|
touchpad->finger_state |= TOUCHPAD_FINGERS_ONE;
|
|
|
|
else
|
|
|
|
touchpad->finger_state &= ~TOUCHPAD_FINGERS_ONE;
|
|
|
|
break;
|
|
|
|
case BTN_TOOL_DOUBLETAP:
|
|
|
|
if (e->value)
|
|
|
|
touchpad->finger_state |= TOUCHPAD_FINGERS_TWO;
|
|
|
|
else
|
|
|
|
touchpad->finger_state &= ~TOUCHPAD_FINGERS_TWO;
|
|
|
|
break;
|
|
|
|
case BTN_TOOL_TRIPLETAP:
|
|
|
|
if (e->value)
|
|
|
|
touchpad->finger_state |= TOUCHPAD_FINGERS_THREE;
|
|
|
|
else
|
|
|
|
touchpad->finger_state &= ~TOUCHPAD_FINGERS_THREE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
touchpad_process(struct evdev_dispatch *dispatch,
|
|
|
|
struct evdev_device *device,
|
|
|
|
struct input_event *e,
|
|
|
|
uint32_t time)
|
|
|
|
{
|
|
|
|
struct touchpad_dispatch *touchpad =
|
|
|
|
(struct touchpad_dispatch *) dispatch;
|
|
|
|
|
|
|
|
switch (e->type) {
|
|
|
|
case EV_SYN:
|
|
|
|
if (e->code == SYN_REPORT)
|
|
|
|
touchpad->event_mask |= TOUCHPAD_EVENT_REPORT;
|
|
|
|
break;
|
|
|
|
case EV_ABS:
|
|
|
|
process_absolute(touchpad, device, e);
|
|
|
|
break;
|
|
|
|
case EV_KEY:
|
|
|
|
process_key(touchpad, device, e, time);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
touchpad_update_state(touchpad, time);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
touchpad_destroy(struct evdev_dispatch *dispatch)
|
|
|
|
{
|
|
|
|
struct touchpad_dispatch *touchpad =
|
|
|
|
(struct touchpad_dispatch *) dispatch;
|
|
|
|
|
|
|
|
touchpad->filter->interface->destroy(touchpad->filter);
|
|
|
|
wl_event_source_remove(touchpad->fsm.timer_source);
|
|
|
|
free(dispatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct evdev_dispatch_interface touchpad_interface = {
|
|
|
|
touchpad_process,
|
|
|
|
touchpad_destroy
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
|
|
|
touchpad_init(struct touchpad_dispatch *touchpad,
|
|
|
|
struct evdev_device *device)
|
|
|
|
{
|
|
|
|
struct weston_motion_filter *accel;
|
|
|
|
struct wl_event_loop *loop;
|
|
|
|
|
|
|
|
unsigned long prop_bits[INPUT_PROP_MAX];
|
|
|
|
struct input_absinfo absinfo;
|
|
|
|
unsigned long abs_bits[NBITS(ABS_MAX)];
|
|
|
|
|
|
|
|
bool has_buttonpad;
|
|
|
|
|
|
|
|
double width;
|
|
|
|
double height;
|
|
|
|
double diagonal;
|
|
|
|
|
|
|
|
touchpad->base.interface = &touchpad_interface;
|
|
|
|
touchpad->device = device;
|
|
|
|
|
|
|
|
/* Detect model */
|
|
|
|
touchpad->model = get_touchpad_model(device);
|
|
|
|
|
|
|
|
ioctl(device->fd, EVIOCGPROP(sizeof(prop_bits)), prop_bits);
|
|
|
|
has_buttonpad = TEST_BIT(prop_bits, INPUT_PROP_BUTTONPAD);
|
|
|
|
|
|
|
|
/* Configure pressure */
|
|
|
|
ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), abs_bits);
|
|
|
|
if (TEST_BIT(abs_bits, ABS_PRESSURE)) {
|
|
|
|
ioctl(device->fd, EVIOCGABS(ABS_PRESSURE), &absinfo);
|
|
|
|
configure_touchpad_pressure(touchpad,
|
|
|
|
absinfo.minimum,
|
|
|
|
absinfo.maximum);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure acceleration factor */
|
|
|
|
width = abs(device->abs.max_x - device->abs.min_x);
|
|
|
|
height = abs(device->abs.max_y - device->abs.min_y);
|
|
|
|
diagonal = sqrt(width*width + height*height);
|
|
|
|
|
|
|
|
touchpad->constant_accel_factor =
|
|
|
|
DEFAULT_CONSTANT_ACCEL_NUMERATOR / diagonal;
|
|
|
|
|
|
|
|
touchpad->min_accel_factor = DEFAULT_MIN_ACCEL_FACTOR;
|
|
|
|
touchpad->max_accel_factor = DEFAULT_MAX_ACCEL_FACTOR;
|
|
|
|
|
|
|
|
touchpad->hysteresis.margin_x =
|
|
|
|
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
|
|
|
|
touchpad->hysteresis.margin_y =
|
|
|
|
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
|
|
|
|
touchpad->hysteresis.center_x = 0;
|
|
|
|
touchpad->hysteresis.center_y = 0;
|
|
|
|
|
|
|
|
/* Configure acceleration profile */
|
|
|
|
accel = create_pointer_accelator_filter(touchpad_profile);
|
|
|
|
if (accel == NULL)
|
|
|
|
return -1;
|
|
|
|
touchpad->filter = accel;
|
|
|
|
|
|
|
|
/* Setup initial state */
|
|
|
|
touchpad->reset = 1;
|
|
|
|
|
|
|
|
memset(touchpad->motion_history, 0, sizeof touchpad->motion_history);
|
|
|
|
touchpad->motion_index = 0;
|
|
|
|
touchpad->motion_count = 0;
|
|
|
|
|
|
|
|
touchpad->state = TOUCHPAD_STATE_NONE;
|
|
|
|
touchpad->last_finger_state = 0;
|
|
|
|
touchpad->finger_state = 0;
|
|
|
|
|
|
|
|
wl_array_init(&touchpad->fsm.events);
|
|
|
|
touchpad->fsm.state = FSM_IDLE;
|
|
|
|
|
|
|
|
loop = wl_display_get_event_loop(device->seat->compositor->wl_display);
|
|
|
|
touchpad->fsm.timer_source =
|
|
|
|
wl_event_loop_add_timer(loop, fsm_timout_handler, touchpad);
|
|
|
|
if (touchpad->fsm.timer_source == NULL) {
|
|
|
|
accel->interface->destroy(accel);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure */
|
|
|
|
touchpad->fsm.enable = !has_buttonpad;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct evdev_dispatch *
|
|
|
|
evdev_touchpad_create(struct evdev_device *device)
|
|
|
|
{
|
|
|
|
struct touchpad_dispatch *touchpad;
|
|
|
|
|
|
|
|
touchpad = malloc(sizeof *touchpad);
|
|
|
|
if (touchpad == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (touchpad_init(touchpad, device) != 0) {
|
|
|
|
free(touchpad);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return &touchpad->base;
|
|
|
|
}
|