diff options
author | Henrik Rydberg <rydberg@euromail.se> | 2010-06-17 18:12:58 +0200 |
---|---|---|
committer | Henrik Rydberg <rydberg@euromail.se> | 2010-06-17 18:12:58 +0200 |
commit | 66e5de9eaefc33ffa6af3617f9ec7a50f10af50d (patch) | |
tree | 66535311e0b9c422b30341237fd596fb9ebbdd75 | |
download | mtdev-git-66e5de9eaefc33ffa6af3617f9ec7a50f10af50d.tar.gz |
Initial load of mtdev project
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | COPYING | 23 | ||||
-rw-r--r-- | Makefile | 53 | ||||
-rw-r--r-- | README | 9 | ||||
-rw-r--r-- | include/mtdev-mapping.h | 76 | ||||
-rw-r--r-- | include/mtdev.h | 225 | ||||
-rw-r--r-- | src/caps.c | 103 | ||||
-rw-r--r-- | src/common.h | 87 | ||||
-rw-r--r-- | src/core.c | 393 | ||||
-rw-r--r-- | src/evbuf.h | 64 | ||||
-rw-r--r-- | src/iobuf.c | 69 | ||||
-rw-r--r-- | src/iobuf.h | 42 | ||||
-rw-r--r-- | src/match.c | 392 | ||||
-rw-r--r-- | src/match.h | 43 | ||||
-rw-r--r-- | src/state.h | 62 | ||||
-rw-r--r-- | test/mtdev-mapgen.c | 76 | ||||
-rw-r--r-- | test/mtdev.c | 95 |
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 @@ -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)" @@ -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; +} |