From owner-dev-commits-src-all@freebsd.org Wed Sep 8 00:02:31 2021 Return-Path: Delivered-To: dev-commits-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id ED1716700AE; Wed, 8 Sep 2021 00:02:31 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4H42Py5pL6z4WWT; Wed, 8 Sep 2021 00:02:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 094BC1BBF4; Wed, 8 Sep 2021 00:02:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 18802TwZ050389; Wed, 8 Sep 2021 00:02:29 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 18802Tls050388; Wed, 8 Sep 2021 00:02:29 GMT (envelope-from git) Date: Wed, 8 Sep 2021 00:02:29 GMT Message-Id: <202109080002.18802Tls050388@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Vladimir Kondratyev Subject: git: b79de251fc60 - stable/13 - evdev: Import support for touch-tracking. MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: wulf X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: b79de251fc60429e3f5e0939eb001163d2a616f4 Auto-Submitted: auto-generated X-BeenThere: dev-commits-src-all@freebsd.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Commit messages for all branches of the src repository List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Sep 2021 00:02:32 -0000 The branch stable/13 has been updated by wulf: URL: https://cgit.FreeBSD.org/src/commit/?id=b79de251fc60429e3f5e0939eb001163d2a616f4 commit b79de251fc60429e3f5e0939eb001163d2a616f4 Author: Vladimir Kondratyev AuthorDate: 2021-08-24 22:50:53 +0000 Commit: Vladimir Kondratyev CommitDate: 2021-09-07 23:59:12 +0000 evdev: Import support for touch-tracking. Touch tracking is a process of assignment of unique trackingID to each initiated contact on the surface. Keeping the trackingIDs persistent across multitouch reports requires solving of so called Euclidian Bipartite Matching problem. This commit imports EBM-solver implementation based on Dinitz-Kronrod algorithm to find minimum cost matching between contacts listed in two consecutive reports. Obtained from: OpenBSD (cherry picked from commit 4c0a134e32a7f4dec556fea15c8de22f69864492) --- sys/dev/evdev/evdev.h | 3 + sys/dev/evdev/evdev_mt.c | 211 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 212 insertions(+), 2 deletions(-) diff --git a/sys/dev/evdev/evdev.h b/sys/dev/evdev/evdev.h index e1c5aedb029c..4cf885612c3e 100644 --- a/sys/dev/evdev/evdev.h +++ b/sys/dev/evdev/evdev.h @@ -91,6 +91,7 @@ extern int evdev_sysmouse_t_axis; #define EVDEV_FLAG_EXT_EPOCH 0x03 /* evdev_push_* is allways called with * input (global) epoch entered */ #define EVDEV_FLAG_MT_KEEPID 0x04 /* Do not reassign tracking ID */ +#define EVDEV_FLAG_MT_TRACK 0x05 /* Assign touch to slot by evdev */ #define EVDEV_FLAG_MAX 0x1F #define EVDEV_FLAG_CNT (EVDEV_FLAG_MAX + 1) @@ -159,6 +160,8 @@ int evdev_get_mt_slot_by_tracking_id(struct evdev_dev *, int32_t); void evdev_support_mt_compat(struct evdev_dev *); void evdev_push_mt_compat(struct evdev_dev *); int evdev_mt_push_slot(struct evdev_dev *, int, union evdev_mt_slot *); +int evdev_mt_push_frame(struct evdev_dev *, union evdev_mt_slot *, int); +void evdev_mt_match_frame(struct evdev_dev *, union evdev_mt_slot *, int); void evdev_mt_push_autorel(struct evdev_dev *); static inline int evdev_mt_id_to_slot(struct evdev_dev *evdev, int32_t id) diff --git a/sys/dev/evdev/evdev_mt.c b/sys/dev/evdev/evdev_mt.c index 1a600fe3480d..f61943604a3a 100644 --- a/sys/dev/evdev/evdev_mt.c +++ b/sys/dev/evdev/evdev_mt.c @@ -25,6 +25,21 @@ * * $FreeBSD$ */ +/*- + * Copyright (c) 2015, 2016 Ulf Brosziewski + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ #include #include @@ -69,6 +84,7 @@ struct evdev_mt { slotset_t touches; /* the set of slots with unsynchronized state */ slotset_t frame; + int *matrix; union evdev_mt_slot slots[]; }; @@ -84,12 +100,24 @@ ffc_slot(struct evdev_dev *evdev, slotset_t slots) void evdev_mt_init(struct evdev_dev *evdev) { + struct evdev_mt *mt; + size_t size = offsetof(struct evdev_mt, slots); int slot, slots; slots = MAXIMAL_MT_SLOT(evdev) + 1; + size += sizeof(mt->slots[0]) * slots; + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) { + size += sizeof(mt->matrix[0]) * (slots + 6) * slots; + } + + mt = malloc(size, M_EVDEV, M_WAITOK | M_ZERO); + evdev->ev_mt = mt; - evdev->ev_mt = malloc(offsetof(struct evdev_mt, slots) + - sizeof(union evdev_mt_slot) * slots, M_EVDEV, M_WAITOK | M_ZERO); + if (bit_test(evdev->ev_flags, EVDEV_FLAG_MT_TRACK)) { + evdev_support_abs(evdev, + ABS_MT_TRACKING_ID, -1, slots - 1, 0, 0, 0); + mt->matrix = (int *)(mt->slots + slots); + } /* Initialize multitouch protocol type B states */ for (slot = 0; slot < slots; slot++) @@ -162,6 +190,185 @@ evdev_mt_push_slot(struct evdev_dev *evdev, int slot, return (0); } +/* + * Find a minimum-weight matching for an m-by-n matrix. + * + * m must be greater than or equal to n. The size of the buffer must be + * at least 3m + 3n. + * + * On return, the first m elements of the buffer contain the row-to- + * column mappings, i.e., buffer[i] is the column index for row i, or -1 + * if there is no assignment for that row (which may happen if n < m). + * + * Wrong results because of overflows will not occur with input values + * in the range of 0 to INT_MAX / 2 inclusive. + * + * The function applies the Dinic-Kronrod algorithm. It is not modern or + * popular, but it seems to be a good choice for small matrices at least. + * The original form of the algorithm is modified as follows: There is no + * initial search for row minima, the initial assignments are in a + * "virtual" column with the index -1 and zero values. This permits inputs + * with n < m, and it simplifies the reassignments. + */ +static void +evdev_mt_matching(int *matrix, int m, int n, int *buffer) +{ + int i, j, k, d, e, row, col, delta; + int *p; + int *r2c = buffer; /* row-to-column assignments */ + int *red = r2c + m; /* reduced values of the assignments */ + int *mc = red + m; /* row-wise minimal elements of cs */ + int *cs = mc + m; /* the column set */ + int *c2r = cs + n; /* column-to-row assignments in cs */ + int *cd = c2r + n; /* column deltas (reduction) */ + + for (p = r2c; p < red; *p++ = -1) {} + for (; p < mc; *p++ = 0) {} + for (col = 0; col < n; col++) { + delta = INT_MAX; + for (i = 0, p = matrix + col; i < m; i++, p += n) { + d = *p - red[i]; + if (d < delta || (d == delta && r2c[i] < 0)) { + delta = d; + row = i; + } + } + cd[col] = delta; + if (r2c[row] < 0) { + r2c[row] = col; + continue; + } + for (p = mc; p < cs; *p++ = col) {} + for (k = 0; (j = r2c[row]) >= 0;) { + cs[k++] = j; + c2r[j] = row; + mc[row] -= n; + delta = INT_MAX; + for (i = 0, p = matrix; i < m; i++, p += n) + if (mc[i] >= 0) { + d = p[mc[i]] - cd[mc[i]]; + e = p[j] - cd[j]; + if (e < d) { + d = e; + mc[i] = j; + } + d -= red[i]; + if (d < delta || (d == delta + && r2c[i] < 0)) { + delta = d; + row = i; + } + } + cd[col] += delta; + for (i = 0; i < k; i++) { + cd[cs[i]] += delta; + red[c2r[cs[i]]] -= delta; + } + } + for (j = mc[row]; (r2c[row] = j) != col;) { + row = c2r[j]; + j = mc[row] + n; + } + } +} + +/* + * Assign tracking IDs to the points in the pt array. The tracking ID + * assignment pairs the points with points of the previous frame in + * such a way that the sum of the squared distances is minimal. Using + * squares instead of simple distances favours assignments with more uniform + * distances, and it is faster. + * Set tracking id to -1 for unassigned (new) points. + */ +void +evdev_mt_match_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, + int size) +{ + struct evdev_mt *mt = evdev->ev_mt; + int i, j, m, n, dx, dy, slot, num_touches; + int *p, *r2c, *c2r; + + EVDEV_LOCK_ASSERT(evdev); + MPASS(mt->matrix != NULL); + MPASS(size >= 0 && size <= MAXIMAL_MT_SLOT(evdev) + 1); + + if (size == 0) + return; + + p = mt->matrix; + num_touches = bitcount(mt->touches); + if (num_touches >= size) { + FOREACHBIT(mt->touches, slot) + for (i = 0; i < size; i++) { + dx = pt[i].x - mt->slots[slot].x; + dy = pt[i].y - mt->slots[slot].y; + *p++ = dx * dx + dy * dy; + } + m = num_touches; + n = size; + } else { + for (i = 0; i < size; i++) + FOREACHBIT(mt->touches, slot) { + dx = pt[i].x - mt->slots[slot].x; + dy = pt[i].y - mt->slots[slot].y; + *p++ = dx * dx + dy * dy; + } + m = size; + n = num_touches; + } + evdev_mt_matching(mt->matrix, m, n, p); + + r2c = p; + c2r = p + m; + for (i = 0; i < m; i++) + if ((j = r2c[i]) >= 0) + c2r[j] = i; + + p = (n == size ? c2r : r2c); + for (i = 0; i < size; i++) + if (*p++ < 0) + pt[i].id = -1; + + p = (n == size ? r2c : c2r); + FOREACHBIT(mt->touches, slot) + if ((i = *p++) >= 0) + pt[i].id = mt->tracking_ids[slot]; +} + +static void +evdev_mt_send_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, int size) +{ + struct evdev_mt *mt = evdev->ev_mt; + union evdev_mt_slot *slot; + + EVDEV_LOCK_ASSERT(evdev); + MPASS(size >= 0 && size <= MAXIMAL_MT_SLOT(evdev) + 1); + + /* + * While MT-matching assign tracking IDs of new contacts to be equal + * to a slot number to make things simpler. + */ + for (slot = pt; slot < pt + size; slot++) { + if (slot->id < 0) + slot->id = ffc_slot(evdev, mt->touches | mt->frame); + if (slot->id >= 0) + evdev_mt_send_slot(evdev, slot->id, slot); + } +} + +int +evdev_mt_push_frame(struct evdev_dev *evdev, union evdev_mt_slot *pt, int size) +{ + if (size < 0 || size > MAXIMAL_MT_SLOT(evdev) + 1) + return (EINVAL); + + EVDEV_ENTER(evdev); + evdev_mt_send_frame(evdev, pt, size); + EVDEV_EXIT(evdev); + + return (0); +} + int evdev_mt_get_last_slot(struct evdev_dev *evdev) {