@daugasauron/dolly-host-touch 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
1
  # @daugasauron/dolly-host-touch
2
2
 
3
- Dolly's `touch@0` host module: fingers on a touchscreen: every one that is down, each with its id. Give the URL of its
3
+ Dolly's `touch@0` host module: fingers on a touchscreen, every one that is down with its id, and a program's buttons. Give the URL of its
4
4
  `module.json` to `boot()`'s `modules`; @daugasauron/dolly's README says how.
package/abi.mjs CHANGED
@@ -10,10 +10,17 @@ export const DOLLY_TOUCH_DOWN = 1;
10
10
  export const DOLLY_TOUCH_MOVE = 2;
11
11
  export const DOLLY_TOUCH_UP = 3;
12
12
  export const DOLLY_TOUCH_CANCEL = 4;
13
+ export const DOLLY_TOUCH_PRESS = 5;
13
14
  export const DOLLY_TOUCH_FINGERS = 10;
15
+ export const DOLLY_TOUCH_BUTTONS_OFFSET = 16400;
16
+ export const DOLLY_TOUCH_LABEL_COUNT_OFFSET = 16404;
17
+ export const DOLLY_TOUCH_LABELS_OFFSET = 16408;
18
+ export const DOLLY_TOUCH_BUTTON_COUNT = 12;
19
+ export const DOLLY_TOUCH_LABEL_SIZE = 32;
14
20
  export const DOLLY_TOUCH_ACQUIRE = 92;
15
21
  export const DOLLY_TOUCH_READ = 93;
16
22
  export const DOLLY_TOUCH_RELEASE = 94;
23
+ export const DOLLY_TOUCH_BUTTONS = 95;
17
24
  export const DOLLY_TOUCH_READ_RECORDS = 64;
18
25
  export const DOLLY_ERRNO = Object.freeze({ E2BIG: 1, EACCES: 2, EADDRINUSE: 3, EADDRNOTAVAIL: 4, EAFNOSUPPORT: 5, EAGAIN: 6, EALREADY: 7, EBADF: 8, EBADMSG: 9, EBUSY: 10, ECANCELED: 11, ECHILD: 12, ECONNABORTED: 13, ECONNREFUSED: 14, ECONNRESET: 15, EDEADLK: 16, EDESTADDRREQ: 17, EDOM: 18, EDQUOT: 19, EEXIST: 20, EFAULT: 21, EFBIG: 22, EHOSTUNREACH: 23, EIDRM: 24, EILSEQ: 25, EINPROGRESS: 26, EINTR: 27, EINVAL: 28, EIO: 29, EISCONN: 30, EISDIR: 31, ELOOP: 32, EMFILE: 33, EMLINK: 34, EMSGSIZE: 35, EMULTIHOP: 36, ENAMETOOLONG: 37, ENETDOWN: 38, ENETRESET: 39, ENETUNREACH: 40, ENFILE: 41, ENOBUFS: 42, ENODEV: 43, ENOENT: 44, ENOEXEC: 45, ENOLCK: 46, ENOLINK: 47, ENOMEM: 48, ENOMSG: 49, ENOPROTOOPT: 50, ENOSPC: 51, ENOSYS: 52, ENOTCONN: 53, ENOTDIR: 54, ENOTEMPTY: 55, ENOTRECOVERABLE: 56, ENOTSOCK: 57, ENOTTY: 59, ENXIO: 60, EOVERFLOW: 61, EOWNERDEAD: 62, EPERM: 63, EPIPE: 64, EPROTO: 65, EPROTONOSUPPORT: 66, EPROTOTYPE: 67, ERANGE: 68, EROFS: 69, ESPIPE: 70, ESRCH: 71, ESTALE: 72, ETIMEDOUT: 73, ETXTBSY: 74, EXDEV: 75, ENODATA: 116, ENOTSUP: 138 });
19
- export const DOLLY_TOUCH_ABI_DIGEST = "515bdcabf63b7396d485abded3a066c0c55771b0eedf4de824f3f728e645cf1c";
26
+ export const DOLLY_TOUCH_ABI_DIGEST = "a666fc4a91ca78e6a7f42e9357d4e3aa8586d6aa662049f7a590794408caa67d";
package/module.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "touch",
3
3
  "version": 0,
4
- "gives": "fingers on a touchscreen: every one that is down, each with its id",
5
- "dependencies": ["runtime@0"],
4
+ "gives": "fingers on a touchscreen, every one that is down with its id, and a program's buttons",
5
+ "dependencies": ["runtime@0", "input@0"],
6
6
  "phase": "image",
7
7
  "imports": [],
8
8
  "host": "touch.mjs",
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
- "version": "0.1.4",
2
+ "version": "0.1.5",
3
3
  "type": "module",
4
4
  "name": "@daugasauron/dolly-host-touch",
5
5
  "license": "MIT",
6
6
  "dependencies": {
7
- "@daugasauron/dolly": "0.1.4"
7
+ "@daugasauron/dolly": "0.1.5"
8
8
  },
9
- "description": "Dolly's touch@0 host module: fingers on a touchscreen: every one that is down, each with its id.",
9
+ "description": "Dolly's touch@0 host module: fingers on a touchscreen, every one that is down with its id, and a program's buttons.",
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "git+https://github.com/daugasauron/dolly.git"
package/touch.mjs CHANGED
@@ -5,6 +5,7 @@ const word = { read: T.DOLLY_TOUCH_WORD_READ, write: T.DOLLY_TOUCH_WORD_WRITE,
5
5
  lease: T.DOLLY_TOUCH_WORD_LEASE, enabled: T.DOLLY_TOUCH_WORD_ENABLED };
6
6
  // Moves leave room for every finger's down and its end.
7
7
  const moveReserve = 2 * T.DOLLY_TOUCH_FINGERS;
8
+ const buttonsEnd = T.DOLLY_TOUCH_LABELS_OFFSET + T.DOLLY_TOUCH_BUTTON_COUNT * T.DOLLY_TOUCH_LABEL_SIZE;
8
9
 
9
10
  // The Worker's end of the touch mailbox, the one producer of its records: the
10
11
  // page sends it the fingers it took. A finger is delivered under the lease its
@@ -16,7 +17,8 @@ export class TouchTransport {
16
17
  if (!(buffer instanceof SharedArrayBuffer)) throw new Error("Dolly touch transport requires shared Wasm memory");
17
18
  if (!Number.isSafeInteger(address) || address <= 0 || address % 4 !== 0 || !Number.isSafeInteger(capacity) ||
18
19
  capacity <= moveReserve || (capacity & (capacity - 1)) !== 0 ||
19
- address > buffer.byteLength - T.DOLLY_TOUCH_HEADER_SIZE - capacity * T.DOLLY_TOUCH_RECORD_SIZE) {
20
+ T.DOLLY_TOUCH_HEADER_SIZE + capacity * T.DOLLY_TOUCH_RECORD_SIZE > T.DOLLY_TOUCH_BUTTONS_OFFSET ||
21
+ address > buffer.byteLength - buttonsEnd) {
20
22
  throw new Error("Dolly supplied an invalid touch mailbox");
21
23
  }
22
24
  this.view = new DataView(buffer);
@@ -46,16 +48,23 @@ export class TouchTransport {
46
48
  Atomics.store(this.words, this.address / 4 + word.write, (write + 1) | 0);
47
49
  }
48
50
 
49
- // One Pointer Event of a finger as samples ({ x, y, pressure, time }, the
50
- // coalesced moves of a pointermove). Returns whether the finger is this
51
- // module's, so that it is not also the pointer.
52
- finger(phase, pointerId, samples) {
51
+ // The lease records are written under. A new one ends the fingers of the
52
+ // one before: they are nobody's until they end.
53
+ follow() {
53
54
  const lease = this.lease();
54
55
  if (lease !== this.heldLease) {
55
56
  for (const pointer of this.fingers.keys()) this.ignored.add(pointer);
56
57
  this.fingers.clear();
57
58
  this.heldLease = lease;
58
59
  }
60
+ return lease;
61
+ }
62
+
63
+ // One Pointer Event of a finger as samples ({ x, y, pressure, time }, the
64
+ // coalesced moves of a pointermove). Returns whether the finger is this
65
+ // module's, so that it is not also the pointer.
66
+ finger(phase, pointerId, samples) {
67
+ const lease = this.follow();
59
68
  const id = this.fingers.get(pointerId), ends = phase === T.DOLLY_TOUCH_UP || phase === T.DOLLY_TOUCH_CANCEL;
60
69
  if (id === undefined) {
61
70
  if (this.ignored.has(pointerId)) {
@@ -86,6 +95,33 @@ export class TouchTransport {
86
95
  return true;
87
96
  }
88
97
 
98
+ // The holder's labels and the labels word they were read under, or null
99
+ // while the kernel is changing them.
100
+ buttons() {
101
+ const word = (this.address + T.DOLLY_TOUCH_BUTTONS_OFFSET) / 4;
102
+ const labels = Atomics.load(this.words, word) >>> 0;
103
+ const count = Math.min(this.view.getUint32(this.address + T.DOLLY_TOUCH_LABEL_COUNT_OFFSET, true),
104
+ T.DOLLY_TOUCH_BUTTON_COUNT);
105
+ const texts = Array.from({ length: count }, (_, index) => {
106
+ const at = this.address + T.DOLLY_TOUCH_LABELS_OFFSET + index * T.DOLLY_TOUCH_LABEL_SIZE;
107
+ const bytes = new Uint8Array(this.view.buffer, at, T.DOLLY_TOUCH_LABEL_SIZE).slice();
108
+ return new TextDecoder().decode(bytes.subarray(0, bytes.indexOf(0) >>> 0));
109
+ });
110
+ return Atomics.load(this.words, word) >>> 0 === labels ? { labels, texts } : null;
111
+ }
112
+
113
+ // A press of the holder's button index, of the labels read under the word
114
+ // labels, while the ring keeps room for it and every finger's end.
115
+ press(index, labels, time) {
116
+ if (!this.follow()) return;
117
+ if (this.free() < this.fingers.size + 1) {
118
+ this.dropped++;
119
+ return;
120
+ }
121
+ this.write(index, T.DOLLY_TOUCH_PRESS, { x: labels, y: 0, pressure: 0, time });
122
+ Atomics.notify(this.words, this.address / 4 + word.write);
123
+ }
124
+
89
125
  // Tells the kernel whether this page listens: without it no lease is given.
90
126
  enable(enabled) {
91
127
  Atomics.store(this.words, this.address / 4 + word.enabled, enabled ? 1 : 0);
@@ -98,8 +134,9 @@ const phases = { pointerdown: T.DOLLY_TOUCH_DOWN, pointermove: T.DOLLY_TOUCH_MOV
98
134
  // The page's fingers on the canvas. They are taken before input@0's listeners
99
135
  // on the canvas see them, and only while a program holds the lease, as the
100
136
  // Worker last reported it: without one the terminal's scroll and pinch are
101
- // input@0's. A finger taken stays this module's until it ends.
102
- export function browser({ canvas, surfaceSize, send }) {
137
+ // input@0's. A finger taken stays this module's until it ends. The holder's
138
+ // buttons are input@0's bar's, before its Paste.
139
+ export function browser({ canvas, surfaceSize, send, get }) {
103
140
  let started = false, lease = 0;
104
141
  const taken = new Set();
105
142
  // Positions are pixels of the frame the canvas shows, as input@0's pointer records are.
@@ -130,24 +167,36 @@ export function browser({ canvas, surfaceSize, send }) {
130
167
  canvas.addEventListener("contextmenu", menu);
131
168
  return {
132
169
  page: { get touchTransport() { return started ? { lease: () => lease } : undefined; } },
133
- messages: { "touch-lease"(message) { lease = message.lease; } },
170
+ messages: {
171
+ "touch-lease"(message) { lease = message.lease; },
172
+ "touch-buttons"({ labels, texts }) {
173
+ get("input").buttons(texts, (index, event) =>
174
+ send({ type: "touch-press", index, labels, time: Math.floor(event.timeStamp) }));
175
+ },
176
+ },
134
177
  start() { started = true; },
135
178
  dispose() {
136
179
  for (const type of Object.keys(phases)) window.removeEventListener(type, listen, { capture: true });
137
180
  canvas.removeEventListener("contextmenu", menu);
181
+ get("input")?.buttons([]);
138
182
  started = false;
139
183
  },
140
184
  };
141
185
  }
142
186
 
143
- // The holder's lease is read at each service tick and reported when it changes.
187
+ // The holder's lease and labels are read at each service tick and reported when they change.
144
188
  export function worker({ get, send }) {
145
- let transport, stop, reported = 0;
189
+ let transport, stop, reported = 0, shown = 0;
146
190
  return {
147
- messages: { "touch-finger"({ phase, pointerId, samples }) { transport?.finger(phase, pointerId, samples); } },
191
+ messages: {
192
+ "touch-finger"({ phase, pointerId, samples }) { transport?.finger(phase, pointerId, samples); },
193
+ "touch-press"({ index, labels, time }) { transport?.press(index, labels, time); },
194
+ },
148
195
  service() {
149
196
  const lease = transport?.lease() ?? 0;
150
197
  if (transport && lease !== reported) send({ type: "touch-lease", lease: reported = lease });
198
+ const buttons = transport?.buttons();
199
+ if (buttons && buttons.labels !== shown) send({ type: "touch-buttons", ...buttons, labels: shown = buttons.labels });
151
200
  },
152
201
  start({ kernel, memory }) {
153
202
  const address = Number(kernel.dolly_touch_mailbox_address());