Reviewed-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>dev
parent
a4bac9e0e0
commit
823ad33ef3
@ -1,800 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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 "config.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <math.h> |
|
||||||
#include <string.h> |
|
||||||
#include <stdbool.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <linux/input.h> |
|
||||||
|
|
||||||
#include "filter.h" |
|
||||||
#include "evdev.h" |
|
||||||
#include "../shared/config-parser.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) { |
|
||||||
notify_motion(touchpad->device->seat, time, |
|
||||||
wl_fixed_from_double(dx), |
|
||||||
wl_fixed_from_double(dy)); |
|
||||||
} 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) |
|
||||||
{ |
|
||||||
uint32_t code; |
|
||||||
|
|
||||||
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: |
|
||||||
if (!touchpad->fsm.enable && e->code == BTN_LEFT && |
|
||||||
touchpad->finger_state == TOUCHPAD_FINGERS_TWO) |
|
||||||
code = BTN_RIGHT; |
|
||||||
else |
|
||||||
code = e->code; |
|
||||||
notify_button(device->seat, time, 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 void |
|
||||||
touchpad_parse_config(struct touchpad_dispatch *touchpad, double diagonal) |
|
||||||
{ |
|
||||||
struct weston_compositor *compositor = |
|
||||||
touchpad->device->seat->compositor; |
|
||||||
struct weston_config_section *s; |
|
||||||
double constant_accel_factor; |
|
||||||
double min_accel_factor; |
|
||||||
double max_accel_factor; |
|
||||||
|
|
||||||
s = weston_config_get_section(compositor->config, |
|
||||||
"touchpad", NULL, NULL); |
|
||||||
weston_config_section_get_double(s, "constant_accel_factor", |
|
||||||
&constant_accel_factor, |
|
||||||
DEFAULT_CONSTANT_ACCEL_NUMERATOR); |
|
||||||
weston_config_section_get_double(s, "min_accel_factor", |
|
||||||
&min_accel_factor, |
|
||||||
DEFAULT_MIN_ACCEL_FACTOR); |
|
||||||
weston_config_section_get_double(s, "max_accel_factor", |
|
||||||
&max_accel_factor, |
|
||||||
DEFAULT_MAX_ACCEL_FACTOR); |
|
||||||
|
|
||||||
touchpad->constant_accel_factor = |
|
||||||
constant_accel_factor / diagonal; |
|
||||||
touchpad->min_accel_factor = min_accel_factor; |
|
||||||
touchpad->max_accel_factor = max_accel_factor; |
|
||||||
} |
|
||||||
|
|
||||||
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_parse_config(touchpad, diagonal); |
|
||||||
|
|
||||||
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; |
|
||||||
} |
|
@ -1,755 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2010 Intel Corporation |
|
||||||
* |
|
||||||
* 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" |
|
||||||
|
|
||||||
#include <errno.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <string.h> |
|
||||||
#include <linux/input.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <fcntl.h> |
|
||||||
#include <mtdev.h> |
|
||||||
#include <assert.h> |
|
||||||
|
|
||||||
#include "compositor.h" |
|
||||||
#include "evdev.h" |
|
||||||
|
|
||||||
#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int(10) |
|
||||||
|
|
||||||
void |
|
||||||
evdev_led_update(struct evdev_device *device, enum weston_led leds) |
|
||||||
{ |
|
||||||
static const struct { |
|
||||||
enum weston_led weston; |
|
||||||
int evdev; |
|
||||||
} map[] = { |
|
||||||
{ LED_NUM_LOCK, LED_NUML }, |
|
||||||
{ LED_CAPS_LOCK, LED_CAPSL }, |
|
||||||
{ LED_SCROLL_LOCK, LED_SCROLLL }, |
|
||||||
}; |
|
||||||
struct input_event ev[ARRAY_LENGTH(map) + 1]; |
|
||||||
unsigned int i; |
|
||||||
|
|
||||||
if (!(device->seat_caps & EVDEV_SEAT_KEYBOARD)) |
|
||||||
return; |
|
||||||
|
|
||||||
memset(ev, 0, sizeof(ev)); |
|
||||||
for (i = 0; i < ARRAY_LENGTH(map); i++) { |
|
||||||
ev[i].type = EV_LED; |
|
||||||
ev[i].code = map[i].evdev; |
|
||||||
ev[i].value = !!(leds & map[i].weston); |
|
||||||
} |
|
||||||
ev[i].type = EV_SYN; |
|
||||||
ev[i].code = SYN_REPORT; |
|
||||||
|
|
||||||
i = write(device->fd, ev, sizeof ev); |
|
||||||
(void)i; /* no, we really don't care about the return value */ |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
transform_absolute(struct evdev_device *device, int32_t *x, int32_t *y) |
|
||||||
{ |
|
||||||
if (!device->abs.apply_calibration) { |
|
||||||
*x = device->abs.x; |
|
||||||
*y = device->abs.y; |
|
||||||
return; |
|
||||||
} else { |
|
||||||
*x = device->abs.x * device->abs.calibration[0] + |
|
||||||
device->abs.y * device->abs.calibration[1] + |
|
||||||
device->abs.calibration[2]; |
|
||||||
|
|
||||||
*y = device->abs.x * device->abs.calibration[3] + |
|
||||||
device->abs.y * device->abs.calibration[4] + |
|
||||||
device->abs.calibration[5]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
evdev_flush_pending_event(struct evdev_device *device, uint32_t time) |
|
||||||
{ |
|
||||||
struct weston_seat *master = device->seat; |
|
||||||
wl_fixed_t x, y; |
|
||||||
int32_t cx, cy; |
|
||||||
int slot, seat_slot; |
|
||||||
|
|
||||||
slot = device->mt.slot; |
|
||||||
switch (device->pending_event) { |
|
||||||
case EVDEV_NONE: |
|
||||||
return; |
|
||||||
case EVDEV_RELATIVE_MOTION: |
|
||||||
notify_motion(master, time, device->rel.dx, device->rel.dy); |
|
||||||
device->rel.dx = 0; |
|
||||||
device->rel.dy = 0; |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_MT_DOWN: |
|
||||||
if (device->output == NULL) |
|
||||||
break; |
|
||||||
weston_output_transform_coordinate(device->output, |
|
||||||
wl_fixed_from_int(device->mt.slots[slot].x), |
|
||||||
wl_fixed_from_int(device->mt.slots[slot].y), |
|
||||||
&x, &y); |
|
||||||
seat_slot = ffs(~master->slot_map) - 1; |
|
||||||
device->mt.slots[slot].seat_slot = seat_slot; |
|
||||||
master->slot_map |= 1 << seat_slot; |
|
||||||
|
|
||||||
notify_touch(master, time, seat_slot, x, y, WL_TOUCH_DOWN); |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_MT_MOTION: |
|
||||||
if (device->output == NULL) |
|
||||||
break; |
|
||||||
weston_output_transform_coordinate(device->output, |
|
||||||
wl_fixed_from_int(device->mt.slots[slot].x), |
|
||||||
wl_fixed_from_int(device->mt.slots[slot].y), |
|
||||||
&x, &y); |
|
||||||
seat_slot = device->mt.slots[slot].seat_slot; |
|
||||||
notify_touch(master, time, seat_slot, x, y, WL_TOUCH_MOTION); |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_MT_UP: |
|
||||||
seat_slot = device->mt.slots[slot].seat_slot; |
|
||||||
master->slot_map &= ~(1 << seat_slot); |
|
||||||
notify_touch(master, time, seat_slot, 0, 0, WL_TOUCH_UP); |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_TOUCH_DOWN: |
|
||||||
if (device->output == NULL) |
|
||||||
break; |
|
||||||
transform_absolute(device, &cx, &cy); |
|
||||||
weston_output_transform_coordinate(device->output, |
|
||||||
wl_fixed_from_int(cx), |
|
||||||
wl_fixed_from_int(cy), |
|
||||||
&x, &y); |
|
||||||
seat_slot = ffs(~master->slot_map) - 1; |
|
||||||
device->abs.seat_slot = seat_slot; |
|
||||||
master->slot_map |= 1 << seat_slot; |
|
||||||
notify_touch(master, time, seat_slot, x, y, WL_TOUCH_DOWN); |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_MOTION: |
|
||||||
if (device->output == NULL) |
|
||||||
break; |
|
||||||
transform_absolute(device, &cx, &cy); |
|
||||||
weston_output_transform_coordinate(device->output, |
|
||||||
wl_fixed_from_int(cx), |
|
||||||
wl_fixed_from_int(cy), |
|
||||||
&x, &y); |
|
||||||
|
|
||||||
if (device->seat_caps & EVDEV_SEAT_TOUCH) |
|
||||||
notify_touch(master, time, device->abs.seat_slot, |
|
||||||
x, y, WL_TOUCH_MOTION); |
|
||||||
else if (device->seat_caps & EVDEV_SEAT_POINTER) |
|
||||||
notify_motion_absolute(master, time, x, y); |
|
||||||
break; |
|
||||||
case EVDEV_ABSOLUTE_TOUCH_UP: |
|
||||||
seat_slot = device->abs.seat_slot; |
|
||||||
master->slot_map &= ~(1 << seat_slot); |
|
||||||
notify_touch(master, time, seat_slot, 0, 0, WL_TOUCH_UP); |
|
||||||
break; |
|
||||||
default: |
|
||||||
assert(0 && "Unknown pending event type"); |
|
||||||
} |
|
||||||
|
|
||||||
device->pending_event = EVDEV_NONE; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
evdev_process_touch_button(struct evdev_device *device, int time, int value) |
|
||||||
{ |
|
||||||
if (device->pending_event != EVDEV_NONE && |
|
||||||
device->pending_event != EVDEV_ABSOLUTE_MOTION) |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
|
|
||||||
device->pending_event = (value ? |
|
||||||
EVDEV_ABSOLUTE_TOUCH_DOWN : |
|
||||||
EVDEV_ABSOLUTE_TOUCH_UP); |
|
||||||
} |
|
||||||
|
|
||||||
static inline void |
|
||||||
evdev_process_key(struct evdev_device *device, struct input_event *e, int time) |
|
||||||
{ |
|
||||||
/* ignore kernel key repeat */ |
|
||||||
if (e->value == 2) |
|
||||||
return; |
|
||||||
|
|
||||||
if (e->code == BTN_TOUCH) { |
|
||||||
if (!device->is_mt) |
|
||||||
evdev_process_touch_button(device, time, e->value); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
|
|
||||||
switch (e->code) { |
|
||||||
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; |
|
||||||
|
|
||||||
default: |
|
||||||
notify_key(device->seat, |
|
||||||
time, e->code, |
|
||||||
e->value ? WL_KEYBOARD_KEY_STATE_PRESSED : |
|
||||||
WL_KEYBOARD_KEY_STATE_RELEASED, |
|
||||||
STATE_UPDATE_AUTOMATIC); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
evdev_process_touch(struct evdev_device *device, |
|
||||||
struct input_event *e, |
|
||||||
uint32_t time) |
|
||||||
{ |
|
||||||
int screen_width, screen_height; |
|
||||||
|
|
||||||
if (device->output == NULL) |
|
||||||
return; |
|
||||||
|
|
||||||
screen_width = device->output->current_mode->width; |
|
||||||
screen_height = device->output->current_mode->height; |
|
||||||
|
|
||||||
switch (e->code) { |
|
||||||
case ABS_MT_SLOT: |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
device->mt.slot = e->value; |
|
||||||
break; |
|
||||||
case ABS_MT_TRACKING_ID: |
|
||||||
if (device->pending_event != EVDEV_NONE && |
|
||||||
device->pending_event != EVDEV_ABSOLUTE_MT_MOTION) |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
if (e->value >= 0) |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MT_DOWN; |
|
||||||
else |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MT_UP; |
|
||||||
break; |
|
||||||
case ABS_MT_POSITION_X: |
|
||||||
device->mt.slots[device->mt.slot].x = |
|
||||||
(e->value - device->abs.min_x) * screen_width / |
|
||||||
(device->abs.max_x - device->abs.min_x); |
|
||||||
if (device->pending_event == EVDEV_NONE) |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MT_MOTION; |
|
||||||
break; |
|
||||||
case ABS_MT_POSITION_Y: |
|
||||||
device->mt.slots[device->mt.slot].y = |
|
||||||
(e->value - device->abs.min_y) * screen_height / |
|
||||||
(device->abs.max_y - device->abs.min_y); |
|
||||||
if (device->pending_event == EVDEV_NONE) |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MT_MOTION; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static inline void |
|
||||||
evdev_process_absolute_motion(struct evdev_device *device, |
|
||||||
struct input_event *e) |
|
||||||
{ |
|
||||||
int screen_width, screen_height; |
|
||||||
|
|
||||||
if (device->output == NULL) |
|
||||||
return; |
|
||||||
|
|
||||||
screen_width = device->output->current_mode->width; |
|
||||||
screen_height = device->output->current_mode->height; |
|
||||||
|
|
||||||
switch (e->code) { |
|
||||||
case ABS_X: |
|
||||||
device->abs.x = |
|
||||||
(e->value - device->abs.min_x) * screen_width / |
|
||||||
(device->abs.max_x - device->abs.min_x); |
|
||||||
if (device->pending_event == EVDEV_NONE) |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MOTION; |
|
||||||
break; |
|
||||||
case ABS_Y: |
|
||||||
device->abs.y = |
|
||||||
(e->value - device->abs.min_y) * screen_height / |
|
||||||
(device->abs.max_y - device->abs.min_y); |
|
||||||
if (device->pending_event == EVDEV_NONE) |
|
||||||
device->pending_event = EVDEV_ABSOLUTE_MOTION; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static inline void |
|
||||||
evdev_process_relative(struct evdev_device *device, |
|
||||||
struct input_event *e, uint32_t time) |
|
||||||
{ |
|
||||||
switch (e->code) { |
|
||||||
case REL_X: |
|
||||||
if (device->pending_event != EVDEV_RELATIVE_MOTION) |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
device->rel.dx += wl_fixed_from_int(e->value); |
|
||||||
device->pending_event = EVDEV_RELATIVE_MOTION; |
|
||||||
break; |
|
||||||
case REL_Y: |
|
||||||
if (device->pending_event != EVDEV_RELATIVE_MOTION) |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
device->rel.dy += wl_fixed_from_int(e->value); |
|
||||||
device->pending_event = EVDEV_RELATIVE_MOTION; |
|
||||||
break; |
|
||||||
case REL_WHEEL: |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
switch (e->value) { |
|
||||||
case -1: |
|
||||||
/* Scroll down */ |
|
||||||
case 1: |
|
||||||
/* Scroll up */ |
|
||||||
notify_axis(device->seat, |
|
||||||
time, |
|
||||||
WL_POINTER_AXIS_VERTICAL_SCROLL, |
|
||||||
-1 * e->value * DEFAULT_AXIS_STEP_DISTANCE); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
} |
|
||||||
break; |
|
||||||
case REL_HWHEEL: |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
switch (e->value) { |
|
||||||
case -1: |
|
||||||
/* Scroll left */ |
|
||||||
case 1: |
|
||||||
/* Scroll right */ |
|
||||||
notify_axis(device->seat, |
|
||||||
time, |
|
||||||
WL_POINTER_AXIS_HORIZONTAL_SCROLL, |
|
||||||
e->value * DEFAULT_AXIS_STEP_DISTANCE); |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static inline void |
|
||||||
evdev_process_absolute(struct evdev_device *device, |
|
||||||
struct input_event *e, |
|
||||||
uint32_t time) |
|
||||||
{ |
|
||||||
if (device->is_mt) { |
|
||||||
evdev_process_touch(device, e, time); |
|
||||||
} else { |
|
||||||
evdev_process_absolute_motion(device, e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fallback_process(struct evdev_dispatch *dispatch, |
|
||||||
struct evdev_device *device, |
|
||||||
struct input_event *event, |
|
||||||
uint32_t time) |
|
||||||
{ |
|
||||||
switch (event->type) { |
|
||||||
case EV_REL: |
|
||||||
evdev_process_relative(device, event, time); |
|
||||||
break; |
|
||||||
case EV_ABS: |
|
||||||
evdev_process_absolute(device, event, time); |
|
||||||
break; |
|
||||||
case EV_KEY: |
|
||||||
evdev_process_key(device, event, time); |
|
||||||
break; |
|
||||||
case EV_SYN: |
|
||||||
evdev_flush_pending_event(device, time); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
fallback_destroy(struct evdev_dispatch *dispatch) |
|
||||||
{ |
|
||||||
free(dispatch); |
|
||||||
} |
|
||||||
|
|
||||||
struct evdev_dispatch_interface fallback_interface = { |
|
||||||
fallback_process, |
|
||||||
fallback_destroy |
|
||||||
}; |
|
||||||
|
|
||||||
static struct evdev_dispatch * |
|
||||||
fallback_dispatch_create(void) |
|
||||||
{ |
|
||||||
struct evdev_dispatch *dispatch = malloc(sizeof *dispatch); |
|
||||||
if (dispatch == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
dispatch->interface = &fallback_interface; |
|
||||||
|
|
||||||
return dispatch; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
evdev_process_events(struct evdev_device *device, |
|
||||||
struct input_event *ev, int count) |
|
||||||
{ |
|
||||||
struct evdev_dispatch *dispatch = device->dispatch; |
|
||||||
struct input_event *e, *end; |
|
||||||
uint32_t time = 0; |
|
||||||
|
|
||||||
e = ev; |
|
||||||
end = e + count; |
|
||||||
for (e = ev; e < end; e++) { |
|
||||||
time = e->time.tv_sec * 1000 + e->time.tv_usec / 1000; |
|
||||||
|
|
||||||
dispatch->interface->process(dispatch, device, e, time); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
evdev_device_data(int fd, uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct weston_compositor *ec; |
|
||||||
struct evdev_device *device = data; |
|
||||||
struct input_event ev[32]; |
|
||||||
int len; |
|
||||||
|
|
||||||
ec = device->seat->compositor; |
|
||||||
if (!ec->session_active) |
|
||||||
return 1; |
|
||||||
|
|
||||||
/* If the compositor is repainting, this function is called only once
|
|
||||||
* per frame and we have to process all the events available on the |
|
||||||
* fd, otherwise there will be input lag. */ |
|
||||||
do { |
|
||||||
if (device->mtdev) |
|
||||||
len = mtdev_get(device->mtdev, fd, ev, |
|
||||||
ARRAY_LENGTH(ev)) * |
|
||||||
sizeof (struct input_event); |
|
||||||
else |
|
||||||
len = read(fd, &ev, sizeof ev); |
|
||||||
|
|
||||||
if (len < 0 || len % sizeof ev[0] != 0) { |
|
||||||
if (len < 0 && errno != EAGAIN && errno != EINTR) { |
|
||||||
weston_log("device %s died\n", |
|
||||||
device->devnode); |
|
||||||
wl_event_source_remove(device->source); |
|
||||||
device->source = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
evdev_process_events(device, ev, len / sizeof ev[0]); |
|
||||||
|
|
||||||
} while (len > 0); |
|
||||||
|
|
||||||
return 1; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
evdev_configure_device(struct evdev_device *device) |
|
||||||
{ |
|
||||||
struct input_absinfo absinfo; |
|
||||||
unsigned long ev_bits[NBITS(EV_MAX)]; |
|
||||||
unsigned long abs_bits[NBITS(ABS_MAX)]; |
|
||||||
unsigned long rel_bits[NBITS(REL_MAX)]; |
|
||||||
unsigned long key_bits[NBITS(KEY_MAX)]; |
|
||||||
int has_abs, has_rel, has_mt; |
|
||||||
int has_button, has_keyboard, has_touch; |
|
||||||
unsigned int i; |
|
||||||
|
|
||||||
has_rel = 0; |
|
||||||
has_abs = 0; |
|
||||||
has_mt = 0; |
|
||||||
has_button = 0; |
|
||||||
has_keyboard = 0; |
|
||||||
has_touch = 0; |
|
||||||
|
|
||||||
ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits); |
|
||||||
if (TEST_BIT(ev_bits, EV_ABS)) { |
|
||||||
ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)), |
|
||||||
abs_bits); |
|
||||||
|
|
||||||
if (TEST_BIT(abs_bits, ABS_X)) { |
|
||||||
ioctl(device->fd, EVIOCGABS(ABS_X), &absinfo); |
|
||||||
device->abs.min_x = absinfo.minimum; |
|
||||||
device->abs.max_x = absinfo.maximum; |
|
||||||
has_abs = 1; |
|
||||||
} |
|
||||||
if (TEST_BIT(abs_bits, ABS_Y)) { |
|
||||||
ioctl(device->fd, EVIOCGABS(ABS_Y), &absinfo); |
|
||||||
device->abs.min_y = absinfo.minimum; |
|
||||||
device->abs.max_y = absinfo.maximum; |
|
||||||
has_abs = 1; |
|
||||||
} |
|
||||||
/* We only handle the slotted Protocol B in weston.
|
|
||||||
Devices with ABS_MT_POSITION_* but not ABS_MT_SLOT |
|
||||||
require mtdev for conversion. */ |
|
||||||
if (TEST_BIT(abs_bits, ABS_MT_POSITION_X) && |
|
||||||
TEST_BIT(abs_bits, ABS_MT_POSITION_Y)) { |
|
||||||
ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_X), |
|
||||||
&absinfo); |
|
||||||
device->abs.min_x = absinfo.minimum; |
|
||||||
device->abs.max_x = absinfo.maximum; |
|
||||||
ioctl(device->fd, EVIOCGABS(ABS_MT_POSITION_Y), |
|
||||||
&absinfo); |
|
||||||
device->abs.min_y = absinfo.minimum; |
|
||||||
device->abs.max_y = absinfo.maximum; |
|
||||||
device->is_mt = 1; |
|
||||||
has_touch = 1; |
|
||||||
has_mt = 1; |
|
||||||
|
|
||||||
if (!TEST_BIT(abs_bits, ABS_MT_SLOT)) { |
|
||||||
device->mtdev = mtdev_new_open(device->fd); |
|
||||||
if (!device->mtdev) { |
|
||||||
weston_log("mtdev required but failed to open for %s\n", |
|
||||||
device->devnode); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
device->mt.slot = device->mtdev->caps.slot.value; |
|
||||||
} else { |
|
||||||
ioctl(device->fd, EVIOCGABS(ABS_MT_SLOT), |
|
||||||
&absinfo); |
|
||||||
device->mt.slot = absinfo.value; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if (TEST_BIT(ev_bits, EV_REL)) { |
|
||||||
ioctl(device->fd, EVIOCGBIT(EV_REL, sizeof(rel_bits)), |
|
||||||
rel_bits); |
|
||||||
if (TEST_BIT(rel_bits, REL_X) || TEST_BIT(rel_bits, REL_Y)) |
|
||||||
has_rel = 1; |
|
||||||
} |
|
||||||
if (TEST_BIT(ev_bits, EV_KEY)) { |
|
||||||
ioctl(device->fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), |
|
||||||
key_bits); |
|
||||||
if (TEST_BIT(key_bits, BTN_TOOL_FINGER) && |
|
||||||
!TEST_BIT(key_bits, BTN_TOOL_PEN) && |
|
||||||
(has_abs || has_mt)) { |
|
||||||
device->dispatch = evdev_touchpad_create(device); |
|
||||||
weston_log("input device %s, %s is a touchpad\n", |
|
||||||
device->devname, device->devnode); |
|
||||||
} |
|
||||||
for (i = KEY_ESC; i < KEY_MAX; i++) { |
|
||||||
if (i >= BTN_MISC && i < KEY_OK) |
|
||||||
continue; |
|
||||||
if (TEST_BIT(key_bits, i)) { |
|
||||||
has_keyboard = 1; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
if (TEST_BIT(key_bits, BTN_TOUCH)) |
|
||||||
has_touch = 1; |
|
||||||
for (i = BTN_MISC; i < BTN_JOYSTICK; i++) { |
|
||||||
if (TEST_BIT(key_bits, i)) { |
|
||||||
has_button = 1; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if (TEST_BIT(ev_bits, EV_LED)) |
|
||||||
has_keyboard = 1; |
|
||||||
|
|
||||||
if ((has_abs || has_rel) && has_button) { |
|
||||||
weston_seat_init_pointer(device->seat); |
|
||||||
device->seat_caps |= EVDEV_SEAT_POINTER; |
|
||||||
weston_log("input device %s, %s is a pointer caps =%s%s%s\n", |
|
||||||
device->devname, device->devnode, |
|
||||||
has_abs ? " absolute-motion" : "", |
|
||||||
has_rel ? " relative-motion": "", |
|
||||||
has_button ? " button" : ""); |
|
||||||
} |
|
||||||
if (has_keyboard) { |
|
||||||
if (weston_seat_init_keyboard(device->seat, NULL) < 0) |
|
||||||
return -1; |
|
||||||
device->seat_caps |= EVDEV_SEAT_KEYBOARD; |
|
||||||
weston_log("input device %s, %s is a keyboard\n", |
|
||||||
device->devname, device->devnode); |
|
||||||
} |
|
||||||
if (has_touch && !has_button) { |
|
||||||
weston_seat_init_touch(device->seat); |
|
||||||
device->seat_caps |= EVDEV_SEAT_TOUCH; |
|
||||||
weston_log("input device %s, %s is a touch device\n", |
|
||||||
device->devname, device->devnode); |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
notify_output_destroy(struct wl_listener *listener, void *data) |
|
||||||
{ |
|
||||||
struct evdev_device *device = |
|
||||||
container_of(listener, |
|
||||||
struct evdev_device, output_destroy_listener); |
|
||||||
struct weston_compositor *c = device->seat->compositor; |
|
||||||
struct weston_output *output; |
|
||||||
|
|
||||||
if (!device->output_name && !wl_list_empty(&c->output_list)) { |
|
||||||
output = container_of(c->output_list.next, |
|
||||||
struct weston_output, link); |
|
||||||
evdev_device_set_output(device, output); |
|
||||||
} else { |
|
||||||
device->output = NULL; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
evdev_device_set_output(struct evdev_device *device, |
|
||||||
struct weston_output *output) |
|
||||||
{ |
|
||||||
if (device->output_destroy_listener.notify) { |
|
||||||
wl_list_remove(&device->output_destroy_listener.link); |
|
||||||
device->output_destroy_listener.notify = NULL; |
|
||||||
} |
|
||||||
|
|
||||||
device->output = output; |
|
||||||
device->output_destroy_listener.notify = notify_output_destroy; |
|
||||||
wl_signal_add(&output->destroy_signal, |
|
||||||
&device->output_destroy_listener); |
|
||||||
} |
|
||||||
|
|
||||||
struct evdev_device * |
|
||||||
evdev_device_create(struct weston_seat *seat, const char *path, int device_fd) |
|
||||||
{ |
|
||||||
struct evdev_device *device; |
|
||||||
struct weston_compositor *ec; |
|
||||||
char devname[256] = "unknown"; |
|
||||||
|
|
||||||
device = zalloc(sizeof *device); |
|
||||||
if (device == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
ec = seat->compositor; |
|
||||||
device->seat = seat; |
|
||||||
device->seat_caps = 0; |
|
||||||
device->is_mt = 0; |
|
||||||
device->mtdev = NULL; |
|
||||||
device->devnode = strdup(path); |
|
||||||
device->mt.slot = -1; |
|
||||||
device->rel.dx = 0; |
|
||||||
device->rel.dy = 0; |
|
||||||
device->dispatch = NULL; |
|
||||||
device->fd = device_fd; |
|
||||||
device->pending_event = EVDEV_NONE; |
|
||||||
wl_list_init(&device->link); |
|
||||||
|
|
||||||
ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname); |
|
||||||
devname[sizeof(devname) - 1] = '\0'; |
|
||||||
device->devname = strdup(devname); |
|
||||||
|
|
||||||
if (evdev_configure_device(device) == -1) |
|
||||||
goto err; |
|
||||||
|
|
||||||
if (device->seat_caps == 0) { |
|
||||||
evdev_device_destroy(device); |
|
||||||
return EVDEV_UNHANDLED_DEVICE; |
|
||||||
} |
|
||||||
|
|
||||||
/* If the dispatch was not set up use the fallback. */ |
|
||||||
if (device->dispatch == NULL) |
|
||||||
device->dispatch = fallback_dispatch_create(); |
|
||||||
if (device->dispatch == NULL) |
|
||||||
goto err; |
|
||||||
|
|
||||||
device->source = wl_event_loop_add_fd(ec->input_loop, device->fd, |
|
||||||
WL_EVENT_READABLE, |
|
||||||
evdev_device_data, device); |
|
||||||
if (device->source == NULL) |
|
||||||
goto err; |
|
||||||
|
|
||||||
return device; |
|
||||||
|
|
||||||
err: |
|
||||||
evdev_device_destroy(device); |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
evdev_device_destroy(struct evdev_device *device) |
|
||||||
{ |
|
||||||
struct evdev_dispatch *dispatch; |
|
||||||
|
|
||||||
if (device->seat_caps & EVDEV_SEAT_POINTER) |
|
||||||
weston_seat_release_pointer(device->seat); |
|
||||||
if (device->seat_caps & EVDEV_SEAT_KEYBOARD) |
|
||||||
weston_seat_release_keyboard(device->seat); |
|
||||||
if (device->seat_caps & EVDEV_SEAT_TOUCH) |
|
||||||
weston_seat_release_touch(device->seat); |
|
||||||
|
|
||||||
dispatch = device->dispatch; |
|
||||||
if (dispatch) |
|
||||||
dispatch->interface->destroy(dispatch); |
|
||||||
|
|
||||||
if (device->source) |
|
||||||
wl_event_source_remove(device->source); |
|
||||||
if (device->output) |
|
||||||
wl_list_remove(&device->output_destroy_listener.link); |
|
||||||
wl_list_remove(&device->link); |
|
||||||
if (device->mtdev) |
|
||||||
mtdev_close_delete(device->mtdev); |
|
||||||
close(device->fd); |
|
||||||
free(device->devname); |
|
||||||
free(device->devnode); |
|
||||||
free(device->output_name); |
|
||||||
free(device); |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
evdev_notify_keyboard_focus(struct weston_seat *seat, |
|
||||||
struct wl_list *evdev_devices) |
|
||||||
{ |
|
||||||
struct evdev_device *device; |
|
||||||
struct wl_array keys; |
|
||||||
unsigned int i, set; |
|
||||||
char evdev_keys[(KEY_CNT + 7) / 8]; |
|
||||||
char all_keys[(KEY_CNT + 7) / 8]; |
|
||||||
uint32_t *k; |
|
||||||
int ret; |
|
||||||
|
|
||||||
if (!seat->keyboard_device_count > 0) |
|
||||||
return; |
|
||||||
|
|
||||||
memset(all_keys, 0, sizeof all_keys); |
|
||||||
wl_list_for_each(device, evdev_devices, link) { |
|
||||||
memset(evdev_keys, 0, sizeof evdev_keys); |
|
||||||
ret = ioctl(device->fd, |
|
||||||
EVIOCGKEY(sizeof evdev_keys), evdev_keys); |
|
||||||
if (ret < 0) { |
|
||||||
weston_log("failed to get keys for device %s\n", |
|
||||||
device->devnode); |
|
||||||
continue; |
|
||||||
} |
|
||||||
for (i = 0; i < ARRAY_LENGTH(evdev_keys); i++) |
|
||||||
all_keys[i] |= evdev_keys[i]; |
|
||||||
} |
|
||||||
|
|
||||||
wl_array_init(&keys); |
|
||||||
for (i = 0; i < KEY_CNT; i++) { |
|
||||||
set = all_keys[i >> 3] & (1 << (i & 7)); |
|
||||||
if (set) { |
|
||||||
k = wl_array_add(&keys, sizeof *k); |
|
||||||
*k = i; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
notify_keyboard_focus_in(seat, &keys, STATE_UPDATE_AUTOMATIC); |
|
||||||
|
|
||||||
wl_array_release(&keys); |
|
||||||
} |
|
@ -1,137 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2011, 2012 Intel Corporation |
|
||||||
* |
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef EVDEV_H |
|
||||||
#define EVDEV_H |
|
||||||
|
|
||||||
#include "config.h" |
|
||||||
|
|
||||||
#include <linux/input.h> |
|
||||||
#include <wayland-util.h> |
|
||||||
|
|
||||||
#define MAX_SLOTS 16 |
|
||||||
|
|
||||||
enum evdev_event_type { |
|
||||||
EVDEV_NONE, |
|
||||||
EVDEV_ABSOLUTE_TOUCH_DOWN, |
|
||||||
EVDEV_ABSOLUTE_MOTION, |
|
||||||
EVDEV_ABSOLUTE_TOUCH_UP, |
|
||||||
EVDEV_ABSOLUTE_MT_DOWN, |
|
||||||
EVDEV_ABSOLUTE_MT_MOTION, |
|
||||||
EVDEV_ABSOLUTE_MT_UP, |
|
||||||
EVDEV_RELATIVE_MOTION, |
|
||||||
}; |
|
||||||
|
|
||||||
enum evdev_device_seat_capability { |
|
||||||
EVDEV_SEAT_POINTER = (1 << 0), |
|
||||||
EVDEV_SEAT_KEYBOARD = (1 << 1), |
|
||||||
EVDEV_SEAT_TOUCH = (1 << 2) |
|
||||||
}; |
|
||||||
|
|
||||||
struct evdev_device { |
|
||||||
struct weston_seat *seat; |
|
||||||
struct wl_list link; |
|
||||||
struct wl_event_source *source; |
|
||||||
struct weston_output *output; |
|
||||||
struct evdev_dispatch *dispatch; |
|
||||||
struct wl_listener output_destroy_listener; |
|
||||||
char *devnode; |
|
||||||
char *devname; |
|
||||||
char *output_name; |
|
||||||
int fd; |
|
||||||
struct { |
|
||||||
int min_x, max_x, min_y, max_y; |
|
||||||
uint32_t seat_slot; |
|
||||||
int32_t x, y; |
|
||||||
|
|
||||||
int apply_calibration; |
|
||||||
float calibration[6]; |
|
||||||
} abs; |
|
||||||
|
|
||||||
struct { |
|
||||||
int slot; |
|
||||||
struct { |
|
||||||
int32_t x, y; |
|
||||||
uint32_t seat_slot; |
|
||||||
} slots[MAX_SLOTS]; |
|
||||||
} mt; |
|
||||||
struct mtdev *mtdev; |
|
||||||
|
|
||||||
struct { |
|
||||||
wl_fixed_t dx, dy; |
|
||||||
} rel; |
|
||||||
|
|
||||||
enum evdev_event_type pending_event; |
|
||||||
enum evdev_device_seat_capability seat_caps; |
|
||||||
|
|
||||||
int is_mt; |
|
||||||
}; |
|
||||||
|
|
||||||
/* copied from udev/extras/input_id/input_id.c */ |
|
||||||
/* we must use this kernel-compatible implementation */ |
|
||||||
#define BITS_PER_LONG (sizeof(unsigned long) * 8) |
|
||||||
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) |
|
||||||
#define OFF(x) ((x)%BITS_PER_LONG) |
|
||||||
#define BIT(x) (1UL<<OFF(x)) |
|
||||||
#define LONG(x) ((x)/BITS_PER_LONG) |
|
||||||
#define TEST_BIT(array, bit) ((array[LONG(bit)] >> OFF(bit)) & 1) |
|
||||||
/* end copied */ |
|
||||||
|
|
||||||
#define EVDEV_UNHANDLED_DEVICE ((struct evdev_device *) 1) |
|
||||||
|
|
||||||
struct evdev_dispatch; |
|
||||||
|
|
||||||
struct evdev_dispatch_interface { |
|
||||||
/* Process an evdev input event. */ |
|
||||||
void (*process)(struct evdev_dispatch *dispatch, |
|
||||||
struct evdev_device *device, |
|
||||||
struct input_event *event, |
|
||||||
uint32_t time); |
|
||||||
|
|
||||||
/* Destroy an event dispatch handler and free all its resources. */ |
|
||||||
void (*destroy)(struct evdev_dispatch *dispatch); |
|
||||||
}; |
|
||||||
|
|
||||||
struct evdev_dispatch { |
|
||||||
struct evdev_dispatch_interface *interface; |
|
||||||
}; |
|
||||||
|
|
||||||
struct evdev_dispatch * |
|
||||||
evdev_touchpad_create(struct evdev_device *device); |
|
||||||
|
|
||||||
void |
|
||||||
evdev_led_update(struct evdev_device *device, enum weston_led leds); |
|
||||||
|
|
||||||
struct evdev_device * |
|
||||||
evdev_device_create(struct weston_seat *seat, const char *path, int device_fd); |
|
||||||
|
|
||||||
void |
|
||||||
evdev_device_set_output(struct evdev_device *device, |
|
||||||
struct weston_output *output); |
|
||||||
void |
|
||||||
evdev_device_destroy(struct evdev_device *device); |
|
||||||
|
|
||||||
void |
|
||||||
evdev_notify_keyboard_focus(struct weston_seat *seat, |
|
||||||
struct wl_list *evdev_devices); |
|
||||||
|
|
||||||
#endif /* EVDEV_H */ |
|
@ -1,337 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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 "config.h" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <limits.h> |
|
||||||
#include <math.h> |
|
||||||
|
|
||||||
#include <wayland-util.h> |
|
||||||
|
|
||||||
#include "compositor.h" |
|
||||||
#include "filter.h" |
|
||||||
|
|
||||||
WL_EXPORT void |
|
||||||
weston_filter_dispatch(struct weston_motion_filter *filter, |
|
||||||
struct weston_motion_params *motion, |
|
||||||
void *data, uint32_t time) |
|
||||||
{ |
|
||||||
filter->interface->filter(filter, motion, data, time); |
|
||||||
} |
|
||||||
|
|
||||||
/*
|
|
||||||
* Pointer acceleration filter |
|
||||||
*/ |
|
||||||
|
|
||||||
#define MAX_VELOCITY_DIFF 1.0 |
|
||||||
#define MOTION_TIMEOUT 300 /* (ms) */ |
|
||||||
#define NUM_POINTER_TRACKERS 16 |
|
||||||
|
|
||||||
struct pointer_tracker { |
|
||||||
double dx; |
|
||||||
double dy; |
|
||||||
uint32_t time; |
|
||||||
int dir; |
|
||||||
}; |
|
||||||
|
|
||||||
struct pointer_accelerator; |
|
||||||
struct pointer_accelerator { |
|
||||||
struct weston_motion_filter base; |
|
||||||
|
|
||||||
accel_profile_func_t profile; |
|
||||||
|
|
||||||
double velocity; |
|
||||||
double last_velocity; |
|
||||||
int last_dx; |
|
||||||
int last_dy; |
|
||||||
|
|
||||||
struct pointer_tracker *trackers; |
|
||||||
int cur_tracker; |
|
||||||
}; |
|
||||||
|
|
||||||
enum directions { |
|
||||||
N = 1 << 0, |
|
||||||
NE = 1 << 1, |
|
||||||
E = 1 << 2, |
|
||||||
SE = 1 << 3, |
|
||||||
S = 1 << 4, |
|
||||||
SW = 1 << 5, |
|
||||||
W = 1 << 6, |
|
||||||
NW = 1 << 7, |
|
||||||
UNDEFINED_DIRECTION = 0xff |
|
||||||
}; |
|
||||||
|
|
||||||
static int |
|
||||||
get_direction(int dx, int dy) |
|
||||||
{ |
|
||||||
int dir = UNDEFINED_DIRECTION; |
|
||||||
int d1, d2; |
|
||||||
double r; |
|
||||||
|
|
||||||
if (abs(dx) < 2 && abs(dy) < 2) { |
|
||||||
if (dx > 0 && dy > 0) |
|
||||||
dir = S | SE | E; |
|
||||||
else if (dx > 0 && dy < 0) |
|
||||||
dir = N | NE | E; |
|
||||||
else if (dx < 0 && dy > 0) |
|
||||||
dir = S | SW | W; |
|
||||||
else if (dx < 0 && dy < 0) |
|
||||||
dir = N | NW | W; |
|
||||||
else if (dx > 0) |
|
||||||
dir = NW | W | SW; |
|
||||||
else if (dx < 0) |
|
||||||
dir = NE | E | SE; |
|
||||||
else if (dy > 0) |
|
||||||
dir = SE | S | SW; |
|
||||||
else if (dy < 0) |
|
||||||
dir = NE | N | NW; |
|
||||||
} |
|
||||||
else { |
|
||||||
/* Calculate r within the interval [0 to 8)
|
|
||||||
* |
|
||||||
* r = [0 .. 2π] where 0 is North |
|
||||||
* d_f = r / 2π ([0 .. 1)) |
|
||||||
* d_8 = 8 * d_f |
|
||||||
*/ |
|
||||||
r = atan2(dy, dx); |
|
||||||
r = fmod(r + 2.5*M_PI, 2*M_PI); |
|
||||||
r *= 4*M_1_PI; |
|
||||||
|
|
||||||
/* Mark one or two close enough octants */ |
|
||||||
d1 = (int)(r + 0.9) % 8; |
|
||||||
d2 = (int)(r + 0.1) % 8; |
|
||||||
|
|
||||||
dir = (1 << d1) | (1 << d2); |
|
||||||
} |
|
||||||
|
|
||||||
return dir; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
feed_trackers(struct pointer_accelerator *accel, |
|
||||||
double dx, double dy, |
|
||||||
uint32_t time) |
|
||||||
{ |
|
||||||
int i, current; |
|
||||||
struct pointer_tracker *trackers = accel->trackers; |
|
||||||
|
|
||||||
for (i = 0; i < NUM_POINTER_TRACKERS; i++) { |
|
||||||
trackers[i].dx += dx; |
|
||||||
trackers[i].dy += dy; |
|
||||||
} |
|
||||||
|
|
||||||
current = (accel->cur_tracker + 1) % NUM_POINTER_TRACKERS; |
|
||||||
accel->cur_tracker = current; |
|
||||||
|
|
||||||
trackers[current].dx = 0.0; |
|
||||||
trackers[current].dy = 0.0; |
|
||||||
trackers[current].time = time; |
|
||||||
trackers[current].dir = get_direction(dx, dy); |
|
||||||
} |
|
||||||
|
|
||||||
static struct pointer_tracker * |
|
||||||
tracker_by_offset(struct pointer_accelerator *accel, unsigned int offset) |
|
||||||
{ |
|
||||||
unsigned int index = |
|
||||||
(accel->cur_tracker + NUM_POINTER_TRACKERS - offset) |
|
||||||
% NUM_POINTER_TRACKERS; |
|
||||||
return &accel->trackers[index]; |
|
||||||
} |
|
||||||
|
|
||||||
static double |
|
||||||
calculate_tracker_velocity(struct pointer_tracker *tracker, uint32_t time) |
|
||||||
{ |
|
||||||
int dx; |
|
||||||
int dy; |
|
||||||
double distance; |
|
||||||
|
|
||||||
dx = tracker->dx; |
|
||||||
dy = tracker->dy; |
|
||||||
distance = sqrt(dx*dx + dy*dy); |
|
||||||
return distance / (double)(time - tracker->time); |
|
||||||
} |
|
||||||
|
|
||||||
static double |
|
||||||
calculate_velocity(struct pointer_accelerator *accel, uint32_t time) |
|
||||||
{ |
|
||||||
struct pointer_tracker *tracker; |
|
||||||
double velocity; |
|
||||||
double result = 0.0; |
|
||||||
double initial_velocity; |
|
||||||
double velocity_diff; |
|
||||||
unsigned int offset; |
|
||||||
|
|
||||||
unsigned int dir = tracker_by_offset(accel, 0)->dir; |
|
||||||
|
|
||||||
/* Find first velocity */ |
|
||||||
for (offset = 1; offset < NUM_POINTER_TRACKERS; offset++) { |
|
||||||
tracker = tracker_by_offset(accel, offset); |
|
||||||
|
|
||||||
if (time <= tracker->time) |
|
||||||
continue; |
|
||||||
|
|
||||||
result = initial_velocity = |
|
||||||
calculate_tracker_velocity(tracker, time); |
|
||||||
if (initial_velocity > 0.0) |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
/* Find least recent vector within a timelimit, maximum velocity diff
|
|
||||||
* and direction threshold. */ |
|
||||||
for (; offset < NUM_POINTER_TRACKERS; offset++) { |
|
||||||
tracker = tracker_by_offset(accel, offset); |
|
||||||
|
|
||||||
/* Stop if too far away in time */ |
|
||||||
if (time - tracker->time > MOTION_TIMEOUT || |
|
||||||
tracker->time > time) |
|
||||||
break; |
|
||||||
|
|
||||||
/* Stop if direction changed */ |
|
||||||
dir &= tracker->dir; |
|
||||||
if (dir == 0) |
|
||||||
break; |
|
||||||
|
|
||||||
velocity = calculate_tracker_velocity(tracker, time); |
|
||||||
|
|
||||||
/* Stop if velocity differs too much from initial */ |
|
||||||
velocity_diff = fabs(initial_velocity - velocity); |
|
||||||
if (velocity_diff > MAX_VELOCITY_DIFF) |
|
||||||
break; |
|
||||||
|
|
||||||
result = velocity; |
|
||||||
} |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
static double |
|
||||||
acceleration_profile(struct pointer_accelerator *accel, |
|
||||||
void *data, double velocity, uint32_t time) |
|
||||||
{ |
|
||||||
return accel->profile(&accel->base, data, velocity, time); |
|
||||||
} |
|
||||||
|
|
||||||
static double |
|
||||||
calculate_acceleration(struct pointer_accelerator *accel, |
|
||||||
void *data, double velocity, uint32_t time) |
|
||||||
{ |
|
||||||
double factor; |
|
||||||
|
|
||||||
/* Use Simpson's rule to calculate the avarage acceleration between
|
|
||||||
* the previous motion and the most recent. */ |
|
||||||
factor = acceleration_profile(accel, data, velocity, time); |
|
||||||
factor += acceleration_profile(accel, data, accel->last_velocity, time); |
|
||||||
factor += 4.0 * |
|
||||||
acceleration_profile(accel, data, |
|
||||||
(accel->last_velocity + velocity) / 2, |
|
||||||
time); |
|
||||||
|
|
||||||
factor = factor / 6.0; |
|
||||||
|
|
||||||
return factor; |
|
||||||
} |
|
||||||
|
|
||||||
static double |
|
||||||
soften_delta(double last_delta, double delta) |
|
||||||
{ |
|
||||||
if (delta < -1.0 || delta > 1.0) { |
|
||||||
if (delta > last_delta) |
|
||||||
return delta - 0.5; |
|
||||||
else if (delta < last_delta) |
|
||||||
return delta + 0.5; |
|
||||||
} |
|
||||||
|
|
||||||
return delta; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
apply_softening(struct pointer_accelerator *accel, |
|
||||||
struct weston_motion_params *motion) |
|
||||||
{ |
|
||||||
motion->dx = soften_delta(accel->last_dx, motion->dx); |
|
||||||
motion->dy = soften_delta(accel->last_dy, motion->dy); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
accelerator_filter(struct weston_motion_filter *filter, |
|
||||||
struct weston_motion_params *motion, |
|
||||||
void *data, uint32_t time) |
|
||||||
{ |
|
||||||
struct pointer_accelerator *accel = |
|
||||||
(struct pointer_accelerator *) filter; |
|
||||||
double velocity; |
|
||||||
double accel_value; |
|
||||||
|
|
||||||
feed_trackers(accel, motion->dx, motion->dy, time); |
|
||||||
velocity = calculate_velocity(accel, time); |
|
||||||
accel_value = calculate_acceleration(accel, data, velocity, time); |
|
||||||
|
|
||||||
motion->dx = accel_value * motion->dx; |
|
||||||
motion->dy = accel_value * motion->dy; |
|
||||||
|
|
||||||
apply_softening(accel, motion); |
|
||||||
|
|
||||||
accel->last_dx = motion->dx; |
|
||||||
accel->last_dy = motion->dy; |
|
||||||
|
|
||||||
accel->last_velocity = velocity; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
accelerator_destroy(struct weston_motion_filter *filter) |
|
||||||
{ |
|
||||||
struct pointer_accelerator *accel = |
|
||||||
(struct pointer_accelerator *) filter; |
|
||||||
|
|
||||||
free(accel->trackers); |
|
||||||
free(accel); |
|
||||||
} |
|
||||||
|
|
||||||
struct weston_motion_filter_interface accelerator_interface = { |
|
||||||
accelerator_filter, |
|
||||||
accelerator_destroy |
|
||||||
}; |
|
||||||
|
|
||||||
WL_EXPORT struct weston_motion_filter * |
|
||||||
create_pointer_accelator_filter(accel_profile_func_t profile) |
|
||||||
{ |
|
||||||
struct pointer_accelerator *filter; |
|
||||||
|
|
||||||
filter = malloc(sizeof *filter); |
|
||||||
if (filter == NULL) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
filter->base.interface = &accelerator_interface; |
|
||||||
|
|
||||||
filter->profile = profile; |
|
||||||
filter->last_velocity = 0.0; |
|
||||||
filter->last_dx = 0; |
|
||||||
filter->last_dy = 0; |
|
||||||
|
|
||||||
filter->trackers = |
|
||||||
calloc(NUM_POINTER_TRACKERS, sizeof *filter->trackers); |
|
||||||
filter->cur_tracker = 0; |
|
||||||
|
|
||||||
return &filter->base; |
|
||||||
} |
|
@ -1,63 +0,0 @@ |
|||||||
/*
|
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _FILTER_H_ |
|
||||||
#define _FILTER_H_ |
|
||||||
|
|
||||||
#include "config.h" |
|
||||||
|
|
||||||
#include <wayland-util.h> |
|
||||||
|
|
||||||
#include "compositor.h" |
|
||||||
|
|
||||||
struct weston_motion_params { |
|
||||||
double dx, dy; |
|
||||||
}; |
|
||||||
|
|
||||||
struct weston_motion_filter; |
|
||||||
|
|
||||||
void |
|
||||||
weston_filter_dispatch(struct weston_motion_filter *filter, |
|
||||||
struct weston_motion_params *motion, |
|
||||||
void *data, uint32_t time); |
|
||||||
|
|
||||||
|
|
||||||
struct weston_motion_filter_interface { |
|
||||||
void (*filter)(struct weston_motion_filter *filter, |
|
||||||
struct weston_motion_params *motion, |
|
||||||
void *data, uint32_t time); |
|
||||||
void (*destroy)(struct weston_motion_filter *filter); |
|
||||||
}; |
|
||||||
|
|
||||||
struct weston_motion_filter { |
|
||||||
struct weston_motion_filter_interface *interface; |
|
||||||
}; |
|
||||||
|
|
||||||
typedef double (*accel_profile_func_t)(struct weston_motion_filter *filter, |
|
||||||
void *data, |
|
||||||
double velocity, |
|
||||||
uint32_t time); |
|
||||||
|
|
||||||
struct weston_motion_filter * |
|
||||||
create_pointer_accelator_filter(accel_profile_func_t filter); |
|
||||||
|
|
||||||
#endif // _FILTER_H_
|
|
@ -1,34 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2014 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _UDEV_INPUT_H_ |
|
||||||
#define _UDEV_INPUT_H_ |
|
||||||
|
|
||||||
#include "config.h" |
|
||||||
|
|
||||||
#ifdef BUILD_LIBINPUT_BACKEND |
|
||||||
#include "libinput-seat.h" |
|
||||||
#else |
|
||||||
#include "udev-seat.h" |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,417 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2013 Intel Corporation |
|
||||||
* |
|
||||||
* 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" |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <string.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <fcntl.h> |
|
||||||
|
|
||||||
#include "compositor.h" |
|
||||||
#include "launcher-util.h" |
|
||||||
#include "evdev.h" |
|
||||||
#include "udev-seat.h" |
|
||||||
|
|
||||||
static const char default_seat[] = "seat0"; |
|
||||||
static const char default_seat_name[] = "default"; |
|
||||||
|
|
||||||
static void |
|
||||||
udev_seat_destroy(struct udev_seat *seat); |
|
||||||
|
|
||||||
static int |
|
||||||
device_added(struct udev_device *udev_device, struct udev_input *input) |
|
||||||
{ |
|
||||||
struct weston_compositor *c; |
|
||||||
struct evdev_device *device; |
|
||||||
struct weston_output *output; |
|
||||||
const char *devnode; |
|
||||||
const char *device_seat, *seat_name, *output_name; |
|
||||||
const char *calibration_values; |
|
||||||
int fd; |
|
||||||
struct udev_seat *seat; |
|
||||||
|
|
||||||
device_seat = udev_device_get_property_value(udev_device, "ID_SEAT"); |
|
||||||
if (!device_seat) |
|
||||||
device_seat = default_seat; |
|
||||||
|
|
||||||
if (strcmp(device_seat, input->seat_id)) |
|
||||||
return 0; |
|
||||||
|
|
||||||
c = input->compositor; |
|
||||||
devnode = udev_device_get_devnode(udev_device); |
|
||||||
|
|
||||||
/* Search for matching logical seat */ |
|
||||||
seat_name = udev_device_get_property_value(udev_device, "WL_SEAT"); |
|
||||||
if (!seat_name) |
|
||||||
seat_name = default_seat_name; |
|
||||||
|
|
||||||
seat = udev_seat_get_named(input, seat_name); |
|
||||||
|
|
||||||
if (seat == NULL) |
|
||||||
return -1; |
|
||||||
|
|
||||||
/* Use non-blocking mode so that we can loop on read on
|
|
||||||
* evdev_device_data() until all events on the fd are |
|
||||||
* read. mtdev_get() also expects this. */ |
|
||||||
fd = weston_launcher_open(c->launcher, devnode, O_RDWR | O_NONBLOCK); |
|
||||||
if (fd < 0) { |
|
||||||
weston_log("opening input device '%s' failed.\n", devnode); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
device = evdev_device_create(&seat->base, devnode, fd); |
|
||||||
if (device == EVDEV_UNHANDLED_DEVICE) { |
|
||||||
weston_launcher_close(c->launcher, fd); |
|
||||||
weston_log("not using input device '%s'.\n", devnode); |
|
||||||
return 0; |
|
||||||
} else if (device == NULL) { |
|
||||||
weston_launcher_close(c->launcher, fd); |
|
||||||
weston_log("failed to create input device '%s'.\n", devnode); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
calibration_values = |
|
||||||
udev_device_get_property_value(udev_device, |
|
||||||
"WL_CALIBRATION"); |
|
||||||
|
|
||||||
if (calibration_values && sscanf(calibration_values, |
|
||||||
"%f %f %f %f %f %f", |
|
||||||
&device->abs.calibration[0], |
|
||||||
&device->abs.calibration[1], |
|
||||||
&device->abs.calibration[2], |
|
||||||
&device->abs.calibration[3], |
|
||||||
&device->abs.calibration[4], |
|
||||||
&device->abs.calibration[5]) == 6) { |
|
||||||
device->abs.apply_calibration = 1; |
|
||||||
weston_log ("Applying calibration: %f %f %f %f %f %f\n", |
|
||||||
device->abs.calibration[0], |
|
||||||
device->abs.calibration[1], |
|
||||||
device->abs.calibration[2], |
|
||||||
device->abs.calibration[3], |
|
||||||
device->abs.calibration[4], |
|
||||||
device->abs.calibration[5]); |
|
||||||
} |
|
||||||
|
|
||||||
wl_list_insert(seat->devices_list.prev, &device->link); |
|
||||||
|
|
||||||
if (seat->base.output && seat->base.pointer) |
|
||||||
weston_pointer_clamp(seat->base.pointer, |
|
||||||
&seat->base.pointer->x, |
|
||||||
&seat->base.pointer->y); |
|
||||||
|
|
||||||
output_name = udev_device_get_property_value(udev_device, "WL_OUTPUT"); |
|
||||||
if (output_name) { |
|
||||||
device->output_name = strdup(output_name); |
|
||||||
wl_list_for_each(output, &c->output_list, link) |
|
||||||
if (strcmp(output->name, device->output_name) == 0) |
|
||||||
evdev_device_set_output(device, output); |
|
||||||
} else if (device->output == NULL && !wl_list_empty(&c->output_list)) { |
|
||||||
output = container_of(c->output_list.next, |
|
||||||
struct weston_output, link); |
|
||||||
evdev_device_set_output(device, output); |
|
||||||
} |
|
||||||
|
|
||||||
if (input->enabled == 1) |
|
||||||
weston_seat_repick(&seat->base); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
udev_input_add_devices(struct udev_input *input, struct udev *udev) |
|
||||||
{ |
|
||||||
struct udev_enumerate *e; |
|
||||||
struct udev_list_entry *entry; |
|
||||||
struct udev_device *device; |
|
||||||
const char *path, *sysname; |
|
||||||
struct udev_seat *seat; |
|
||||||
int devices_found = 0; |
|
||||||
|
|
||||||
e = udev_enumerate_new(udev); |
|
||||||
udev_enumerate_add_match_subsystem(e, "input"); |
|
||||||
udev_enumerate_scan_devices(e); |
|
||||||
udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) { |
|
||||||
path = udev_list_entry_get_name(entry); |
|
||||||
device = udev_device_new_from_syspath(udev, path); |
|
||||||
|
|
||||||
sysname = udev_device_get_sysname(device); |
|
||||||
if (strncmp("event", sysname, 5) != 0) { |
|
||||||
udev_device_unref(device); |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
if (device_added(device, input) < 0) { |
|
||||||
udev_device_unref(device); |
|
||||||
udev_enumerate_unref(e); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
udev_device_unref(device); |
|
||||||
} |
|
||||||
udev_enumerate_unref(e); |
|
||||||
|
|
||||||
wl_list_for_each(seat, &input->compositor->seat_list, base.link) { |
|
||||||
evdev_notify_keyboard_focus(&seat->base, &seat->devices_list); |
|
||||||
|
|
||||||
if (!wl_list_empty(&seat->devices_list)) |
|
||||||
devices_found = 1; |
|
||||||
} |
|
||||||
|
|
||||||
if (devices_found == 0) { |
|
||||||
weston_log( |
|
||||||
"warning: no input devices on entering Weston. " |
|
||||||
"Possible causes:\n" |
|
||||||
"\t- no permissions to read /dev/input/event*\n" |
|
||||||
"\t- seats misconfigured " |
|
||||||
"(Weston backend option 'seat', " |
|
||||||
"udev device property ID_SEAT)\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static int |
|
||||||
evdev_udev_handler(int fd, uint32_t mask, void *data) |
|
||||||
{ |
|
||||||
struct udev_input *input = data; |
|
||||||
struct udev_device *udev_device; |
|
||||||
struct evdev_device *device, *next; |
|
||||||
const char *action; |
|
||||||
const char *devnode; |
|
||||||
struct udev_seat *seat; |
|
||||||
|
|
||||||
udev_device = udev_monitor_receive_device(input->udev_monitor); |
|
||||||
if (!udev_device) |
|
||||||
return 1; |
|
||||||
|
|
||||||
action = udev_device_get_action(udev_device); |
|
||||||
if (!action) |
|
||||||
goto out; |
|
||||||
|
|
||||||
if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0) |
|
||||||
goto out; |
|
||||||
|
|
||||||
if (!strcmp(action, "add")) { |
|
||||||
device_added(udev_device, input); |
|
||||||
} |
|
||||||
else if (!strcmp(action, "remove")) { |
|
||||||
devnode = udev_device_get_devnode(udev_device); |
|
||||||
wl_list_for_each(seat, &input->compositor->seat_list, base.link) { |
|
||||||
wl_list_for_each_safe(device, next, &seat->devices_list, link) |
|
||||||
if (!strcmp(device->devnode, devnode)) { |
|
||||||
weston_log("input device %s, %s removed\n", |
|
||||||
device->devname, device->devnode); |
|
||||||
weston_launcher_close(input->compositor->launcher, |
|
||||||
device->fd); |
|
||||||
evdev_device_destroy(device); |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
out: |
|
||||||
udev_device_unref(udev_device); |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
int |
|
||||||
udev_input_enable(struct udev_input *input) |
|
||||||
{ |
|
||||||
struct wl_event_loop *loop; |
|
||||||
struct weston_compositor *c = input->compositor; |
|
||||||
int fd; |
|
||||||
|
|
||||||
input->udev_monitor = udev_monitor_new_from_netlink(input->udev, "udev"); |
|
||||||
if (!input->udev_monitor) { |
|
||||||
weston_log("udev: failed to create the udev monitor\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
udev_monitor_filter_add_match_subsystem_devtype(input->udev_monitor, |
|
||||||
"input", NULL); |
|
||||||
|
|
||||||
if (udev_monitor_enable_receiving(input->udev_monitor)) { |
|
||||||
weston_log("udev: failed to bind the udev monitor\n"); |
|
||||||
udev_monitor_unref(input->udev_monitor); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
loop = wl_display_get_event_loop(c->wl_display); |
|
||||||
fd = udev_monitor_get_fd(input->udev_monitor); |
|
||||||
input->udev_monitor_source = |
|
||||||
wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, |
|
||||||
evdev_udev_handler, input); |
|
||||||
if (!input->udev_monitor_source) { |
|
||||||
udev_monitor_unref(input->udev_monitor); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
if (udev_input_add_devices(input, input->udev) < 0) |
|
||||||
return -1; |
|
||||||
|
|
||||||
input->enabled = 1; |
|
||||||
|
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
udev_input_remove_devices(struct udev_input *input) |
|
||||||
{ |
|
||||||
struct evdev_device *device, *next; |
|
||||||
struct udev_seat *seat; |
|
||||||
|
|
||||||
wl_list_for_each(seat, &input->compositor->seat_list, base.link) { |
|
||||||
wl_list_for_each_safe(device, next, &seat->devices_list, link) { |
|
||||||
weston_launcher_close(input->compositor->launcher, |
|
||||||
device->fd); |
|
||||||
evdev_device_destroy(device); |
|
||||||
} |
|
||||||
|
|
||||||
if (seat->base.keyboard) |
|
||||||
notify_keyboard_focus_out(&seat->base); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
udev_input_disable(struct udev_input *input) |
|
||||||
{ |
|
||||||
if (!input->udev_monitor) |
|
||||||
return; |
|
||||||
|
|
||||||
udev_monitor_unref(input->udev_monitor); |
|
||||||
input->udev_monitor = NULL; |
|
||||||
wl_event_source_remove(input->udev_monitor_source); |
|
||||||
input->udev_monitor_source = NULL; |
|
||||||
|
|
||||||
udev_input_remove_devices(input); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
int |
|
||||||
udev_input_init(struct udev_input *input, struct weston_compositor *c, struct udev *udev, |
|
||||||
const char *seat_id) |
|
||||||
{ |
|
||||||
memset(input, 0, sizeof *input); |
|
||||||
input->seat_id = strdup(seat_id); |
|
||||||
input->compositor = c; |
|
||||||
input->udev = udev; |
|
||||||
input->udev = udev_ref(udev); |
|
||||||
if (udev_input_enable(input) < 0) |
|
||||||
goto err; |
|
||||||
|
|
||||||
return 0; |
|
||||||
|
|
||||||
err: |
|
||||||
free(input->seat_id); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
void |
|
||||||
udev_input_destroy(struct udev_input *input) |
|
||||||
{ |
|
||||||
struct udev_seat *seat, *next; |
|
||||||
udev_input_disable(input); |
|
||||||
wl_list_for_each_safe(seat, next, &input->compositor->seat_list, base.link) |
|
||||||
udev_seat_destroy(seat); |
|
||||||
udev_unref(input->udev); |
|
||||||
free(input->seat_id); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
drm_led_update(struct weston_seat *seat_base, enum weston_led leds) |
|
||||||
{ |
|
||||||
struct udev_seat *seat = (struct udev_seat *) seat_base; |
|
||||||
struct evdev_device *device; |
|
||||||
|
|
||||||
wl_list_for_each(device, &seat->devices_list, link) |
|
||||||
evdev_led_update(device, leds); |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
notify_output_create(struct wl_listener *listener, void *data) |
|
||||||
{ |
|
||||||
struct udev_seat *seat = container_of(listener, struct udev_seat, |
|
||||||
output_create_listener); |
|
||||||
struct evdev_device *device; |
|
||||||
struct weston_output *output = data; |
|
||||||
|
|
||||||
wl_list_for_each(device, &seat->devices_list, link) { |
|
||||||
if (device->output_name && |
|
||||||
strcmp(output->name, device->output_name) == 0) { |
|
||||||
evdev_device_set_output(device, output); |
|
||||||
} |
|
||||||
|
|
||||||
if (device->output_name == NULL && device->output == NULL) |
|
||||||
evdev_device_set_output(device, output); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static struct udev_seat * |
|
||||||
udev_seat_create(struct udev_input *input, const char *seat_name) |
|
||||||
{ |
|
||||||
struct weston_compositor *c = input->compositor; |
|
||||||
struct udev_seat *seat; |
|
||||||
|
|
||||||
seat = zalloc(sizeof *seat); |
|
||||||
if (!seat) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
weston_seat_init(&seat->base, c, seat_name); |
|
||||||
seat->base.led_update = drm_led_update; |
|
||||||
|
|
||||||
seat->output_create_listener.notify = notify_output_create; |
|
||||||
wl_signal_add(&c->output_created_signal, |
|
||||||
&seat->output_create_listener); |
|
||||||
|
|
||||||
wl_list_init(&seat->devices_list); |
|
||||||
return seat; |
|
||||||
} |
|
||||||
|
|
||||||
static void |
|
||||||
udev_seat_destroy(struct udev_seat *seat) |
|
||||||
{ |
|
||||||
weston_seat_release(&seat->base); |
|
||||||
wl_list_remove(&seat->output_create_listener.link); |
|
||||||
free(seat); |
|
||||||
} |
|
||||||
|
|
||||||
struct udev_seat * |
|
||||||
udev_seat_get_named(struct udev_input *input, const char *seat_name) |
|
||||||
{ |
|
||||||
struct weston_compositor *c = input->compositor; |
|
||||||
struct udev_seat *seat; |
|
||||||
|
|
||||||
wl_list_for_each(seat, &c->seat_list, base.link) { |
|
||||||
if (strcmp(seat->base.seat_name, seat_name) == 0) |
|
||||||
return seat; |
|
||||||
} |
|
||||||
|
|
||||||
seat = udev_seat_create(input, seat_name); |
|
||||||
|
|
||||||
if (!seat) |
|
||||||
return NULL; |
|
||||||
|
|
||||||
return seat; |
|
||||||
} |
|
@ -1,57 +0,0 @@ |
|||||||
/*
|
|
||||||
* Copyright © 2013 Intel Corporation |
|
||||||
* |
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
|
|
||||||
#ifndef _UDEV_SEAT_H_ |
|
||||||
#define _UDEV_SEAT_H_ |
|
||||||
|
|
||||||
#include "config.h" |
|
||||||
|
|
||||||
#include <libudev.h> |
|
||||||
|
|
||||||
#include "compositor.h" |
|
||||||
|
|
||||||
struct udev_seat { |
|
||||||
struct weston_seat base; |
|
||||||
struct wl_list devices_list; |
|
||||||
struct wl_listener output_create_listener; |
|
||||||
}; |
|
||||||
|
|
||||||
struct udev_input { |
|
||||||
struct udev *udev; |
|
||||||
struct udev_monitor *udev_monitor; |
|
||||||
struct wl_event_source *udev_monitor_source; |
|
||||||
char *seat_id; |
|
||||||
struct weston_compositor *compositor; |
|
||||||
int enabled; |
|
||||||
}; |
|
||||||
|
|
||||||
int udev_input_enable(struct udev_input *input); |
|
||||||
void udev_input_disable(struct udev_input *input); |
|
||||||
int udev_input_init(struct udev_input *input, |
|
||||||
struct weston_compositor *c, |
|
||||||
struct udev *udev, |
|
||||||
const char *seat_id); |
|
||||||
void udev_input_destroy(struct udev_input *input); |
|
||||||
|
|
||||||
struct udev_seat *udev_seat_get_named(struct udev_input *input, |
|
||||||
const char *seat_name); |
|
||||||
#endif |
|
Loading…
Reference in new issue