summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2010-06-17 18:12:58 +0200
committerHenrik Rydberg <rydberg@euromail.se>2010-06-17 18:12:58 +0200
commit66e5de9eaefc33ffa6af3617f9ec7a50f10af50d (patch)
tree66535311e0b9c422b30341237fd596fb9ebbdd75
downloadmtdev-git-66e5de9eaefc33ffa6af3617f9ec7a50f10af50d.tar.gz
Initial load of mtdev project
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
-rw-r--r--.gitignore3
-rw-r--r--COPYING23
-rw-r--r--Makefile53
-rw-r--r--README9
-rw-r--r--include/mtdev-mapping.h76
-rw-r--r--include/mtdev.h225
-rw-r--r--src/caps.c103
-rw-r--r--src/common.h87
-rw-r--r--src/core.c393
-rw-r--r--src/evbuf.h64
-rw-r--r--src/iobuf.c69
-rw-r--r--src/iobuf.h42
-rw-r--r--src/match.c392
-rw-r--r--src/match.h43
-rw-r--r--src/state.h62
-rw-r--r--test/mtdev-mapgen.c76
-rw-r--r--test/mtdev.c95
17 files changed, 1815 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7eb23b5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+bin
+obj
+patches
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..7fbfa7b
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,23 @@
+mtdev - MT device event converter (MIT license)
+
+Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+Copyright (C) 2010 Canonical Ltd.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c778193
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,53 @@
+VERSION = 1
+PATCHLEVEL = 0
+EXTRAVERSION = beta1
+
+LIBRARY = mtdev.so
+MODULES = src
+
+o_src = match iobuf caps core
+
+TARGETS += test/mtdev-mapgen
+TARGETS += test/mtdev
+
+OBJECTS = $(addsuffix .o,\
+ $(foreach mod,$(MODULES),\
+ $(addprefix $(mod)/,$(o_$(mod)))))
+
+TBIN = $(addprefix bin/,$(TARGETS))
+TLIB = $(addprefix obj/,$(LIBRARY))
+TOBJ = $(addprefix obj/,$(addsuffix .o,$(TARGETS)))
+OBJS = $(addprefix obj/,$(OBJECTS))
+LIBS =
+
+DLIB = usr/lib/xorg/modules
+
+INCLUDE = -Iinclude
+OPTS = -O3 -fPIC
+
+.PHONY: all clean
+.PRECIOUS: obj/%.o
+
+all: $(OBJS) $(TLIB) $(TOBJ) $(TBIN)
+
+bin/%: obj/%.o $(TLIB)
+ @mkdir -p $(@D)
+ gcc $< -o $@ $(TLIB) $(LIBS)
+
+$(TLIB): $(OBJS) $(XOBJS)
+ @rm -f $(TLIB)
+ gcc -shared $(OBJS) $(XOBJS) -Wl,-soname -Wl,$(LIBRARY) -o $@
+
+obj/%.o: %.c
+ @mkdir -p $(@D)
+ gcc $(INCLUDE) $(OPTS) -c $< -o $@
+
+clean:
+ rm -rf bin obj
+
+distclean: clean
+ rm -rf debian/*.log debian/files
+
+install: $(TLIB) $(TFDI)
+ install -d "$(DESTDIR)/$(DLIB)"
+ install -m 755 $(TLIB) "$(DESTDIR)/$(DLIB)"
diff --git a/README b/README
new file mode 100644
index 0000000..37ef122
--- /dev/null
+++ b/README
@@ -0,0 +1,9 @@
+mtdev - MT device event converter (MIT license)
+
+The mtdev library transforms all variants of kernel MT events to the
+slotted type B protocol. See the kernel documentation for more
+details.
+
+---
+Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+Copyright (C) 2010 Canonical Ltd.
diff --git a/include/mtdev-mapping.h b/include/mtdev-mapping.h
new file mode 100644
index 0000000..67fa06e
--- /dev/null
+++ b/include/mtdev-mapping.h
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+/* Tables are automatically generated by mtdev-mapgen */
+
+#ifndef _MTDEV_MAPPING_H
+#define _MTDEV_MAPPING_H
+
+#include <mtdev.h>
+
+#define MTDEV_TRACKING_ID 9
+#define MTDEV_POSITION_X 5
+#define MTDEV_POSITION_Y 6
+#define MTDEV_TOUCH_MAJOR 0
+#define MTDEV_TOUCH_MINOR 1
+#define MTDEV_WIDTH_MAJOR 2
+#define MTDEV_WIDTH_MINOR 3
+#define MTDEV_ORIENTATION 4
+
+static const unsigned int mtdev_map_abs2mt[ABS_CNT] = {
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008,
+ 0x0009, 0x000a, 0x000b, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+static const unsigned int mtdev_map_mt2abs[MT_ABS_SIZE] = {
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a,
+};
+
+static inline int mtdev_is_absmt(unsigned int code)
+{
+ return mtdev_map_abs2mt[code];
+}
+
+static inline unsigned int mtdev_abs2mt(unsigned int code)
+{
+ return mtdev_map_abs2mt[code] - 1;
+}
+
+static inline unsigned int mtdev_mt2abs(unsigned int mtcode)
+{
+ return mtdev_map_mt2abs[mtcode];
+}
+
+#endif
diff --git a/include/mtdev.h b/include/mtdev.h
new file mode 100644
index 0000000..5f2f6a3
--- /dev/null
+++ b/include/mtdev.h
@@ -0,0 +1,225 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef _MTDEV_H
+#define _MTDEV_H
+
+#include <linux/input.h>
+
+/* includes available in 2.6.30-rc5 */
+#ifndef BTN_TOOL_QUADTAP
+#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */
+#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
+#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
+#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
+#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
+#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
+#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
+#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
+#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
+#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
+#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
+#define SYN_MT_REPORT 2
+#define MT_TOOL_FINGER 0
+#define MT_TOOL_PEN 1
+#endif
+
+/* includes available in 2.6.33 */
+#ifndef ABS_MT_PRESSURE
+#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
+#endif
+
+/* includes available in 2.6.36 */
+#ifndef ABS_MT_SLOT
+#define ABS_MT_SLOT 0x2f /* MT slot being modified */
+#define MT_SLOT_ABS_EVENTS { \
+ ABS_MT_TOUCH_MAJOR, \
+ ABS_MT_TOUCH_MINOR, \
+ ABS_MT_WIDTH_MAJOR, \
+ ABS_MT_WIDTH_MINOR, \
+ ABS_MT_ORIENTATION, \
+ ABS_MT_POSITION_X, \
+ ABS_MT_POSITION_Y, \
+ ABS_MT_TOOL_TYPE, \
+ ABS_MT_BLOB_ID, \
+ ABS_MT_TRACKING_ID, \
+ ABS_MT_PRESSURE, \
+}
+#endif
+
+#define MT_ABS_SIZE 11
+
+/**
+ * struct mt_caps - protocol capabilities of kernel device
+ * @has_mtdata: true if the device has MT capabilities
+ * @has_slot: true if the device sends MT slots
+ * @nullid: tracking id used to represent null
+ * @slot: slot event properties
+ * @abs: ABS_MT event properties
+ */
+struct mtdev_caps {
+ int has_mtdata;
+ int has_slot;
+ int has_abs[MT_ABS_SIZE];
+ int nullid;
+ struct input_absinfo slot;
+ struct input_absinfo abs[MT_ABS_SIZE];
+};
+
+/**
+ * struct mtdev - represents an input MT device
+ * @caps: the kernel device protocol capabilities
+ * @state: internal mtdev parsing state
+ *
+ * The mtdev structure represents a kernel MT device type B, emitting
+ * MT slot events. The events put into mtdev may be from any MT
+ * device, specifically type A without contact tracking, type A with
+ * contact tracking, or type B with contact tracking. See the kernel
+ * documentation for further details.
+ *
+ */
+struct mtdev {
+ struct mtdev_caps caps;
+ struct mtdev_state *state;
+};
+
+/**
+ * mtdev_init - initialize mtdev converter
+ * @dev: the mtdev to initialize
+ *
+ * Sets up the internal data structures.
+ *
+ * Returns zero on success, negative error number otherwise.
+ */
+int mtdev_init(struct mtdev *dev);
+
+/**
+ * mtdev_configure - configure the mtdev converter
+ * @dev: the mtdev to configure
+ * @fd: file descriptor of the kernel device
+ *
+ * Reads the device properties to set up the protocol capabilities.
+ * If preferred, this can be done by hand, omitting this call.
+ *
+ * Returns zero on success, negative error number otherwise.
+ */
+int mtdev_configure(struct mtdev *dev, int fd);
+
+/**
+ * mtdev_open - open an mtdev converter
+ * @dev: the mtdev to open
+ * @fd: file descriptor of the kernel device
+ *
+ * Initialize the mtdev structure and configure it by reading
+ * the protocol capabilities through the file descriptor.
+ *
+ * Returns zero on success, negative error number otherwise.
+ *
+ * This call combines mtdev_init() and mtdev_configure(), which
+ * may be used separately instead.
+ */
+int mtdev_open(struct mtdev *dev, int fd);
+
+/**
+ * mtdev_fetch - fetch an event from the kernel device
+ * @dev: the mtdev in use
+ * @ev: the kernel input event to fill
+ * @fd: file descriptor of the kernel device
+ *
+ * Fetch a kernel event from the kernel device. The read operation
+ * behaves as dictated by the file descriptior; if O_NONBLOCK is not
+ * set, the read will block until an event is available.
+ *
+ * On success, returns the number of events read. Otherwise, a standard
+ * negative error number is returned.
+ */
+int mtdev_fetch(struct mtdev *dev, struct input_event *ev, int fd);
+
+/**
+ * mtdev_put - put an event into the converter
+ * @dev: the mtdev in use
+ * @ev: the kernel input event to put
+ *
+ * Put a kernel event into the mtdev converter. The event should
+ * come straight from the device.
+ *
+ * This call does not block; if the buffer becomes full, older events
+ * are dropped. The buffer is guaranteed to handle several complete MT
+ * packets.
+ */
+void mtdev_put(struct mtdev *dev, const struct input_event *ev);
+
+/**
+ * mtdev_pull - pull events from the kernel device
+ * @dev: the mtdev in use
+ * @fd: file descriptor of the kernel device
+ * @max_events: max number of events to read
+ *
+ * Read a maxmimum of max_events events from the device, and put them
+ * in the converter. The read operation behaves as dictated by the
+ * file descriptior; if O_NONBLOCK is not set, the read will block
+ * until max_events events are available or the buffer is full.
+ *
+ * On success, returns the number of events read. Otherwise, a standard
+ * negative error number is returned.
+ *
+ * This call combines mtdev_fetch() with mtdev_put(), which
+ * may be used separately instead.
+ */
+int mtdev_pull(struct mtdev *dev, int fd, int max_events);
+
+/**
+ * mtdev_empty - check if there are events to get
+ * @dev: the mtdev in use
+ *
+ * Returns true if the event queue is empty, false otherwise.
+ */
+int mtdev_empty(struct mtdev *dev);
+
+/**
+ * mtdev_get - get canonical events from mtdev
+ * @dev: the mtdev in use
+ * @ev: the input event to fill
+ *
+ * Get a canonical event from mtdev. The events appear as if they came
+ * from a type B device emitting MT slot events.
+ *
+ * The queue must be non-empty before calling this function.
+ */
+void mtdev_get(struct mtdev *dev, struct input_event* ev);
+
+/**
+ * mtdev_close - close the mtdev converter
+ * @dev: the mtdev to close
+ *
+ * Deallocates all memory associated with mtdev, and sets the state
+ * pointer to NULL.
+ */
+void mtdev_close(struct mtdev *dev);
+
+#endif
diff --git a/src/caps.c b/src/caps.c
new file mode 100644
index 0000000..1821bd7
--- /dev/null
+++ b/src/caps.c
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include "common.h"
+
+#define SETABS(c, x, map, key, fd) \
+ (c->has_##x = getbit(map, key) && getabs(&c->x, key, fd))
+
+static const int SN_COORD = 250; /* coordinate signal-to-noise ratio */
+static const int SN_WIDTH = 100; /* width signal-to-noise ratio */
+static const int SN_ORIENT = 10; /* orientation signal-to-noise ratio */
+
+static const int bits_per_long = 8 * sizeof(long);
+
+static inline int nlongs(int nbit)
+{
+ return (nbit + bits_per_long - 1) / bits_per_long;
+}
+
+static inline int getbit(const unsigned long *map, int key)
+{
+ return (map[key / bits_per_long] >> (key % bits_per_long)) & 0x01;
+}
+
+static int getabs(struct input_absinfo *abs, int key, int fd)
+{
+ int rc;
+ SYSCALL(rc = ioctl(fd, EVIOCGABS(key), abs));
+ return rc >= 0;
+}
+
+static int has_mt_data(const struct mtdev_caps *cap)
+{
+ return cap->has_abs[MTDEV_POSITION_X] && cap->has_abs[MTDEV_POSITION_Y];
+}
+
+static void default_fuzz(struct mtdev_caps *cap, int bit, int sn)
+{
+ if (cap->has_abs[bit] && cap->abs[bit].fuzz == 0)
+ cap->abs[bit].fuzz =
+ (cap->abs[bit].maximum - cap->abs[bit].minimum) / sn;
+}
+
+static int read_caps(struct mtdev_caps *cap, int fd)
+{
+ unsigned long absbits[nlongs(ABS_MAX)];
+ int rc, i;
+
+ memset(cap, 0, sizeof(struct mtdev_caps));
+
+ SYSCALL(rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), absbits));
+ if (rc < 0)
+ return rc;
+
+ SETABS(cap, slot, absbits, ABS_MT_SLOT, fd);
+ for (i = 0; i < MT_ABS_SIZE; i++)
+ SETABS(cap, abs[i], absbits, mtdev_mt2abs(i), fd);
+
+ cap->has_mtdata = has_mt_data(cap);
+
+ if (cap->has_abs[MTDEV_TRACKING_ID])
+ cap->nullid = cap->abs[MTDEV_TRACKING_ID].minimum - 1;
+
+ default_fuzz(cap, MTDEV_POSITION_X, SN_COORD);
+ default_fuzz(cap, MTDEV_POSITION_Y, SN_COORD);
+ default_fuzz(cap, MTDEV_TOUCH_MAJOR, SN_WIDTH);
+ default_fuzz(cap, MTDEV_TOUCH_MINOR, SN_WIDTH);
+ default_fuzz(cap, MTDEV_WIDTH_MAJOR, SN_WIDTH);
+ default_fuzz(cap, MTDEV_WIDTH_MINOR, SN_WIDTH);
+ default_fuzz(cap, MTDEV_ORIENTATION, SN_ORIENT);
+
+ return 0;
+}
+
+int mtdev_configure(struct mtdev *dev, int fd)
+{
+ return read_caps(&dev->caps, fd);
+}
diff --git a/src/common.h b/src/common.h
new file mode 100644
index 0000000..03bc7bd
--- /dev/null
+++ b/src/common.h
@@ -0,0 +1,87 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <mtdev-mapping.h>
+#include <malloc.h>
+#include <string.h>
+#include <errno.h>
+
+#define DIM_FINGER 32
+#define DIM2_FINGER (DIM_FINGER * DIM_FINGER)
+
+/* event buffer size (must be a power of two) */
+#define DIM_EVENTS 512
+
+/* all bit masks have this type */
+typedef unsigned int bitmask_t;
+
+#define BITMASK(x) (1U << (x))
+#define BITONES(x) (BITMASK(x) - 1U)
+#define GETBIT(m, x) (((m) >> (x)) & 1U)
+#define SETBIT(m, x) (m |= BITMASK(x))
+#define CLEARBIT(m, x) (m &= ~BITMASK(x))
+#define MODBIT(m, x, b) ((b) ? SETBIT(m, x) : CLEARBIT(m, x))
+
+static inline int maxval(int x, int y) { return x > y ? x : y; }
+static inline int minval(int x, int y) { return x < y ? x : y; }
+
+static inline int clamp15(int x)
+{
+ return x < -32767 ? -32767 : x > 32767 ? 32767 : x;
+}
+
+/* absolute scale is assumed to fit in 15 bits */
+static inline int dist2(int dx, int dy)
+{
+ dx = clamp15(dx);
+ dy = clamp15(dy);
+ return dx * dx + dy * dy;
+}
+
+/* Count number of bits (Sean Eron Andersson's Bit Hacks) */
+static inline int bitcount(unsigned v)
+{
+ v -= ((v>>1) & 0x55555555);
+ v = (v&0x33333333) + ((v>>2) & 0x33333333);
+ return (((v + (v>>4)) & 0xF0F0F0F) * 0x1010101) >> 24;
+}
+
+/* Return index of first bit [0-31], -1 on zero */
+#define firstbit(v) (__builtin_ffs(v) - 1)
+
+/* boost-style foreach bit */
+#define foreach_bit(i, m) \
+ for (i = firstbit(m); i >= 0; i = firstbit((m) & (~0U << i + 1)))
+
+/* robust system ioctl calls */
+#define SYSCALL(call) while (((call) == -1) && (errno == EINTR))
+
+#endif
diff --git a/src/core.c b/src/core.c
new file mode 100644
index 0000000..3df8257
--- /dev/null
+++ b/src/core.c
@@ -0,0 +1,393 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include "state.h"
+#include "iobuf.h"
+#include "evbuf.h"
+
+static inline int istouch(const struct mtdev_slot *data,
+ const struct mtdev_caps *caps)
+{
+ return data->abs[MTDEV_TOUCH_MAJOR] ||
+ !caps->has_abs[MTDEV_TOUCH_MAJOR];
+}
+
+/* Dmitry Torokhov's code from kernel/driver/input/input.c */
+static int defuzz(int value, int old_val, int fuzz)
+{
+ if (fuzz) {
+ if (value > old_val - fuzz / 2 && value < old_val + fuzz / 2)
+ return old_val;
+
+ if (value > old_val - fuzz && value < old_val + fuzz)
+ return (old_val * 3 + value) / 4;
+
+ if (value > old_val - fuzz * 2 && value < old_val + fuzz * 2)
+ return (old_val + value) / 2;
+ }
+
+ return value;
+}
+
+/*
+ * solve - solve contact matching problem
+ * @state: mtdev state
+ * @caps: device capabilities
+ * @sid: array of current tracking ids
+ * @sx: array of current position x
+ * @sy: array of current position y
+ * @sn: number of current contacts
+ * @nid: array of new or matched tracking ids, to be filled
+ * @nx: array of new position x
+ * @ny: array of new position y
+ * @nn: number of new contacts
+ * @touch: which of the new contacts to fill
+ */
+static void solve(struct mtdev_state *state, const struct mtdev_caps *caps,
+ const int *sid, const int *sx, const int *sy, int sn,
+ int *nid, const int *nx, const int *ny, int nn,
+ bitmask_t touch)
+{
+ int A[DIM2_FINGER], *row;
+ int n2s[DIM_FINGER];
+ int id, i, j;
+
+ /* setup distance matrix for contact matching */
+ for (j = 0; j < sn; j++) {
+ row = A + nn * j;
+ for (i = 0; i < nn; i++)
+ row[i] = dist2(nx[i] - sx[j], ny[i] - sy[j]);
+ }
+
+ match_fingers(n2s, A, nn, sn);
+
+ /* update matched contacts and create new ones */
+ foreach_bit(i, touch) {
+ j = n2s[i];
+ id = j >= 0 ? sid[j] : caps->nullid;
+ while (id == caps->nullid)
+ id = ++state->lastid;
+ nid[i] = id;
+ }
+}
+
+/*
+ * assign_tracking_id - assign tracking ids to all contacts
+ * @state: mtdev state
+ * @caps: device capabilities
+ * @data: array of all present contacts, to be filled
+ * @prop: array of all set contacts properties
+ * @size: number of contacts in array
+ * @touch: which of the contacts are actual touches
+ */
+static void assign_tracking_id(struct mtdev_state *state,
+ const struct mtdev_caps *caps,
+ struct mtdev_slot *data, bitmask_t *prop,
+ int size, bitmask_t touch)
+{
+ int sid[DIM_FINGER], sx[DIM_FINGER], sy[DIM_FINGER], sn = 0;
+ int nid[DIM_FINGER], nx[DIM_FINGER], ny[DIM_FINGER], i;
+ foreach_bit(i, state->used) {
+ sid[sn] = state->data[i].abs[MTDEV_TRACKING_ID];
+ sx[sn] = state->data[i].abs[MTDEV_POSITION_X];
+ sy[sn] = state->data[i].abs[MTDEV_POSITION_Y];
+ sn++;
+ }
+ for (i = 0; i < size; i++) {
+ nx[i] = data[i].abs[MTDEV_POSITION_X];
+ ny[i] = data[i].abs[MTDEV_POSITION_Y];
+ }
+ solve(state, caps, sid, sx, sy, sn, nid, nx, ny, size, touch);
+ for (i = 0; i < size; i++) {
+ data[i].abs[MTDEV_TRACKING_ID] =
+ GETBIT(touch, i) ? nid[i] : caps->nullid;
+ prop[i] |= BITMASK(MTDEV_TRACKING_ID);
+ }
+}
+
+/*
+ * process_typeA - consume MT events and update mtdev state
+ * @state: mtdev state
+ * @data: array of all present contacts, to be filled
+ * @prop: array of all set contacts properties, to be filled
+ *
+ * This function is called when a SYN_REPORT is seen, right before
+ * that event is pushed to the queue.
+ *
+ * Returns -1 if the packet is not MT related and should not affect
+ * the current mtdev state.
+ */
+static int process_typeA(struct mtdev_state *state,
+ struct mtdev_slot *data, bitmask_t *prop)
+{
+ struct input_event ev;
+ int consumed, mtcode;
+ int mtcnt = 0, size = 0;
+ prop[size] = 0;
+ while (!evbuf_empty(&state->inbuf)) {
+ evbuf_get(&state->inbuf, &ev);
+ consumed = 0;
+ switch (ev.type) {
+ case EV_SYN:
+ switch (ev.code) {
+ case SYN_MT_REPORT:
+ if (size < DIM_FINGER &&
+ GETBIT(prop[size], MTDEV_POSITION_X) &&
+ GETBIT(prop[size], MTDEV_POSITION_Y))
+ size++;
+ if (size < DIM_FINGER)
+ prop[size] = 0;
+ mtcnt++;
+ consumed = 1;
+ break;
+ }
+ break;
+ case EV_KEY:
+ switch (ev.code) {
+ case BTN_TOUCH:
+ mtcnt++;
+ break;
+ }
+ break;
+ case EV_ABS:
+ if (size < DIM_FINGER && mtdev_is_absmt(ev.code)) {
+ mtcode = mtdev_abs2mt(ev.code);
+ data[size].abs[mtcode] = ev.value;
+ prop[size] |= BITMASK(mtcode);
+ mtcnt++;
+ consumed = 1;
+ }
+ break;
+ }
+ if (!consumed)
+ evbuf_put(&state->outbuf, &ev);
+ }
+ return mtcnt ? size : -1;
+}
+
+/*
+ * process_typeB - propagate events without parsing
+ * @state: mtdev state
+ *
+ * This function is called when a SYN_REPORT is seen, right before
+ * that event is pushed to the queue.
+ */
+static void process_typeB(struct mtdev_state *state)
+{
+ struct input_event ev;
+ while (!evbuf_empty(&state->inbuf)) {
+ evbuf_get(&state->inbuf, &ev);
+ evbuf_put(&state->outbuf, &ev);
+ }
+}
+
+/*
+ * filter_data - apply input filtering on new incoming data
+ * @state: mtdev state
+ * @caps: device capabilities
+ * @data: the incoming data to filter
+ * @prop: the properties to filter
+ * @slot: the slot the data refers to
+ */
+static void filter_data(const struct mtdev_state *state,
+ const struct mtdev_caps *caps,
+ struct mtdev_slot *data, bitmask_t prop,
+ int slot)
+{
+ int i;
+ foreach_bit(i, prop) {
+ int fuzz = caps->abs[i].fuzz;
+ int oldval = state->data[slot].abs[i];
+ data->abs[i] = defuzz(data->abs[i], oldval, fuzz);
+ }
+}
+
+/*
+ * push_slot_changes - propagate state changes
+ * @state: mtdev state
+ * @data: the incoming data to propagate
+ * @prop: the properties to propagate
+ * @slot: the slot the data refers to
+ * @syn: reference to the SYN_REPORT event
+ */
+static void push_slot_changes(struct mtdev_state *state,
+ const struct mtdev_slot *data, bitmask_t prop,
+ int slot, const struct input_event *syn)
+{
+ struct input_event ev;
+ int i, count = 0;
+ foreach_bit(i, prop)
+ if (state->data[slot].abs[i] != data->abs[i])
+ count++;
+ if (!count)
+ return;
+ ev.time = syn->time;
+ ev.type = EV_ABS;
+ ev.code = ABS_MT_SLOT;
+ ev.value = slot;
+ if (state->slot != ev.value) {
+ evbuf_put(&state->outbuf, &ev);
+ state->slot = ev.value;
+ }
+ foreach_bit(i, prop) {
+ ev.code = mtdev_mt2abs(i);
+ ev.value = data->abs[i];
+ if (state->data[slot].abs[i] != ev.value) {
+ evbuf_put(&state->outbuf, &ev);
+ state->data[slot].abs[i] = ev.value;
+ }
+ }
+}
+
+/*
+ * apply_typeA_changes - parse and propagate state changes
+ * @state: mtdev state
+ * @caps: device capabilities
+ * @data: array of data to apply
+ * @prop: array of properties to apply
+ * @size: number of contacts in array
+ * @syn: reference to the SYN_REPORT event
+ */
+static void apply_typeA_changes(struct mtdev_state *state,
+ const struct mtdev_caps *caps,
+ struct mtdev_slot *data, const bitmask_t *prop,
+ int size, const struct input_event *syn)
+{
+ bitmask_t unused = ~state->used;
+ bitmask_t used = 0;
+ int i, slot, id;
+ for (i = 0; i < size; i++) {
+ id = data[i].abs[MTDEV_TRACKING_ID];
+ foreach_bit(slot, state->used) {
+ if (state->data[slot].abs[MTDEV_TRACKING_ID] != id)
+ continue;
+ filter_data(state, caps, &data[i], prop[i], slot);
+ push_slot_changes(state, &data[i], prop[i], slot, syn);
+ SETBIT(used, slot);
+ id = caps->nullid;
+ break;
+ }
+ if (id != caps->nullid) {
+ slot = firstbit(unused);
+ push_slot_changes(state, &data[i], prop[i], slot, syn);
+ SETBIT(used, slot);
+ CLEARBIT(unused, slot);
+ }
+ }
+
+ /* clear unused slots and update slot usage */
+ foreach_bit(slot, state->used & ~used) {
+ struct mtdev_slot tdata = state->data[slot];
+ bitmask_t tprop = BITMASK(MTDEV_TRACKING_ID);
+ tdata.abs[MTDEV_TRACKING_ID] = caps->nullid;
+ push_slot_changes(state, &tdata, tprop, slot, syn);
+ }
+ state->used = used;
+}
+
+/*
+ * convert_A_to_B - propagate a type A packet as a type B packet
+ * @state: mtdev state
+ * @caps: device capabilities
+ * @syn: reference to the SYN_REPORT event
+ */
+static void convert_A_to_B(struct mtdev_state *state,
+ const struct mtdev_caps *caps,
+ const struct input_event *syn)
+{
+ struct mtdev_slot data[DIM_FINGER];
+ bitmask_t prop[DIM_FINGER];
+ int size = process_typeA(state, data, prop);
+ if (size < 0)
+ return;
+ if (!caps->has_abs[MTDEV_TRACKING_ID]) {
+ bitmask_t touch = 0;
+ int i;
+ for (i = 0; i < size; i++)
+ MODBIT(touch, i, istouch(&data[i], caps));
+ assign_tracking_id(state, caps, data, prop, size, touch);
+ }
+ apply_typeA_changes(state, caps, data, prop, size, syn);
+}
+
+int mtdev_init(struct mtdev *dev)
+{
+ memset(dev, 0, sizeof(struct mtdev));
+ dev->state = calloc(1, sizeof(struct mtdev_state));
+ if (!dev->state)
+ return -ENOMEM;
+ return 0;
+}
+
+int mtdev_open(struct mtdev *dev, int fd)
+{
+ int ret;
+ ret = mtdev_init(dev);
+ if (ret)
+ goto error;
+ ret = mtdev_configure(dev, fd);
+ if (ret)
+ goto mtdev;
+ return 0;
+ mtdev:
+ mtdev_close(dev);
+ error:
+ return ret;
+}
+
+void mtdev_put(struct mtdev *dev, const struct input_event *ev)
+{
+ struct mtdev_state *state = dev->state;
+ if (ev->type == EV_SYN && ev->code == SYN_REPORT) {
+ bitmask_t head = state->outbuf.head;
+ if (dev->state)
+ convert_A_to_B(state, &dev->caps, ev);
+ else
+ process_typeB(state);
+ if (state->outbuf.head != head)
+ evbuf_put(&state->outbuf, ev);
+ } else {
+ evbuf_put(&state->inbuf, ev);
+ }
+}
+
+int mtdev_empty(struct mtdev *dev)
+{
+ return evbuf_empty(&dev->state->outbuf);
+}
+
+void mtdev_get(struct mtdev *dev, struct input_event* ev)
+{
+ evbuf_get(&dev->state->outbuf, ev);
+}
+
+void mtdev_close(struct mtdev *dev)
+{
+ free(dev->state);
+ memset(dev, 0, sizeof(struct mtdev));
+}
diff --git a/src/evbuf.h b/src/evbuf.h
new file mode 100644
index 0000000..ba61cd5
--- /dev/null
+++ b/src/evbuf.h
@@ -0,0 +1,64 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef MTDEV_EVBUF_H
+#define MTDEV_EVBUF_H
+
+#include "common.h"
+
+struct mtdev_evbuf {
+ int head;
+ int tail;
+ struct input_event buffer[DIM_EVENTS];
+};
+
+static inline int evbuf_empty(const struct mtdev_evbuf *evbuf)
+{
+ return evbuf->head == evbuf->tail;
+}
+
+static inline int evbuf_full(const struct mtdev_evbuf *evbuf)
+{
+ return ((evbuf->head + 1) & (DIM_EVENTS - 1)) == evbuf->tail;
+}
+
+static inline void evbuf_put(struct mtdev_evbuf *evbuf,
+ const struct input_event *ev)
+{
+ evbuf->buffer[evbuf->head++] = *ev;
+ evbuf->head &= DIM_EVENTS - 1;
+}
+
+static inline void evbuf_get(struct mtdev_evbuf *evbuf,
+ struct input_event *ev)
+{
+ *ev = evbuf->buffer[evbuf->tail++];
+ evbuf->tail &= DIM_EVENTS - 1;
+}
+
+#endif
diff --git a/src/iobuf.c b/src/iobuf.c
new file mode 100644
index 0000000..6d238ec
--- /dev/null
+++ b/src/iobuf.c
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include "iobuf.h"
+#include "state.h"
+
+int mtdev_fetch(struct mtdev *dev, struct input_event *ev, int fd)
+{
+ struct mtdev_iobuf *buf = &dev->state->iobuf;
+ int n = buf->head - buf->tail;
+ if (n < EVENT_SIZE) {
+ if (buf->tail && n > 0)
+ memmove(buf->data, buf->data + buf->tail, n);
+ buf->head = n;
+ buf->tail = 0;
+ SYSCALL(n = read(fd, buf->data + buf->head,
+ DIM_BUFFER - buf->head));
+ if (n <= 0)
+ return n;
+ buf->head += n;
+ }
+ if (buf->head - buf->tail < EVENT_SIZE)
+ return 0;
+ memcpy(ev, buf->data + buf->tail, EVENT_SIZE);
+ buf->tail += EVENT_SIZE;
+ return 1;
+}
+
+int mtdev_pull(struct mtdev *dev, int fd, int max_events)
+{
+ struct mtdev_state *state = dev->state;
+ struct input_event ev;
+ int ret, count = 0;
+ while (max_events-- && !evbuf_full(&state->inbuf)) {
+ ret = mtdev_fetch(dev, &ev, fd);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ break;
+ mtdev_put(dev, &ev);
+ count++;
+ }
+ return count;
+}
diff --git a/src/iobuf.h b/src/iobuf.h
new file mode 100644
index 0000000..b54ea48
--- /dev/null
+++ b/src/iobuf.h
@@ -0,0 +1,42 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef MTDEV_IOBUF_H
+#define MTDEV_IOBUF_H
+
+#include "common.h"
+
+#define EVENT_SIZE sizeof(struct input_event)
+#define DIM_BUFFER (DIM_EVENTS * EVENT_SIZE)
+
+struct mtdev_iobuf {
+ int head, tail;
+ char data[DIM_BUFFER];
+};
+
+#endif
diff --git a/src/match.c b/src/match.c
new file mode 100644
index 0000000..1b3700c
--- /dev/null
+++ b/src/match.c
@@ -0,0 +1,392 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include "match.h"
+#include <string.h>
+#include <stdio.h>
+
+/**
+ * Bitmap implementation of the hungarian algorithm (GPL license)
+ *
+ * Copyright (C) 2008 Henrik Rydberg <rydberg@euromail.se>
+ *
+ * Based on code released by Markus Buehren (2004) (BSD license)
+ *
+ * Copyright (C) 2004, Markus Buehren. All rights reserved.
+ * See CREDITS file for full license terms.
+ *
+ */
+
+typedef unsigned col_t[1];
+typedef unsigned mat_t[DIM_FINGER];
+
+#define GET1(m, x) ((m[0] >> (x)) & 1U)
+#define SET1(m, x) (m[0] |= (1U << (x)))
+#define CLEAR1(m, x) (m[0] &= ~(1U << (x)))
+
+#define GET2(m, row, col) ((m[col] >> (row)) & 1U)
+#define SET2(m, row, col) (m[col] |= (1U << (row)))
+#define CLEAR2(m, row, col) (m[col] &= ~(1U << (row)))
+
+/********************************************************/
+
+static void buildixvector(int *ix, mat_t mstar, int nrows, int ncols)
+{
+ int row, col;
+ for (row = 0; row < nrows; row++) {
+ for (col = 0; col < ncols; col++) {
+ if (GET2(mstar, row, col)) {
+ ix[row] = col;
+ break;
+ }
+ }
+ }
+}
+
+
+/********************************************************/
+
+static void step2a(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin);
+static void step2b(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin);
+static void step3(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin);
+static void step4(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin, int row, int col);
+static void step5(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin);
+
+static void ixoptimal(int *ix, int *mdist, int nrows, int ncols)
+{
+ int *mdistTemp, *mdistEnd, *columnEnd, value, minValue;
+ int dmin, row, col;
+ col_t ccol, crow;
+ mat_t mstar, mprime, nmstar;
+
+ memset(ccol, 0, sizeof(col_t));
+ memset(crow, 0, sizeof(col_t));
+ memset(mstar, 0, sizeof(mat_t));
+ memset(mprime, 0, sizeof(mat_t));
+ memset(nmstar, 0, sizeof(mat_t));
+
+ /* initialization */
+ for (row = 0; row < nrows; row++)
+ ix[row] = -1;
+
+ mdistEnd = mdist + nrows * ncols;
+
+ /* preliminary steps */
+ if (nrows <= ncols) {
+ dmin = nrows;
+
+ for (row = 0; row < nrows; row++) {
+ /* find the smallest element in the row */
+ mdistTemp = mdist + row;
+ minValue = *mdistTemp;
+ mdistTemp += nrows;
+ while (mdistTemp < mdistEnd) {
+ value = *mdistTemp;
+ if (value < minValue)
+ minValue = value;
+ mdistTemp += nrows;
+ }
+
+ /* subtract the smallest element from each element
+ of the row */
+ mdistTemp = mdist + row;
+ while (mdistTemp < mdistEnd) {
+ *mdistTemp -= minValue;
+ mdistTemp += nrows;
+ }
+ }
+
+ /* Steps 1 and 2a */
+ for (row = 0; row < nrows; row++) {
+ for (col = 0; col < ncols; col++) {
+ if (mdist[row + nrows * col] != 0)
+ continue;
+ if (GET1(ccol, col))
+ continue;
+ SET2(mstar, row, col);
+ SET1(ccol, col);
+ break;
+ }
+ }
+ } else {
+ dmin = ncols;
+
+ for (col = 0; col < ncols; col++) {
+ /* find the smallest element in the column */
+ mdistTemp = mdist + nrows*col;
+ columnEnd = mdistTemp + nrows;
+
+ minValue = *mdistTemp++;
+ while (mdistTemp < columnEnd) {
+ value = *mdistTemp++;
+ if (value < minValue)
+ minValue = value;
+ }
+
+ /* subtract the smallest element from each element
+ of the column */
+ mdistTemp = mdist + nrows*col;
+ while (mdistTemp < columnEnd)
+ *mdistTemp++ -= minValue;
+ }
+
+ /* Steps 1 and 2a */
+ for (col = 0; col < ncols; col++) {
+ for (row = 0; row < nrows; row++) {
+ if (mdist[row + nrows * col] != 0)
+ continue;
+ if (GET1(crow, row))
+ continue;
+ SET2(mstar, row, col);
+ SET1(ccol, col);
+ SET1(crow, row);
+ break;
+ }
+ }
+ memset(crow, 0, sizeof(col_t));
+ }
+
+ /* move to step 2b */
+ step2b(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+}
+
+/********************************************************/
+static void step2a(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin)
+{
+ int col, row;
+
+ /* cover every column containing a starred zero */
+ for (col = 0; col < ncols; col++) {
+ for (row = 0; row < nrows; row++) {
+ if (!GET2(mstar, row, col))
+ continue;
+ SET1(ccol, col);
+ break;
+ }
+ }
+
+ /* move to step 3 */
+ step2b(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+}
+
+/********************************************************/
+static void step2b(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin)
+{
+ int col, ncc;
+
+ /* count covered columns */
+ ncc = 0;
+ for (col = 0; col < ncols; col++)
+ if (GET1(ccol, col))
+ ncc++;
+
+ if (ncc == dmin) {
+ /* algorithm finished */
+ buildixvector(ix, mstar, nrows, ncols);
+ } else {
+ /* move to step 3 */
+ step3(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+ }
+
+}
+
+/********************************************************/
+static void step3(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin)
+{
+ int zerosFound;
+ int row, col, cstar;
+
+ zerosFound = 1;
+ while (zerosFound) {
+ zerosFound = 0;
+ for (col = 0; col < ncols; col++) {
+ if (GET1(ccol, col))
+ continue;
+ for (row = 0; row < nrows; row++) {
+ if (mdist[row + nrows * col] != 0)
+ continue;
+ if (GET1(crow, row))
+ continue;
+
+ /* prime zero */
+ SET2(mprime, row, col);
+
+ /* find starred zero in current row */
+ for (cstar = 0; cstar < ncols; cstar++)
+ if (GET2(mstar, row, cstar))
+ break;
+
+ if (cstar == ncols) { /* no starred zero */
+ /* move to step 4 */
+ step4(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin, row, col);
+ return;
+ } else {
+ SET1(crow, row);
+ CLEAR1(ccol, cstar);
+ zerosFound = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* move to step 5 */
+ step5(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+}
+
+/********************************************************/
+static void step4(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin, int row, int col)
+{
+ int n, rstar, cstar, primeRow, primeCol;
+
+ /* generate temporary copy of mstar */
+ memcpy(nmstar, mstar, sizeof(mat_t));
+
+ /* star current zero */
+ SET2(nmstar, row, col);
+
+ /* find starred zero in current column */
+ cstar = col;
+ for (rstar = 0; rstar < nrows; rstar++)
+ if (GET2(mstar, rstar, cstar))
+ break;
+
+ while (rstar < nrows) {
+ /* unstar the starred zero */
+ CLEAR2(nmstar, rstar, cstar);
+
+ /* find primed zero in current row */
+ primeRow = rstar;
+ for (primeCol = 0; primeCol < ncols; primeCol++)
+ if (GET2(mprime, primeRow, primeCol))
+ break;
+
+ /* star the primed zero */
+ SET2(nmstar, primeRow, primeCol);
+
+ /* find starred zero in current column */
+ cstar = primeCol;
+ for (rstar = 0; rstar < nrows; rstar++)
+ if (GET2(mstar, rstar, cstar))
+ break;
+ }
+
+ /* use temporary copy as new mstar */
+ /* delete all primes, uncover all rows */
+ memcpy(mstar, nmstar, sizeof(mat_t));
+ memset(mprime, 0, sizeof(mat_t));
+ memset(crow, 0, sizeof(col_t));
+
+ /* move to step 2a */
+ step2a(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+}
+
+/********************************************************/
+static void step5(int *ix, int *mdist, mat_t mstar, mat_t nmstar,
+ mat_t mprime, col_t ccol, col_t crow, int nrows, int ncols,
+ int dmin)
+{
+ int h = 0, value;
+ int row, col, found = 0;
+
+ /* find smallest uncovered element h */
+ for (row = 0; row < nrows; row++) {
+ if (GET1(crow, row))
+ continue;
+ for (col = 0; col < ncols; col++) {
+ if (GET1(ccol, col))
+ continue;
+ value = mdist[row + nrows * col];
+ if (!found || value < h) {
+ h = value;
+ found = 1;
+ }
+ }
+ }
+
+ /* where to go if nothing uncovered? */
+ if (!found)
+ return;
+
+ /* add h to each covered row */
+ for (row = 0; row < nrows; row++) {
+ if (!GET1(crow, row))
+ continue;
+ for (col = 0; col < ncols; col++)
+ mdist[row + nrows * col] += h;
+ }
+
+ /* subtract h from each uncovered column */
+ for (col = 0; col < ncols; col++) {
+ if (GET1(ccol, col))
+ continue;
+ for (row = 0; row < nrows; row++)
+ mdist[row + nrows * col] -= h;
+ }
+
+ /* move to step 3 */
+ step3(ix, mdist, mstar, nmstar,
+ mprime, ccol, crow, nrows, ncols,
+ dmin);
+}
+
+void match_fingers(int ix[DIM_FINGER], int A[DIM2_FINGER], int nrow, int ncol)
+{
+ ixoptimal(ix, A, nrow, ncol);
+}
+
diff --git a/src/match.h b/src/match.h
new file mode 100644
index 0000000..e6d7d73
--- /dev/null
+++ b/src/match.h
@@ -0,0 +1,43 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef MATCHER_H
+#define MATCHER_H
+
+/**
+ * Special implementation of the hungarian algorithm.
+ * The maximum number of fingers matches a uint32.
+ * Bitmasks are used extensively.
+ */
+
+#include "common.h"
+
+void match_fingers(int index[DIM_FINGER], int A[DIM2_FINGER],
+ int nrow, int ncol);
+
+#endif
diff --git a/src/state.h b/src/state.h
new file mode 100644
index 0000000..37ac803
--- /dev/null
+++ b/src/state.h
@@ -0,0 +1,62 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#ifndef MTDEV_STATE_H
+#define MTDEV_STATE_H
+
+#include "iobuf.h"
+#include "evbuf.h"
+
+/*
+ * struct mtdev_slot - represents the state of an input MT slot
+ * @abs: current values of ABS_MT axes for this slot
+ */
+struct mtdev_slot {
+ int abs[MT_ABS_SIZE];
+};
+
+/*
+ * struct mtdev_state - MT slot parsing
+ * @inbuf: input event buffer
+ * @outbuf: output event buffer
+ * @data: array of scratch slot data
+ * @used: bitmask of currently used slots
+ * @slot: slot currently being modified
+ * @lastid: last used tracking id
+ */
+struct mtdev_state {
+ struct mtdev_iobuf iobuf;
+ struct mtdev_evbuf inbuf;
+ struct mtdev_evbuf outbuf;
+ struct mtdev_slot data[DIM_FINGER];
+ bitmask_t used;
+ bitmask_t slot;
+ bitmask_t lastid;
+};
+
+#endif
diff --git a/test/mtdev-mapgen.c b/test/mtdev-mapgen.c
new file mode 100644
index 0000000..823ce96
--- /dev/null
+++ b/test/mtdev-mapgen.c
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include <mtdev-mapping.h>
+#include <stdio.h>
+
+#define BIT_DEF(name) \
+ printf("#define MTDEV_"#name"\t%d\n", \
+ cabs2mt[ABS_MT_##name] - 1)
+
+static unsigned int cabs2mt[ABS_CNT];
+static unsigned int cmt2abs[MT_ABS_SIZE];
+
+void init_caps()
+{
+ static const int init_abs_map[MT_ABS_SIZE] = MT_SLOT_ABS_EVENTS;
+ int i;
+ for (i = 0; i < MT_ABS_SIZE; i++) {
+ cabs2mt[init_abs_map[i]] = i + 1;
+ cmt2abs[i] = init_abs_map[i];
+ }
+}
+
+static inline const char *newln(int i, int n)
+{
+ return i == n - 1 || i % 8 == 7 ? "\n" : "";
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ init_caps();
+ printf("static const unsigned int mtdev_map_abs2mt[ABS_CNT] = {\n");
+ for (i = 0; i < ABS_CNT; i++)
+ printf(" 0x%04x,%s", cabs2mt[i], newln(i, ABS_CNT));
+ printf("};\n\n");
+ printf("static const unsigned int mtdev_map_mt2abs[MT_ABS_SIZE] = {\n");
+ for (i = 0; i < MT_ABS_SIZE; i++)
+ printf(" 0x%04x,%s", cmt2abs[i], newln(i, MT_ABS_SIZE));
+ printf("};\n\n");
+ BIT_DEF(TRACKING_ID);
+ BIT_DEF(POSITION_X);
+ BIT_DEF(POSITION_Y);
+ BIT_DEF(TOUCH_MAJOR);
+ BIT_DEF(TOUCH_MINOR);
+ BIT_DEF(WIDTH_MAJOR);
+ BIT_DEF(WIDTH_MINOR);
+ BIT_DEF(ORIENTATION);
+ printf("\n");
+ return 0;
+}
diff --git a/test/mtdev.c b/test/mtdev.c
new file mode 100644
index 0000000..995a4e2
--- /dev/null
+++ b/test/mtdev.c
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ *
+ * mtdev - MT device event converter (MIT license)
+ *
+ * Copyright (C) 2010 Henrik Rydberg <rydberg@euromail.se>
+ * Copyright (C) 2010 Canonical Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ ****************************************************************************/
+
+#include <mtdev-mapping.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+/* year-proof millisecond event time */
+typedef __u64 mstime_t;
+
+static int use_event(const struct input_event *ev)
+{
+#if 0
+ return ev->type == EV_ABS && mtdev_is_absmt(ev->code);
+#else
+ return 1;
+#endif
+}
+
+static void print_event(const struct input_event *ev)
+{
+ static const mstime_t ms = 1000;
+ static int slot;
+ mstime_t evtime = ev->time.tv_usec / ms + ev->time.tv_sec * ms;
+ if (ev->type == EV_ABS && ev->code == ABS_MT_SLOT)
+ slot = ev->value;
+ fprintf(stderr, "%012llx %02d %01d %04x %d\n",
+ evtime, slot, ev->type, ev->code, ev->value);
+}
+
+static void loop_device(int fd)
+{
+ struct mtdev dev;
+ struct input_event ev;
+ int ret = mtdev_open(&dev, fd);
+ if (ret) {
+ fprintf(stderr, "error: could not open device: %d\n", ret);
+ return;
+ }
+ while (mtdev_pull(&dev, fd, 1) > 0) {
+ while (!mtdev_empty(&dev)) {
+ mtdev_get(&dev, &ev);
+ if (use_event(&ev))
+ print_event(&ev);
+ }
+ }
+ mtdev_close(&dev);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ if (argc < 2) {
+ fprintf(stderr, "Usage: mtdev <device>\n");
+ return -1;
+ }
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "error: could not open device\n");
+ return -1;
+ }
+ if (ioctl(fd, EVIOCGRAB, 1)) {
+ fprintf(stderr, "error: could not grab the device\n");
+ return -1;
+ }
+ loop_device(fd);
+ ioctl(fd, EVIOCGRAB, 0);
+ close(fd);
+ return 0;
+}