ref: f7678a299254ec33beb22fc52484964c7f92ffb5
parent: 677b3b8a881031e1bc2ef25e413b67f15eb119c6
	author: cinap_lenrek <cinap_lenrek@felloff.net>
	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;
}
}
}
--
⑨