Merge branch 'master' into udev
authorJonas Ådahl <jadahl@gmail.com>
Tue, 19 Nov 2013 21:29:38 +0000 (22:29 +0100)
committerJonas Ådahl <jadahl@gmail.com>
Tue, 19 Nov 2013 21:29:38 +0000 (22:29 +0100)
19 files changed:
.gitignore [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
m4/.gitignore [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/evdev-touchpad.c [new file with mode: 0644]
src/evdev.c [new file with mode: 0644]
src/evdev.h [new file with mode: 0644]
src/filter.c [new file with mode: 0644]
src/filter.h [new file with mode: 0644]
src/libinput-private.h [new file with mode: 0644]
src/libinput-util.c [new file with mode: 0644]
src/libinput-util.h [new file with mode: 0644]
src/libinput-version.h.in [new file with mode: 0644]
src/libinput.c [new file with mode: 0644]
src/libinput.h [new file with mode: 0644]
src/libinput.pc.in [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..30a27dc
--- /dev/null
@@ -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
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..3a7ce7e
--- /dev/null
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..06c3092
--- /dev/null
+++ b/README
@@ -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.
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..b08bc83
--- /dev/null
@@ -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" "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..08472cf
--- /dev/null
@@ -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
diff --git a/m4/.gitignore b/m4/.gitignore
new file mode 100644 (file)
index 0000000..38066dd
--- /dev/null
@@ -0,0 +1,5 @@
+libtool.m4
+ltoptions.m4
+ltsugar.m4
+ltversion.m4
+lt~obsolete.m4
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..c1e3149
--- /dev/null
@@ -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
diff --git a/src/evdev-touchpad.c b/src/evdev-touchpad.c
new file mode 100644 (file)
index 0000000..a673a7b
--- /dev/null
@@ -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;
+}
diff --git a/src/evdev.c b/src/evdev.c
new file mode 100644 (file)
index 0000000..55d8a15
--- /dev/null
@@ -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);
+}
diff --git a/src/evdev.h b/src/evdev.h
new file mode 100644 (file)
index 0000000..c8cb405
--- /dev/null
@@ -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 */
diff --git a/src/filter.c b/src/filter.c
new file mode 100644 (file)
index 0000000..397e0f6
--- /dev/null
@@ -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;
+}
diff --git a/src/filter.h b/src/filter.h
new file mode 100644 (file)
index 0000000..6b2a1d2
--- /dev/null
@@ -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 */
diff --git a/src/libinput-private.h b/src/libinput-private.h
new file mode 100644 (file)
index 0000000..f49badc
--- /dev/null
@@ -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 */
diff --git a/src/libinput-util.c b/src/libinput-util.c
new file mode 100644 (file)
index 0000000..9d9d4dd
--- /dev/null
@@ -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;
+}
diff --git a/src/libinput-util.h b/src/libinput-util.h
new file mode 100644 (file)
index 0000000..1395106
--- /dev/null
@@ -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 */
diff --git a/src/libinput-version.h.in b/src/libinput-version.h.in
new file mode 100644 (file)
index 0000000..2b1c4ea
--- /dev/null
@@ -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
diff --git a/src/libinput.c b/src/libinput.c
new file mode 100644 (file)
index 0000000..10d6fc1
--- /dev/null
@@ -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);
+}
diff --git a/src/libinput.h b/src/libinput.h
new file mode 100644 (file)
index 0000000..a051e4a
--- /dev/null
@@ -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 */
diff --git a/src/libinput.pc.in b/src/libinput.pc.in
new file mode 100644 (file)
index 0000000..ed51acc
--- /dev/null
@@ -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