/* * Input Multitouch Library * * Copyright (c) 2008-2010 Henrik Rydberg * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. */ #include "input-mt.h" #define TRKID_BITS 16 #define TRKID_MAX ((1 << TRKID_BITS) - 1) #define TRKID_SGN (1 << (TRKID_BITS - 1)) static int trkid; // destined for input_dev int input_mt_create_slots_new(struct input_dev *dev, unsigned int num_slots) { int ret; ret = input_mt_create_slots(dev, num_slots); if (ret) return ret; input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0); return 0; } /** * input_mt_report_state() - report contact state * @dev: input device with allocated MT slots * @active: true if contact is active, false otherwise * * Reports an active touch via ABS_MT_TRACKING_ID. If active is * true and the slot is currently inactive, a new tracking id is * assigned to the slot. */ void input_mt_report_state(struct input_dev *dev, bool active) { int id = -1; if (dev->mt && active) { id = input_mt_get_value(&dev->mt[dev->slot], ABS_MT_TRACKING_ID); if (id < 0) id = trkid++ & TRKID_MAX; } input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id); } /** * input_mt_report_emulation() - common pointer emulation * @dev: input device with allocated MT slots * * Performs legacy emulation emulation via BTN_TOUCH, etc. */ void input_mt_report_emulation(struct input_dev *dev) { struct input_mt_slot *oldest = 0; int oldid = -1; int count = 0; int i; for (i = 0; i < dev->mtsize; ++i) { struct input_mt_slot *slot = &dev->mt[i]; int id = input_mt_get_value(slot, ABS_MT_TRACKING_ID); if (id < 0) continue; if (!oldest || ((id - oldid) & TRKID_SGN)) { oldest = slot; oldid = id; } count++; } input_event(dev, EV_KEY, BTN_TOUCH, count > 0); input_event(dev, EV_KEY, BTN_TOOL_FINGER, count == 1); input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2); input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3); input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count > 3); if (oldest) { int x = input_mt_get_value(oldest, ABS_MT_POSITION_X); int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y); int p = input_mt_get_value(oldest, ABS_MT_PRESSURE); input_event(dev, EV_ABS, ABS_X, x); input_event(dev, EV_ABS, ABS_Y, y); input_event(dev, EV_ABS, ABS_PRESSURE, p); } else { input_event(dev, EV_ABS, ABS_PRESSURE, 0); } } /* generated by mtdev-kernel - do not edit */ static const u8 match_data[] = { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 2, 1, 1, 0, 2, 2, 1, 2, 0, 0, 0, 1, 2, 3, 1, 1, 0, 2, 3, 2, 1, 2, 0, 3, 3, 1, 2, 3, 0, 0, 0, 1, 1, 1, 2, 1, 0, 0, 3, 0, 1, 1, 3, 1, 0, 2, 2, 3, 1, 2, 0, 0, 4, 0, 1, 2, 2, 4, 2, 1, 0, 0, 5, 0, 2, 1, 1, 5, 2, 0, 1, 1, 4, 1, 0, 2, 3, 2, 4, 1, 2, 0, 3, 3, 4, 1, 2, 3, 0, 0, 5, 0, 1, 2, 3, 2, 5, 2, 1, 0, 3, 3, 5, 2, 1, 3, 0, 0, 6, 0, 2, 1, 3, 1, 6, 2, 0, 1, 3, 3, 6, 2, 3, 1, 0, 0, 7, 0, 2, 3, 1, 1, 7, 2, 0, 3, 1, 2, 7, 2, 3, 0, 1, 0, 0, 1, 1, 2, 2, 1, 2, 1, 0, 0, 3, 0, 1, 1, 4, 2, 0, 3, 4, 2, 1, 0, 5, 0, 2, 2, 5, 1, 2, 2, 4, 6, 2, 1, 0, 1, 5, 6, 2, 0, 1, 2, 3, 7, 1, 2, 0, 0, 5, 7, 0, 2, 1, 1, 3, 8, 1, 0, 2, 0, 4, 8, 0, 1, 2, 2, 5, 8, 2, 1, 0, 3, 3, 5, 8, 2, 1, 3, 0, 1, 6, 8, 2, 0, 1, 3, 3, 6, 8, 2, 3, 1, 0, 1, 7, 8, 2, 0, 3, 1, 2, 7, 8, 2, 3, 0, 1, 2, 4, 9, 1, 2, 0, 3, 3, 4, 9, 1, 2, 3, 0, 0, 6, 9, 0, 2, 1, 3, 3, 6, 9, 3, 2, 1, 0, 0, 7, 9, 0, 2, 3, 1, 2, 7, 9, 3, 2, 0, 1, 1, 4, 10, 1, 0, 2, 3, 3, 4, 10, 1, 3, 2, 0, 0, 5, 10, 0, 1, 2, 3, 3, 5, 10, 3, 1, 2, 0, 0, 7, 10, 0, 3, 2, 1, 1, 7, 10, 3, 0, 2, 1, 1, 4, 11, 1, 0, 3, 2, 2, 4, 11, 1, 3, 0, 2, 0, 5, 11, 0, 1, 3, 2, 2, 5, 11, 3, 1, 0, 2, 0, 6, 11, 0, 3, 1, 2, 1, 6, 11, 3, 0, 1, 2, 0, 0, 1, 1, 2, 2, 3, 3, 1, 2, 1, 0, 0, 3, 0, 1, 1, 4, 2, 0, 3, 4, 2, 1, 0, 5, 0, 2, 2, 5, 1, 2, 1, 6, 3, 0, 3, 6, 3, 1, 5, 6, 3, 2, 0, 7, 0, 3, 2, 7, 1, 3, 4, 7, 2, 3, 2, 4, 6, 2, 1, 0, 1, 5, 6, 2, 0, 1, 2, 3, 7, 1, 2, 0, 0, 5, 7, 0, 2, 1, 1, 3, 8, 1, 0, 2, 0, 4, 8, 0, 1, 2, 2, 4, 9, 3, 1, 0, 1, 5, 9, 3, 0, 1, 2, 7, 9, 3, 2, 0, 5, 7, 9, 3, 2, 1, 1, 8, 9, 3, 0, 2, 4, 8, 9, 3, 1, 2, 2, 3, 10, 1, 3, 0, 0, 5, 10, 0, 3, 1, 2, 6, 10, 2, 3, 0, 5, 6, 10, 2, 3, 1, 0, 8, 10, 0, 3, 2, 3, 8, 10, 1, 3, 2, 1, 3, 11, 1, 0, 3, 0, 4, 11, 0, 1, 3, 1, 6, 11, 2, 0, 3, 4, 6, 11, 2, 1, 3, 0, 7, 11, 0, 2, 3, 3, 7, 11, 1, 2, 3, 3, 6, 9, 12, 3, 2, 1, 0, 2, 7, 9, 12, 3, 2, 0, 1, 3, 5, 10, 12, 3, 1, 2, 0, 1, 7, 10, 12, 3, 0, 2, 1, 2, 5, 11, 12, 3, 1, 0, 2, 1, 6, 11, 12, 3, 0, 1, 2, 3, 6, 8, 13, 2, 3, 1, 0, 2, 7, 8, 13, 2, 3, 0, 1, 3, 4, 10, 13, 1, 3, 2, 0, 0, 7, 10, 13, 0, 3, 2, 1, 2, 4, 11, 13, 1, 3, 0, 2, 0, 6, 11, 13, 0, 3, 1, 2, 3, 5, 8, 14, 2, 1, 3, 0, 1, 7, 8, 14, 2, 0, 3, 1, 3, 4, 9, 14, 1, 2, 3, 0, 0, 7, 9, 14, 0, 2, 3, 1, 1, 4, 11, 14, 1, 0, 3, 2, 0, 5, 11, 14, 0, 1, 3, 2, 2, 5, 8, 15, 2, 1, 0, 3, 1, 6, 8, 15, 2, 0, 1, 3, 2, 4, 9, 15, 1, 2, 0, 3, 0, 6, 9, 15, 0, 2, 1, 3, 1, 4, 10, 15, 1, 0, 2, 3, 0, 5, 10, 15, 0, 1, 2, 3, }; /* generated by mtdev-kernel - do not edit */ static const int match_index[][5] = { { 0, 0, 1, 3, 6 }, { 10, 10, 12, 18, 30 }, { 50, 50, 54, 62, 92 }, { 164, 164, 170, 194, 230 }, { 398, 398, 406, 454, 598 }, { 790 } }; static void set_dist(u32 *dist, const struct trk_coord *b1, const struct trk_coord *e1, const struct trk_coord *b2, const struct trk_coord *e2) { const struct trk_coord *p, *q; for (p = b1; p != e1; p++) for (q = b2; q != e2; q++) *dist++ = abs(q->x - p->x) + abs(q->y - p->y); } const u8 *match_four(const struct trk_coord *old, int nslot, const struct trk_coord *pos, int npos) { u32 d[16], obj, t; const u8 *p, *b, *e; const int *at; set_dist(d, old, old + nslot, pos, pos + npos); at = &match_index[nslot][npos]; b = &match_data[at[0]]; e = &match_data[at[1]]; obj = UINT_MAX, p = b; switch (min(nslot, npos)) { case 1: for (; b != e; b += npos) { t = d[*b++]; if (t < obj) obj = t, p = b; } break; case 2: for (; b != e; b += npos) { t = d[*b++], t += d[*b++]; if (t < obj) obj = t, p = b; } break; case 3: for (; b != e; b += npos) { t = d[*b++], t += d[*b++], t += d[*b++]; if (t < obj) obj = t, p = b; } break; case 4: for (; b != e; b += npos) { t = d[*b++], t += d[*b++], t += d[*b++], t += d[*b++]; if (t < obj) obj = t, p = b; } break; } return p; } /** * input_mt_assign_slots_by_coord() - perform a best-match assignment * @dev: input device with allocated MT slots * @slots: the slot assignment to be filled * @coords: the coordinate array to match * @num_coords: number of coordinates * * Performs a best match against the current contacts and returns * the slot assignment list. New contacts are assigned to unused * slots. * * Returns zero on success, or negative error in case of failure. */ int input_mt_assign_slots_by_coord(struct input_dev *dev, int *slots, const struct trk_coord *coords, int num_coords) { struct trk_coord old[4]; int old2slot[4], nold, i; const u8 *p; if (!dev->mt) return -ENXIO; if (dev->mtsize < 2 || dev->mtsize > 4) return -ENXIO; if (num_coords > dev->mtsize) return -EINVAL; if (num_coords < 1) return 0; nold = 0; for (i = 0; i < dev->mtsize; i++) { const struct input_mt_slot *mt = &dev->mt[i]; if (input_mt_get_value(mt, ABS_MT_TRACKING_ID) < 0) continue; old[nold].x = input_mt_get_value(mt, ABS_MT_POSITION_X); old[nold].y = input_mt_get_value(mt, ABS_MT_POSITION_Y); old2slot[nold++] = i; } p = match_four(old, nold, coords, num_coords); for (i = 0; i < dev->mtsize; i++) if (input_mt_get_value(&dev->mt[i], ABS_MT_TRACKING_ID) < 0) old2slot[nold++] = i; for (i = 0; i < num_coords; i++) slots[i] = old2slot[p[i]]; return 0; } /** * input_mt_sync_slot_complement() - send touchup for remaining slots * @dev: input device with allocated MT slots * @slots: the slot assignment to be filled * @coords: the coordinate array to match * @num_coords: number of coordinates * * Performs a best match against the current contacts and returns * the slot assignment list. New contacts are assigned to unused * slots. * * Returns zero on success, or negative error in case of failure. */ void input_mt_sync_slot_complement(struct input_dev *dev, const int *slots, int num_slots) { unsigned long mask = 0; int i; for (i = 0; i < num_slots; i++) mask |= BIT(slots[i]); for (i = 0; i < dev->mtsize; i++) { if (mask & BIT(i)) continue; input_mt_slot(dev, i); input_mt_report_state(dev, false); } }