Merge branch 'master' into udev

This commit is contained in:
Jonas Ådahl 2013-11-19 22:29:38 +01:00
commit 97a277a9f0
19 changed files with 3180 additions and 0 deletions

26
.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
*.o
*.pc
*.la
*.lo
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
src/.libs/
src/.deps/
src/Makefile
src/Makefile.in
src/libinput-version.h

3
Makefile.am Normal file
View file

@ -0,0 +1,3 @@
SUBDIRS = src
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}

9
README Normal file
View file

@ -0,0 +1,9 @@
This library does processing on input device events while providing an API
to the the user used for delegating more useful input events.
Input event processing includes scaling touch coordinates, generating
pointer events from touchpads, pointer acceleration, etc.
It is based on the input code from the weston Wayland reference compositor.
It has no other dependencies than libmtdev and supports only evdev devices.

9
autogen.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
test -n "$srcdir" || srcdir=`dirname "$0"`
test -n "$srcdir" || srcdir=.
(
cd "$srcdir" &&
autoreconf --force -v --install
) || exit
test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"

56
configure.ac Normal file
View file

@ -0,0 +1,56 @@
AC_PREREQ([2.64])
m4_define([libinput_major_version], [0])
m4_define([libinput_minor_version], [0])
m4_define([libinput_micro_version], [90])
m4_define([libinput_version],
[libinput_major_version.libinput_minor_version.libinput_micro_version])
AC_INIT([libinput],
[libinput_version],
[http://nobugtracker],
[libinput],
[http://nohomepage])
AC_SUBST([LIBINPUT_VERSION_MAJOR], [libinput_major_version])
AC_SUBST([LIBINPUT_VERSION_MINOR], [libinput_minor_version])
AC_SUBST([LIBINPUT_VERSION_MICRO], [libinput_micro_version])
AC_SUBST([LIBINPUT_VERSION], [libinput_version])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz])
AM_SILENT_RULES([yes])
# Check for programs
AC_PROG_CC
# Initialize libtool
LT_PREREQ([2.2])
LT_INIT
AC_CHECK_DECL(EPOLL_CLOEXEC, [],
[AC_MSG_ERROR("EPOLL_CLOEXEC is needed to compile libinput")],
[[#include <sys/epoll.h>]])
AC_CHECK_DECL(TFD_CLOEXEC,[],
[AC_MSG_ERROR("TFD_CLOEXEC is needed to compile libinput")],
[[#include <sys/timerfd.h>]])
AC_CHECK_DECL(CLOCK_MONOTONIC,[],
[AC_MSG_ERROR("CLOCK_MONOTONIC is needed to compile libinput")],
[[#include <time.h>]])
PKG_PROG_PKG_CONFIG()
PKG_CHECK_MODULES(MTDEV, [mtdev >= 1.1.0])
if test "x$GCC" = "xyes"; then
GCC_CFLAGS="-Wall -Wextra -Wno-unused-parameter -g -Wstrict-prototypes -Wmissing-prototypes -fvisibility=hidden"
fi
AC_SUBST(GCC_CFLAGS)
AC_CONFIG_FILES([Makefile
src/Makefile
src/libinput.pc
src/libinput-version.h])
AC_OUTPUT

5
m4/.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
libtool.m4
ltoptions.m4
ltsugar.m4
ltversion.m4
lt~obsolete.m4

28
src/Makefile.am Normal file
View file

@ -0,0 +1,28 @@
lib_LTLIBRARIES = libinput.la
include_HEADERS = \
libinput.h
libinput_la_SOURCES = \
libinput.c \
libinput.h \
libinput-util.c \
libinput-util.h \
evdev.c \
evdev.h \
evdev-touchpad.c \
filter.c \
filter.h
libinput_la_LIBADD = $(MTDEV_LIBS)
libinput_la_CFLAGS = $(MTDEV_CFLAGS) \
$(GCC_CFLAGS)
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libinput.pc
AM_CPPFLAGS = $(FFI_CFLAGS)
AM_CFLAGS = $(GCC_CFLAGS)
DISTCLEANFILES = libinput-version.h
EXTRA_DIST = libinput-version.h.in

833
src/evdev-touchpad.c Normal file
View file

@ -0,0 +1,833 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
#include <linux/input.h>
#include <sys/timerfd.h>
#include "evdev.h"
#include "filter.h"
#include "libinput-private.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;
enum fsm_event *events;
size_t events_len;
size_t events_count;
enum fsm_state state;
struct {
int fd;
struct libinput_source *source;
} timer;
} 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 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 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 motion_params motion;
motion.dx = *dx;
motion.dy = *dy;
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)
{
pointer_notify_button(
&touchpad->device->base,
time,
DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
LIBINPUT_POINTER_BUTTON_STATE_PRESSED);
}
static void
notify_button_released(struct touchpad_dispatch *touchpad, uint32_t time)
{
pointer_notify_button(
&touchpad->device->base,
time,
DEFAULT_TOUCHPAD_SINGLE_TAP_BUTTON,
LIBINPUT_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 event;
unsigned int i;
if (!touchpad->fsm.enable)
return;
if (touchpad->fsm.events_count == 0)
return;
for (i = 0; i < touchpad->fsm.events_count; ++i) {
event = touchpad->fsm.events[i];
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:
touchpad->fsm.state = FSM_IDLE;
break;
}
}
if (timeout != UINT32_MAX) {
struct itimerspec its;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = timeout / 1000;
its.it_value.tv_nsec = (timeout % 1000) * 1000 * 1000;
timerfd_settime(touchpad->fsm.timer.fd, 0, &its, NULL);
}
touchpad->fsm.events_count = 0;
}
static void
push_fsm_event(struct touchpad_dispatch *touchpad,
enum fsm_event event)
{
enum fsm_event *events;
size_t new_len = touchpad->fsm.events_len;
if (!touchpad->fsm.enable)
return;
if (touchpad->fsm.events_count + 1 >= touchpad->fsm.events_len) {
if (new_len == 0)
new_len = 4;
else
new_len *= 2;
events = realloc(touchpad->fsm.events,
sizeof(enum fsm_event) * new_len);
if (!events) {
touchpad->fsm.state = FSM_IDLE;
return;
}
touchpad->fsm.events = events;
touchpad->fsm.events_len = new_len;
}
touchpad->fsm.events[touchpad->fsm.events_count++] = event;
}
static void
fsm_timeout_handler(void *data)
{
struct touchpad_dispatch *touchpad = data;
uint64_t expires;
int len;
struct timespec ts;
uint32_t now;
len = read(touchpad->fsm.timer.fd, &expires, sizeof expires);
if (len != sizeof expires)
/* This will only happen if the application made the fd
* non-blocking, but this function should only be called
* upon the timeout, so lets continue anyway. */
fprintf(stderr, "timerfd read error: %m\n");
if (touchpad->fsm.events_count == 0) {
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
push_fsm_event(touchpad, FSM_EVENT_TIMEOUT);
process_fsm_events(touchpad, now);
}
}
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;
struct libinput_device *base = &touchpad->device->base;
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) {
pointer_notify_motion(
base,
time,
li_fixed_from_double(dx),
li_fixed_from_double(dy));
} else if (touchpad->finger_state == TOUCHPAD_FINGERS_TWO) {
if (dx != 0.0)
pointer_notify_axis(
base,
time,
LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL,
li_fixed_from_double(dx));
if (dy != 0.0)
pointer_notify_axis(
base,
time,
LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL,
li_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;
pointer_notify_button(
&touchpad->device->base,
time,
code,
e->value ? LIBINPUT_POINTER_BUTTON_STATE_PRESSED :
LIBINPUT_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;
struct libinput *libinput = touchpad->device->base.libinput;
touchpad->filter->interface->destroy(touchpad->filter);
libinput_remove_source(libinput, touchpad->fsm.timer.source);
free(touchpad->fsm.events);
free(dispatch);
}
struct evdev_dispatch_interface touchpad_interface = {
touchpad_process,
touchpad_destroy
};
static int
touchpad_init(struct touchpad_dispatch *touchpad,
struct evdev_device *device)
{
struct motion_filter *accel;
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);
/* Set default parameters */
touchpad->constant_accel_factor =
DEFAULT_CONSTANT_ACCEL_NUMERATOR / diagonal;
touchpad->min_accel_factor = DEFAULT_MIN_ACCEL_FACTOR;
touchpad->max_accel_factor = DEFAULT_MAX_ACCEL_FACTOR;
touchpad->hysteresis.margin_x =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
touchpad->hysteresis.margin_y =
diagonal / DEFAULT_HYSTERESIS_MARGIN_DENOMINATOR;
touchpad->hysteresis.center_x = 0;
touchpad->hysteresis.center_y = 0;
/* Configure acceleration profile */
accel = create_pointer_accelator_filter(touchpad_profile);
if (accel == NULL)
return -1;
touchpad->filter = accel;
/* Setup initial state */
touchpad->reset = 1;
memset(touchpad->motion_history, 0, sizeof touchpad->motion_history);
touchpad->motion_index = 0;
touchpad->motion_count = 0;
touchpad->state = TOUCHPAD_STATE_NONE;
touchpad->last_finger_state = 0;
touchpad->finger_state = 0;
touchpad->fsm.events = NULL;
touchpad->fsm.events_count = 0;
touchpad->fsm.events_len = 0;
touchpad->fsm.state = FSM_IDLE;
touchpad->fsm.timer.fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
touchpad->fsm.timer.source =
libinput_add_fd(touchpad->device->base.libinput,
touchpad->fsm.timer.fd,
fsm_timeout_handler,
touchpad);
if (touchpad->fsm.timer.source == NULL) {
close(touchpad->fsm.timer.fd);
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;
}

714
src/evdev.c Normal file
View file

@ -0,0 +1,714 @@
/*
* Copyright © 2010 Intel Corporation
* Copyright © 2013 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 <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 "libinput.h"
#include "evdev.h"
#include "libinput-private.h"
#define DEFAULT_AXIS_STEP_DISTANCE li_fixed_from_int(10)
void
evdev_device_led_update(struct evdev_device *device, enum libinput_led leds)
{
static const struct {
enum libinput_led weston;
int evdev;
} map[] = {
{ LIBINPUT_LED_NUM_LOCK, LED_NUML },
{ LIBINPUT_LED_CAPS_LOCK, LED_CAPSL },
{ LIBINPUT_LED_SCROLL_LOCK, LED_SCROLLL },
};
struct input_event ev[ARRAY_LENGTH(map) + 1];
unsigned int i;
if (!device->caps & EVDEV_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)
{
int32_t cx, cy;
int slot;
struct libinput_device *base = &device->base;
slot = device->mt.slot;
switch (device->pending_event) {
case EVDEV_NONE:
return;
case EVDEV_RELATIVE_MOTION:
pointer_notify_motion(base,
time,
device->rel.dx,
device->rel.dy);
device->rel.dx = 0;
device->rel.dy = 0;
goto handled;
case EVDEV_ABSOLUTE_MT_DOWN:
touch_notify_touch(base,
time,
slot,
li_fixed_from_int(device->mt.slots[slot].x),
li_fixed_from_int(device->mt.slots[slot].y),
LIBINPUT_TOUCH_TYPE_DOWN);
goto handled;
case EVDEV_ABSOLUTE_MT_MOTION:
touch_notify_touch(base,
time,
slot,
li_fixed_from_int(device->mt.slots[slot].x),
li_fixed_from_int(device->mt.slots[slot].y),
LIBINPUT_TOUCH_TYPE_MOTION);
goto handled;
case EVDEV_ABSOLUTE_MT_UP:
touch_notify_touch(base,
time,
slot,
0, 0,
LIBINPUT_TOUCH_TYPE_UP);
goto handled;
case EVDEV_ABSOLUTE_TOUCH_DOWN:
transform_absolute(device, &cx, &cy);
touch_notify_touch(base,
time,
slot,
li_fixed_from_int(cx),
li_fixed_from_int(cy),
LIBINPUT_TOUCH_TYPE_DOWN);
goto handled;
case EVDEV_ABSOLUTE_MOTION:
transform_absolute(device, &cx, &cy);
if (device->caps & EVDEV_TOUCH) {
touch_notify_touch(base,
time,
slot,
li_fixed_from_int(cx),
li_fixed_from_int(cy),
LIBINPUT_TOUCH_TYPE_DOWN);
} else {
pointer_notify_motion_absolute(base,
time,
li_fixed_from_int(cx),
li_fixed_from_int(cy));
}
goto handled;
case EVDEV_ABSOLUTE_TOUCH_UP:
touch_notify_touch(base,
time,
0, 0, 0, LIBINPUT_TOUCH_TYPE_UP);
goto handled;
}
assert(0 && "Unknown pending event type");
handled:
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:
pointer_notify_button(
&device->base,
time,
e->code,
e->value ? LIBINPUT_POINTER_BUTTON_STATE_PRESSED :
LIBINPUT_POINTER_BUTTON_STATE_RELEASED);
break;
default:
keyboard_notify_key(
&device->base,
time,
e->code,
e->value ? LIBINPUT_KEYBOARD_KEY_STATE_PRESSED :
LIBINPUT_KEYBOARD_KEY_STATE_RELEASED);
break;
}
}
static void
evdev_process_touch(struct evdev_device *device,
struct input_event *e,
uint32_t time)
{
struct libinput *libinput = device->base.libinput;
int screen_width;
int screen_height;
libinput->interface->get_current_screen_dimensions(
&device->base,
&screen_width,
&screen_height,
libinput->user_data);
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)
{
struct libinput *libinput = device->base.libinput;
int screen_width;
int screen_height;
libinput->interface->get_current_screen_dimensions(
&device->base,
&screen_width,
&screen_height,
libinput->user_data);
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)
{
struct libinput_device *base = &device->base;
switch (e->code) {
case REL_X:
if (device->pending_event != EVDEV_RELATIVE_MOTION)
evdev_flush_pending_event(device, time);
device->rel.dx += li_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 += li_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 */
pointer_notify_axis(
base,
time,
LIBINPUT_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 */
pointer_notify_axis(
base,
time,
LIBINPUT_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 void
evdev_device_dispatch(void *data)
{
struct evdev_device *device = data;
int fd = device->fd;
struct input_event ev[32];
int len;
/* 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) {
libinput_remove_source(device->base.libinput,
device->source);
device->source = NULL;
}
return;
}
evdev_process_events(device, ev, len / sizeof ev[0]);
} while (len > 0);
}
static int
evdev_handle_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_key, has_abs;
unsigned int i;
has_key = 0;
has_abs = 0;
device->caps = 0;
ioctl(device->fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits);
if (TEST_BIT(ev_bits, EV_ABS)) {
has_abs = 1;
ioctl(device->fd, EVIOCGBIT(EV_ABS, sizeof(abs_bits)),
abs_bits);
if (TEST_BIT(abs_bits, ABS_WHEEL) ||
TEST_BIT(abs_bits, ABS_GAS) ||
TEST_BIT(abs_bits, ABS_BRAKE) ||
TEST_BIT(abs_bits, ABS_HAT0X)) {
/* Device %s is a joystick, ignoring. */
return 0;
}
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;
device->caps |= EVDEV_MOTION_ABS;
}
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;
device->caps |= EVDEV_MOTION_ABS;
}
/* 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;
device->caps |= EVDEV_TOUCH;
if (!TEST_BIT(abs_bits, ABS_MT_SLOT)) {
device->mtdev = mtdev_new_open(device->fd);
if (!device->mtdev) {
/* mtdev required but failed to open. */
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))
device->caps |= EVDEV_MOTION_REL;
}
if (TEST_BIT(ev_bits, EV_KEY)) {
has_key = 1;
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) {
device->dispatch = evdev_touchpad_create(device);
}
for (i = KEY_ESC; i < KEY_MAX; i++) {
if (i >= BTN_MISC && i < KEY_OK)
continue;
if (TEST_BIT(key_bits, i)) {
device->caps |= EVDEV_KEYBOARD;
break;
}
}
if (TEST_BIT(key_bits, BTN_TOUCH)) {
device->caps |= EVDEV_TOUCH;
}
for (i = BTN_MISC; i < BTN_JOYSTICK; i++) {
if (TEST_BIT(key_bits, i)) {
device->caps |= EVDEV_BUTTON;
device->caps &= ~EVDEV_TOUCH;
break;
}
}
}
if (TEST_BIT(ev_bits, EV_LED)) {
device->caps |= EVDEV_KEYBOARD;
}
/* This rule tries to catch accelerometer devices and opt out. We may
* want to adjust the protocol later adding a proper event for dealing
* with accelerometers and implement here accordingly */
if (has_abs && !has_key && !device->is_mt) {
return 0;
}
return 1;
}
static int
evdev_configure_device(struct evdev_device *device)
{
if ((device->caps & (EVDEV_MOTION_ABS | EVDEV_MOTION_REL)) &&
(device->caps & EVDEV_BUTTON)) {
device_register_capability(&device->base,
LIBINPUT_DEVICE_CAP_POINTER);
device->seat_caps |= EVDEV_DEVICE_POINTER;
}
if ((device->caps & EVDEV_KEYBOARD)) {
device_register_capability(&device->base,
LIBINPUT_DEVICE_CAP_KEYBOARD);
device->seat_caps |= EVDEV_DEVICE_KEYBOARD;
}
if ((device->caps & EVDEV_TOUCH)) {
device_register_capability(&device->base,
LIBINPUT_DEVICE_CAP_TOUCH);
device->seat_caps |= EVDEV_DEVICE_TOUCH;
}
return 0;
}
LIBINPUT_EXPORT struct libinput_device *
libinput_device_create_evdev(
struct libinput *libinput,
const char *devnode,
int fd,
void *user_data)
{
struct evdev_device *device;
char devname[256] = "unknown";
device = zalloc(sizeof *device);
if (device == NULL)
return NULL;
device->base.libinput = libinput;
device->base.user_data = user_data;
device->seat_caps = 0;
device->is_mt = 0;
device->mtdev = NULL;
device->devnode = strdup(devnode);
device->mt.slot = -1;
device->rel.dx = 0;
device->rel.dy = 0;
device->dispatch = NULL;
device->fd = fd;
device->pending_event = EVDEV_NONE;
ioctl(device->fd, EVIOCGNAME(sizeof(devname)), devname);
devname[sizeof(devname) - 1] = '\0';
device->devname = strdup(devname);
if (!evdev_handle_device(device)) {
evdev_device_destroy(device);
return NULL;
}
if (evdev_configure_device(device) == -1)
goto err;
/* 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 =
libinput_add_fd(libinput, fd, evdev_device_dispatch, device);
if (!device->source)
goto err;
return &device->base;
err:
evdev_device_destroy(device);
return NULL;
}
int
evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size)
{
memset(keys, 0, size);
return ioctl(device->fd, EVIOCGKEY(size), keys);
}
void
evdev_device_calibrate(struct evdev_device *device, float calibration[6])
{
device->abs.apply_calibration = 1;
memcpy(device->abs.calibration, calibration, sizeof calibration);
}
void
evdev_device_terminate(struct evdev_device *device)
{
if (device->seat_caps & EVDEV_DEVICE_POINTER) {
device_unregister_capability(&device->base,
LIBINPUT_DEVICE_CAP_POINTER);
}
if (device->seat_caps & EVDEV_DEVICE_KEYBOARD) {
device_unregister_capability(&device->base,
LIBINPUT_DEVICE_CAP_KEYBOARD);
}
if (device->seat_caps & EVDEV_DEVICE_TOUCH) {
device_unregister_capability(&device->base,
LIBINPUT_DEVICE_CAP_TOUCH);
}
}
void
evdev_device_destroy(struct evdev_device *device)
{
struct evdev_dispatch *dispatch;
dispatch = device->dispatch;
if (dispatch)
dispatch->interface->destroy(dispatch);
if (device->mtdev)
mtdev_close_delete(device->mtdev);
close(device->fd);
free(device->devname);
free(device->devnode);
free(device);
}

153
src/evdev.h Normal file
View file

@ -0,0 +1,153 @@
/*
* Copyright © 2011, 2012 Intel Corporation
* Copyright © 2013 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 EVDEV_H
#define EVDEV_H
#include "config.h"
#include <linux/input.h>
#include "evdev.h"
#include "libinput-private.h"
static inline void *
zalloc(size_t size)
{
return calloc(1, size);
}
#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_capability {
EVDEV_KEYBOARD = (1 << 0),
EVDEV_BUTTON = (1 << 1),
EVDEV_MOTION_ABS = (1 << 2),
EVDEV_MOTION_REL = (1 << 3),
EVDEV_TOUCH = (1 << 4),
};
enum evdev_device_seat_capability {
EVDEV_DEVICE_POINTER = (1 << 0),
EVDEV_DEVICE_KEYBOARD = (1 << 1),
EVDEV_DEVICE_TOUCH = (1 << 2)
};
struct evdev_device {
struct libinput_device base;
struct libinput_source *source;
struct evdev_dispatch *dispatch;
char *devnode;
char *devname;
int fd;
struct {
int min_x, max_x, min_y, max_y;
int32_t x, y;
int apply_calibration;
float calibration[6];
} abs;
struct {
int slot;
struct {
int32_t x, y;
} slots[MAX_SLOTS];
} mt;
struct mtdev *mtdev;
struct {
li_fixed_t dx, dy;
} rel;
enum evdev_event_type pending_event;
enum evdev_device_capability caps;
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_device_proces_event(struct libinput_event *event);
void
evdev_device_led_update(struct evdev_device *device, enum libinput_led leds);
int
evdev_device_get_keys(struct evdev_device *device, char *keys, size_t size);
void
evdev_device_calibrate(struct evdev_device *device, float calibration[6]);
void
evdev_device_terminate(struct evdev_device *terminate);
void
evdev_device_destroy(struct evdev_device *device);
#endif /* EVDEV_H */

334
src/filter.c Normal file
View file

@ -0,0 +1,334 @@
/*
* 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 "filter.h"
void
filter_dispatch(struct motion_filter *filter,
struct 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 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 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 motion_filter *filter,
struct 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 motion_filter *filter)
{
struct pointer_accelerator *accel =
(struct pointer_accelerator *) filter;
free(accel->trackers);
free(accel);
}
struct motion_filter_interface accelerator_interface = {
accelerator_filter,
accelerator_destroy
};
struct 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;
}

62
src/filter.h Normal file
View file

@ -0,0 +1,62 @@
/*
* 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"
struct motion_params {
double dx, dy;
};
struct motion_filter;
void
filter_dispatch(struct motion_filter *filter,
struct motion_params *motion,
void *data, uint32_t time);
struct motion_filter_interface {
void (*filter)(struct motion_filter *filter,
struct motion_params *motion,
void *data, uint32_t time);
void (*destroy)(struct motion_filter *filter);
};
struct motion_filter {
struct motion_filter_interface *interface;
};
struct motion_filter *
create_linear_acceleration_filter(double speed);
typedef double (*accel_profile_func_t)(struct motion_filter *filter,
void *data,
double velocity,
uint32_t time);
struct motion_filter *
create_pointer_accelator_filter(accel_profile_func_t filter);
#endif /* FILTER_H */

113
src/libinput-private.h Normal file
View file

@ -0,0 +1,113 @@
/*
* Copyright © 2013 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 LIBINPUT_PRIVATE_H
#define LIBINPUT_PRIVATE_H
#include "libinput.h"
#include "libinput-util.h"
struct libinput {
int epoll_fd;
struct list source_destroy_list;
struct libinput_event **events;
size_t events_count;
size_t events_len;
size_t events_in;
size_t events_out;
const struct libinput_interface *interface;
void *user_data;
};
struct libinput_device {
struct libinput *libinput;
void *user_data;
int terminated;
};
typedef void (*libinput_source_dispatch_t)(void *data);
struct libinput_source;
struct libinput_source *
libinput_add_fd(struct libinput *libinput,
int fd,
libinput_source_dispatch_t dispatch,
void *data);
void
libinput_remove_source(struct libinput *libinput,
struct libinput_source *source);
void
libinput_post_event(struct libinput *libinput,
struct libinput_event *event);
void
device_register_capability(struct libinput_device *device,
enum libinput_device_capability capability);
void
device_unregister_capability(struct libinput_device *device,
enum libinput_device_capability capability);
void
keyboard_notify_key(struct libinput_device *device,
uint32_t time,
uint32_t key,
enum libinput_keyboard_key_state state);
void
pointer_notify_motion(struct libinput_device *device,
uint32_t time,
li_fixed_t dx,
li_fixed_t dy);
void
pointer_notify_motion_absolute(struct libinput_device *device,
uint32_t time,
li_fixed_t x,
li_fixed_t y);
void
pointer_notify_button(struct libinput_device *device,
uint32_t time,
int32_t button,
enum libinput_pointer_button_state state);
void
pointer_notify_axis(struct libinput_device *device,
uint32_t time,
enum libinput_pointer_axis axis,
li_fixed_t value);
void
touch_notify_touch(struct libinput_device *device,
uint32_t time,
int32_t slot,
li_fixed_t x,
li_fixed_t y,
enum libinput_touch_type touch_type);
#endif /* LIBINPUT_PRIVATE_H */

62
src/libinput-util.c Normal file
View file

@ -0,0 +1,62 @@
/*
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2011 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.
*/
/*
* This list data structure is verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
*/
#include "config.h"
#include "libinput-private.h"
void
list_init(struct list *list)
{
list->prev = list;
list->next = list;
}
void
list_insert(struct list *list, struct list *elm)
{
elm->prev = list;
elm->next = list->next;
list->next = elm;
elm->next->prev = elm;
}
void
list_remove(struct list *elm)
{
elm->prev->next = elm->next;
elm->next->prev = elm->prev;
elm->next = NULL;
elm->prev = NULL;
}
int
list_empty(const struct list *list)
{
return list->next == list;
}

91
src/libinput-util.h Normal file
View file

@ -0,0 +1,91 @@
/*
* Copyright © 2008 Kristian Høgsberg
*
* 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 LIBINPUT_UTIL_H
#define LIBINPUT_UTIL_H
/*
* This list data structure is a verbatim copy from wayland-util.h from the
* Wayland project; except that wl_ prefix has been removed.
*/
struct list {
struct list *prev;
struct list *next;
};
void list_init(struct list *list);
void list_insert(struct list *list, struct list *elm);
void list_remove(struct list *elm);
int list_empty(const struct list *list);
#ifdef __GNUC__
#define container_of(ptr, sample, member) \
(__typeof__(sample))((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#else
#define container_of(ptr, sample, member) \
(void *)((char *)(ptr) - \
((char *)&(sample)->member - (char *)(sample)))
#endif
#define list_for_each(pos, head, member) \
for (pos = 0, pos = container_of((head)->next, pos, member); \
&pos->member != (head); \
pos = container_of(pos->member.next, pos, member))
#define list_for_each_safe(pos, tmp, head, member) \
for (pos = 0, tmp = 0, \
pos = container_of((head)->next, pos, member), \
tmp = container_of((pos)->member.next, tmp, member); \
&pos->member != (head); \
pos = tmp, \
tmp = container_of(pos->member.next, tmp, member))
#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
/*
* This fixed point implementation is a verbatim copy from wayland-util.h from
* the Wayland project, with the wl_ prefix renamed li_.
*/
static inline li_fixed_t li_fixed_from_int(int i)
{
return i * 256;
}
static inline li_fixed_t
li_fixed_from_double(double d)
{
union {
double d;
int64_t i;
} u;
u.d = d + (3LL << (51 - 8));
return u.i;
}
#define LIBINPUT_EXPORT __attribute__ ((visibility("default")))
#endif /* LIBINPUT_UTIL_H */

31
src/libinput-version.h.in Normal file
View file

@ -0,0 +1,31 @@
/*
* Copyright © 2013 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 LIBINPUT_VERSION_H
#define LIBINPUT_VERSION_H
#define LIBINPUT_VERSION_MAJOR @LIBINPUT_VERSION_MAJOR@
#define LIBINPUT_VERSION_MINOR @LIBINPUT_VERSION_MINOR@
#define LIBINPUT_VERSION_MICRO @LIBINPUT_VERSION_MICRO@
#define LIBINPUT_VERSION "@LIBINPUT_VERSION@"
#endif

444
src/libinput.c Normal file
View file

@ -0,0 +1,444 @@
/*
* Copyright © 2013 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <assert.h>
#include "libinput.h"
#include "evdev.h"
#include "libinput-private.h"
struct libinput_source {
libinput_source_dispatch_t dispatch;
void *user_data;
int fd;
struct list link;
};
struct libinput_source *
libinput_add_fd(struct libinput *libinput,
int fd,
libinput_source_dispatch_t dispatch,
void *user_data)
{
struct libinput_source *source;
struct epoll_event ep;
source = malloc(sizeof *source);
if (!source)
return NULL;
source->dispatch = dispatch;
source->user_data = user_data;
source->fd = fd;
memset(&ep, 0, sizeof ep);
ep.events = EPOLLIN;
ep.data.ptr = source;
if (epoll_ctl(libinput->epoll_fd, EPOLL_CTL_ADD, fd, &ep) < 0) {
close(source->fd);
free(source);
return NULL;
}
return source;
}
void
libinput_remove_source(struct libinput *libinput,
struct libinput_source *source)
{
epoll_ctl(libinput->epoll_fd, EPOLL_CTL_DEL, source->fd, NULL);
close(source->fd);
source->fd = -1;
list_insert(&libinput->source_destroy_list, &source->link);
}
LIBINPUT_EXPORT struct libinput *
libinput_create(const struct libinput_interface *interface, void *user_data)
{
struct libinput *libinput;
libinput = zalloc(sizeof *libinput);
if (!libinput)
return NULL;
list_init(&libinput->source_destroy_list);
libinput->interface = interface;
libinput->user_data = user_data;
libinput->epoll_fd = epoll_create1(EPOLL_CLOEXEC);;
if (libinput->epoll_fd < 0)
return NULL;
return libinput;
}
LIBINPUT_EXPORT void
libinput_destroy(struct libinput *libinput)
{
struct libinput_event *event;
while ((event = libinput_get_event(libinput)))
free(event);
free(libinput->events);
close(libinput->epoll_fd);
free(libinput);
}
LIBINPUT_EXPORT int
libinput_get_fd(struct libinput *libinput)
{
return libinput->epoll_fd;
}
LIBINPUT_EXPORT int
libinput_dispatch(struct libinput *libinput)
{
struct libinput_source *source, *next;
struct epoll_event ep[32];
int i, count;
count = epoll_wait(libinput->epoll_fd, ep, ARRAY_LENGTH(ep), 0);
if (count < 0)
return -1;
for (i = 0; i < count; ++i) {
source = ep[i].data.ptr;
if (source->fd == -1)
continue;
source->dispatch(source->user_data);
}
list_for_each_safe(source, next, &libinput->source_destroy_list, link)
free(source);
list_init(&libinput->source_destroy_list);
return 0;
}
static void
init_event_base(struct libinput_event *event,
enum libinput_event_type type,
struct libinput_device *device)
{
event->type = type;
event->device = device;
}
static void
post_device_event(struct libinput_device *device,
enum libinput_event_type type,
struct libinput_event *event)
{
init_event_base(event, type, device);
libinput_post_event(device->libinput, event);
}
void
device_register_capability(struct libinput_device *device,
enum libinput_device_capability capability)
{
struct libinput_event_device_register_capability *capability_event;
capability_event = malloc(sizeof *capability_event);
*capability_event = (struct libinput_event_device_register_capability) {
.capability = capability,
};
post_device_event(device,
LIBINPUT_EVENT_DEVICE_REGISTER_CAPABILITY,
&capability_event->base);
}
void
device_unregister_capability(struct libinput_device *device,
enum libinput_device_capability capability)
{
struct libinput_event_device_unregister_capability *capability_event;
capability_event = malloc(sizeof *capability_event);
*capability_event = (struct libinput_event_device_unregister_capability) {
.capability = capability,
};
post_device_event(device,
LIBINPUT_EVENT_DEVICE_UNREGISTER_CAPABILITY,
&capability_event->base);
}
void
keyboard_notify_key(struct libinput_device *device,
uint32_t time,
uint32_t key,
enum libinput_keyboard_key_state state)
{
struct libinput_event_keyboard_key *key_event;
key_event = malloc(sizeof *key_event);
if (!key_event)
return;
*key_event = (struct libinput_event_keyboard_key) {
.time = time,
.key = key,
.state = state,
};
post_device_event(device,
LIBINPUT_EVENT_KEYBOARD_KEY,
&key_event->base);
}
void
pointer_notify_motion(struct libinput_device *device,
uint32_t time,
li_fixed_t dx,
li_fixed_t dy)
{
struct libinput_event_pointer_motion *motion_event;
motion_event = malloc(sizeof *motion_event);
if (!motion_event)
return;
*motion_event = (struct libinput_event_pointer_motion) {
.time = time,
.dx = dx,
.dy = dy,
};
post_device_event(device,
LIBINPUT_EVENT_POINTER_MOTION,
&motion_event->base);
}
void
pointer_notify_motion_absolute(struct libinput_device *device,
uint32_t time,
li_fixed_t x,
li_fixed_t y)
{
struct libinput_event_pointer_motion_absolute *motion_absolute_event;
motion_absolute_event = malloc(sizeof *motion_absolute_event);
if (!motion_absolute_event)
return;
*motion_absolute_event = (struct libinput_event_pointer_motion_absolute) {
.time = time,
.x = x,
.y = y,
};
post_device_event(device,
LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE,
&motion_absolute_event->base);
}
void
pointer_notify_button(struct libinput_device *device,
uint32_t time,
int32_t button,
enum libinput_pointer_button_state state)
{
struct libinput_event_pointer_button *button_event;
button_event = malloc(sizeof *button_event);
if (!button_event)
return;
*button_event = (struct libinput_event_pointer_button) {
.time = time,
.button = button,
.state = state,
};
post_device_event(device,
LIBINPUT_EVENT_POINTER_BUTTON,
&button_event->base);
}
void
pointer_notify_axis(struct libinput_device *device,
uint32_t time,
enum libinput_pointer_axis axis,
li_fixed_t value)
{
struct libinput_event_pointer_axis *axis_event;
axis_event = malloc(sizeof *axis_event);
if (!axis_event)
return;
*axis_event = (struct libinput_event_pointer_axis) {
.time = time,
.axis = axis,
.value = value,
};
post_device_event(device,
LIBINPUT_EVENT_POINTER_AXIS,
&axis_event->base);
}
void
touch_notify_touch(struct libinput_device *device,
uint32_t time,
int32_t slot,
li_fixed_t x,
li_fixed_t y,
enum libinput_touch_type touch_type)
{
struct libinput_event_touch_touch *touch_event;
touch_event = malloc(sizeof *touch_event);
if (!touch_event)
return;
*touch_event = (struct libinput_event_touch_touch) {
.time = time,
.slot = slot,
.x = x,
.y = y,
.touch_type = touch_type,
};
post_device_event(device,
LIBINPUT_EVENT_TOUCH_TOUCH,
&touch_event->base);
}
void
libinput_post_event(struct libinput *libinput,
struct libinput_event *event)
{
struct libinput_event **events = libinput->events;
size_t events_len = libinput->events_len;
size_t events_count = libinput->events_count;
size_t move_len;
size_t new_out;
events_count++;
if (events_count > events_len) {
if (events_len == 0)
events_len = 4;
else
events_len *= 2;
events = realloc(events, events_len * sizeof *events);
if (!events) {
fprintf(stderr, "Failed to reallocate event ring "
"buffer");
return;
}
if (libinput->events_count > 0 && libinput->events_in == 0) {
libinput->events_in = libinput->events_len;
} else if (libinput->events_count > 0 &&
libinput->events_out >= libinput->events_in) {
move_len = libinput->events_len - libinput->events_out;
new_out = events_len - move_len;
memmove(events + new_out,
libinput->events + libinput->events_out,
move_len * sizeof *events);
libinput->events_out = new_out;
}
libinput->events = events;
libinput->events_len = events_len;
}
libinput->events_count = events_count;
events[libinput->events_in] = event;
libinput->events_in = (libinput->events_in + 1) % libinput->events_len;
}
LIBINPUT_EXPORT struct libinput_event *
libinput_get_event(struct libinput *libinput)
{
struct libinput_event *event;
if (libinput->events_count == 0)
return NULL;
event = libinput->events[libinput->events_out];
libinput->events_out =
(libinput->events_out + 1) % libinput->events_len;
libinput->events_count--;
return event;
}
LIBINPUT_EXPORT void
libinput_device_terminate(struct libinput_device *device)
{
evdev_device_terminate((struct evdev_device *) device);
device->terminated = 1;
}
LIBINPUT_EXPORT void
libinput_device_destroy(struct libinput_device *device)
{
assert(device->terminated);
evdev_device_destroy((struct evdev_device *) device);
}
LIBINPUT_EXPORT void *
libinput_device_get_user_data(struct libinput_device *device)
{
return device->user_data;
}
LIBINPUT_EXPORT void
libinput_device_led_update(struct libinput_device *device,
enum libinput_led leds)
{
evdev_device_led_update((struct evdev_device *) device, leds);
}
LIBINPUT_EXPORT int
libinput_device_get_keys(struct libinput_device *device,
char *keys, size_t size)
{
return evdev_device_get_keys((struct evdev_device *) device,
keys,
size);
}
LIBINPUT_EXPORT void
libinput_device_calibrate(struct libinput_device *device,
float calibration[6])
{
evdev_device_calibrate((struct evdev_device *) device, calibration);
}

195
src/libinput.h Normal file
View file

@ -0,0 +1,195 @@
/*
* Copyright © 2013 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 LIBINPUT_H
#define LIBINPUT_H
#include <stdlib.h>
#include <stdint.h>
typedef int32_t li_fixed_t;
enum libinput_device_capability {
LIBINPUT_DEVICE_CAP_KEYBOARD = 0,
LIBINPUT_DEVICE_CAP_POINTER = 1,
LIBINPUT_DEVICE_CAP_TOUCH = 2,
};
enum libinput_keyboard_key_state {
LIBINPUT_KEYBOARD_KEY_STATE_RELEASED = 0,
LIBINPUT_KEYBOARD_KEY_STATE_PRESSED = 1,
};
enum libinput_led {
LIBINPUT_LED_NUM_LOCK = (1 << 0),
LIBINPUT_LED_CAPS_LOCK = (1 << 1),
LIBINPUT_LED_SCROLL_LOCK = (1 << 2),
};
enum libinput_pointer_button_state {
LIBINPUT_POINTER_BUTTON_STATE_RELEASED = 0,
LIBINPUT_POINTER_BUTTON_STATE_PRESSED = 1,
};
enum libinput_pointer_axis {
LIBINPUT_POINTER_AXIS_VERTICAL_SCROLL = 0,
LIBINPUT_POINTER_AXIS_HORIZONTAL_SCROLL = 1,
};
enum libinput_touch_type {
LIBINPUT_TOUCH_TYPE_DOWN = 0,
LIBINPUT_TOUCH_TYPE_UP = 1,
LIBINPUT_TOUCH_TYPE_MOTION = 2,
LIBINPUT_TOUCH_TYPE_FRAME = 3,
LIBINPUT_TOUCH_TYPE_CANCEL = 4,
};
enum libinput_event_type {
LIBINPUT_EVENT_DEVICE_REGISTER_CAPABILITY = 200,
LIBINPUT_EVENT_DEVICE_UNREGISTER_CAPABILITY,
LIBINPUT_EVENT_KEYBOARD_KEY = 300,
LIBINPUT_EVENT_POINTER_MOTION = 400,
LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE,
LIBINPUT_EVENT_POINTER_BUTTON,
LIBINPUT_EVENT_POINTER_AXIS,
LIBINPUT_EVENT_TOUCH_TOUCH = 500,
};
struct libinput_event {
enum libinput_event_type type;
struct libinput_device *device;
};
struct libinput_event_device_register_capability {
struct libinput_event base;
enum libinput_device_capability capability;
};
struct libinput_event_device_unregister_capability {
struct libinput_event base;
enum libinput_device_capability capability;
};
struct libinput_event_keyboard_key {
struct libinput_event base;
uint32_t time;
uint32_t key;
enum libinput_keyboard_key_state state;
};
struct libinput_event_pointer_motion {
struct libinput_event base;
uint32_t time;
li_fixed_t dx;
li_fixed_t dy;
};
struct libinput_event_pointer_motion_absolute {
struct libinput_event base;
uint32_t time;
li_fixed_t x;
li_fixed_t y;
};
struct libinput_event_pointer_button {
struct libinput_event base;
uint32_t time;
int32_t button;
enum libinput_pointer_button_state state;
};
struct libinput_event_pointer_axis {
struct libinput_event base;
uint32_t time;
enum libinput_pointer_axis axis;
li_fixed_t value;
};
struct libinput_event_touch_touch {
struct libinput_event base;
uint32_t time;
int32_t slot;
li_fixed_t x;
li_fixed_t y;
enum libinput_touch_type touch_type;
};
struct libinput_fd_handle;
typedef void (*libinput_fd_callback)(int fd, void *data);
struct libinput_interface {
void (*get_current_screen_dimensions)(struct libinput_device *device,
int *width,
int *height,
void *user_data);
};
struct libinput;
struct libinput_device;
struct libinput *
libinput_create(const struct libinput_interface *interface, void *user_data);
int
libinput_get_fd(struct libinput *libinput);
int
libinput_dispatch(struct libinput *libinput);
struct libinput_event *
libinput_get_event(struct libinput *libinput);
void
libinput_destroy(struct libinput *libinput);
struct libinput_device *
libinput_device_create_evdev(struct libinput *libinput,
const char *devnode,
int fd,
void *user_data);
void
libinput_device_terminate(struct libinput_device *device);
void
libinput_device_destroy(struct libinput_device *device);
void *
libinput_device_get_user_data(struct libinput_device *device);
void
libinput_device_led_update(struct libinput_device *device,
enum libinput_led leds);
int
libinput_device_get_keys(struct libinput_device *device,
char *keys, size_t size);
void
libinput_device_calibrate(struct libinput_device *device,
float calibration[6]);
#endif /* LIBINPUT_H */

12
src/libinput.pc.in Normal file
View file

@ -0,0 +1,12 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
datarootdir=@datarootdir@
pkgdatadir=@datadir@/@PACKAGE@
libdir=@libdir@
includedir=@includedir@
Name: Libinput
Description: Input device library
Version: @LIBINPUT_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -linput