shithub: riscv

Download patch

ref: 06783edd7a66eea0285c8a7ad2fd03e5577b80ef
parent: 76812c9bb71193b7a9a4d059890915535ef49441
author: cinap_lenrek <[email protected]>
date: Wed Nov 14 04:12:34 EST 2018

nusb/kb: multitouch support (touchscreens, stylus)

touchscreens signal multiple contact points (X/Y) in
the hid descriptor separated by being nested in separate
collections. the contact point is identified by a
optional contact id. if omited, we use the collection
index and report id.

so we collect all the items (X/Y, buttons, wheel) from
separate collections in Hidslot structures and in the
end combine all the slots together.

buttons are or'ed together while absolute X/Y is applied
when it changed. relative X/Y deltas get added together.

thanks to kivik and Glats for testing.

--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -46,8 +46,16 @@
 };
 
 typedef struct Hidreport Hidreport;
-struct Hidreport
+typedef struct Hidslot Hidslot;
+struct Hidslot
 {
+	int	valid;
+	int	usage;
+	int	id;
+	int	oor;
+
+	int	abs;	/* for xyz */
+
 	int	x;
 	int	y;
 	int	z;
@@ -55,9 +63,15 @@
 	int	b;
 	int	m;
 
-	int	absz;
-	u8int	abs;
+	int	w;
+	int	h;
+};
 
+struct Hidreport
+{
+	int	ns;
+	Hidslot	s[16];
+
 	int	nk;
 	uchar	k[64];
 
@@ -222,7 +236,14 @@
 			switch(t){
 			case Collection:
 				memset(l, 0, Nl*sizeof(l[0]));
+				i = l[Nu] | g[UsagPg]<<16;
+				l[Usage] = i;
+				(*f)(t, v, g, l, c, a);
+
 				d = repparse1(d, e, g, l, v, f, a);
+
+				l[Usage] = i;
+				(*f)(CollectionEnd, v, g, l, c, a);
 				continue;
 			case CollectionEnd:
 				return d;
@@ -495,17 +516,40 @@
 hidparse(int t, int f, int g[], int l[], int, void *a)
 {
 	Hidreport *p = a;
+	Hidslot *s = &p->s[p->ns];
 	int v, m;
 
-	if(t != Input)
+	switch(t){
+	case Input:
+		if(g[RepId] != 0){
+			if(p->p[0] != g[RepId]){
+				p->o = 0;
+				return;
+			}
+			if(p->o < 8)
+				p->o = 8;	/* skip report id byte */
+		}
+		break;
+	case Collection:
+		if(g[RepId] != 0 && p->p[0] != g[RepId])
+			return;
+		if(s->valid && p->ns < nelem(p->s)-1)
+			s = &p->s[++p->ns];
+		memset(s, 0, sizeof(*s));
+		s->usage = l[Usage];
+		s->id = (p->ns+1)<<8 | g[RepId];
 		return;
-	if(g[RepId] != 0){
-		if(p->p[0] != g[RepId]){
-			p->o = 0;
+	case CollectionEnd:
+		if(g[RepId] != 0 && p->p[0] != g[RepId])
 			return;
-		}
-		if(p->o < 8)
-			p->o = 8;	/* skip report id byte */
+		if(!s->valid || s->usage != l[Usage])
+			return;
+		/* if out of range or touchscreen finger not touching, ignore the slot */
+		if(s->oor || s->usage == 0x0D0022 && s->b == 0)
+			s->valid = 0;
+		return;
+	default:
+		return;
 	}
 	v = getbits(p->p, p->e, g[RepSize], p->o);
 	p->o += g[RepSize];
@@ -540,6 +584,9 @@
 			v -= (g[PhysMax] * (g[LogiMax] - g[LogiMin])) / (g[PhysMax] - g[PhysMin]);
 
 		switch(l[Usage]){
+		default:
+			return;
+
 		case 0x090001:
 		case 0x090002:
 		case 0x090003:
@@ -549,35 +596,59 @@
 		case 0x090007:
 		case 0x090008:
 			m = 1<<(l[Usage] - 0x090001);
-			p->m |= m;
-			p->b &= ~m;
+		Button:
+			s->m |= m;
+			s->b &= ~m;
 			if(v != 0)
-				p->b |= m;
+				s->b |= m;
 			break;
+
+		case 0x0D0032:	/* In Range */
+			s->oor = !v;
+			break;
+
+		case 0x0D0042:	/* Tip Switch */
+			m = 1;
+			goto Button;
+		case 0x0D0044:	/* Barrel Switch */
+			m = 2;
+			goto Button;
+		case 0x0D0045:	/* Eraser */
+			m = 4;
+			goto Button;
+
+		case 0x0D0048:	/* Contact width */
+			s->w = v;
+			break;
+		case 0x0D0049:	/* Contact height */
+			s->h = v;
+			break;
+
+		case 0x0D0051:	/* Conteact identifier */
+			s->id = v;
+			break;
+
 		case 0x010030:
 			if((f & (Fabs|Frel)) == Fabs){
 				v = ((vlong)(v - g[LogiMin]) << 31) / (g[LogiMax] - g[LogiMin]);
-				p->abs = 1;
+				s->abs |= 1;
 			}
-			p->x = v;
+			s->x = v;
 			break;
 		case 0x010031:
 			if((f & (Fabs|Frel)) == Fabs){
 				v = ((vlong)(v - g[LogiMin]) << 31) / (g[LogiMax] - g[LogiMin]);
-				p->abs = 1;
+				s->abs |= 2;
 			}
-			p->y = v;
+			s->y = v;
 			break;
 		case 0x010038:
-			if((f & (Fabs|Frel)) == Fabs){
-				p->z = v - p->absz;
-				p->absz = v;
-			} else {
-				p->z = v;
-				p->absz += v;
-			}
+			if((f & (Fabs|Frel)) == Fabs)
+				s->abs |= 4;
+			s->z = v;
 			break;
 		}
+		s->valid = 1;
 	}
 }
 
@@ -600,14 +671,17 @@
 {
 	char	err[ERRMAX], mbuf[80];
 	uchar	lastk[64], uk, dk;
-	int	i, c, b, nerrs, lastb, nlastk;
+	int	i, c, nerrs, lastb, nlastk;
+	int	abs, x, y, z, b;
 	Hidreport p;
-	Hiddev*	f = a;
+	Hidslot lasts[nelem(p.s)], *s, *l;
+	Hiddev *f = a;
 
 	threadsetname("readerproc %s", f->ep->dir);
 	sethipri();
 
 	memset(&p, 0, sizeof(p));
+	memset(lasts, 0, sizeof(lasts));
 	lastb = nlastk = nerrs = 0;
 
 	for(;;){
@@ -634,9 +708,13 @@
 
 		p.o = 0;
 		p.e = p.p + c;
-		p.abs = 0;
+		p.ns = 0;
+		memset(p.s, 0, sizeof(p.s[0]));
 		repparse(f->rep, f->rep+f->nrep, hidparse, &p);
+		if(p.s[p.ns].valid)
+			p.ns++;
 
+		/* handle keyboard report */
 		if(p.nk != 0 || nlastk != 0){
 			if(debug){
 				fprint(2, "kbd: ");
@@ -678,32 +756,75 @@
 			p.nk = 0;
 		}
 
-		/* map mouse buttons */
-		b = p.b & 1;
-		if(p.b & (4|8))
-			b |= 2;
-		if(p.b & 2)
-			b |= 4;
-		if(p.z != 0)
-			b |= (p.z > 0) ? 8 : 16;
+		/* handle mouse/touchpad */
+		if(p.ns == 0)
+			continue;
 
-		if(p.abs || p.x != 0 || p.y != 0 || p.z != 0 || b != lastb){
-			if(debug)
-				if(p.abs)
-					fprint(2, "ptr: b=%x m=%x x=%f y=%f z=%d\n", p.b, p.m, (uint)p.x / 2147483648.0, (uint)p.y / 2147483648.0, p.z);
+		/* combine all the slots */
+		abs = x = y = b = 0;
+		for(i=0; i<p.ns; *l = *s, i++){
+			s = &p.s[i];
+
+			/* find the last slot of the same id */
+			for(l = lasts; l->valid && l < &lasts[nelem(lasts)-1]; l++)
+				if(l->usage == s->usage && l->id == s->id)
+					break;
+			if(l == &lasts[nelem(lasts)-1] || !l->valid)
+				*l = *s;
+
+			/* convet absolute z to relative */
+			z = s->z;
+			if(s->abs & 4)
+				z -= l->z;
+
+			if(debug) {
+				if((s->abs & 3) == 3)
+					fprint(2, "ptr[%d]: id=%x b=%x m=%x x=%f y=%f z=%d\n",
+						i, s->id, s->b, s->m,
+						(uint)s->x / 2147483648.0,
+						(uint)s->y / 2147483648.0,
+						z);
 				else
-					fprint(2, "ptr: b=%x m=%x x=%d y=%d z=%d\n", p.b, p.m, p.x, p.y, p.z);
+					fprint(2, "ptr[%d]: id=%x b=%x m=%x x=%d y=%d z=%d\n",
+						i, s->id, s->b, s->m, s->x, s->y, z);
+			}
 
+			/* map to mouse buttons */
+			b |= s->b & 1;
+			if(s->b & (4|8))
+				b |= 2;
+			if(s->b & 2)
+				b |= 4;
+			if(z != 0)
+				b |= z > 0 ? 8 : 16;
+
+			/* X/Y are absolute? */
+			if((s->abs & 3) == 3){
+				/* ignore absolute position when nothing changed */
+				if(s->abs == l->abs && s->x == l->x && s->y == l->y && s->b == l->b)
+					continue;
+				abs = 1;
+				x = s->x;
+				y = s->y;
+			} else {
+				/* everything needs to be relative */
+				if((s->abs & 3) != 0 || abs)
+					continue;
+				x += s->x;
+				y += s->y;
+			}
+		}
+
+		if(abs || x != 0 || y != 0 || b != lastb){
+			lastb = b;
+
 			if(f->minfd < 0){
 				f->minfd = open("/dev/mousein", OWRITE);
 				if(f->minfd < 0)
 					hdfatal(f, "open /dev/mousein");
 			}
-
-			seprint(mbuf, mbuf+sizeof(mbuf), "%c%11d %11d %11d", "ma"[p.abs], p.x, p.y, b);
+			seprint(mbuf, mbuf+sizeof(mbuf), "%c%11d %11d %11d", "ma"[abs], x, y, b);
 			write(f->minfd, mbuf, strlen(mbuf));
-
-			lastb = b;
 		}
 	}
 }