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