--- /dev/null
+*.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
--- /dev/null
+SUBDIRS = src
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
--- /dev/null
+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.
--- /dev/null
+#!/bin/sh
+
+test -n "$srcdir" || srcdir=`dirname "$0"`
+test -n "$srcdir" || srcdir=.
+(
+ cd "$srcdir" &&
+ autoreconf --force -v --install
+) || exit
+test -n "$NOCONFIGURE" || "$srcdir/configure" "$@"
--- /dev/null
+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
--- /dev/null
+libtool.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+lt~obsolete.m4
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+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