novac 2.0.1 → 2.2.0

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.
Files changed (161) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1574 -597
  3. package/bin/novac +468 -171
  4. package/bin/nvc +522 -0
  5. package/bin/nvml +78 -17
  6. package/demo.nv +0 -0
  7. package/demo_builtins.nv +0 -0
  8. package/demo_http.nv +0 -0
  9. package/examples/bf.nv +69 -0
  10. package/examples/math.nv +21 -0
  11. package/kits/birdAPI/kitdef.js +954 -0
  12. package/kits/kitRNG/kitdef.js +740 -0
  13. package/kits/kitSSH/kitdef.js +1272 -0
  14. package/kits/kitadb/kitdef.js +606 -0
  15. package/kits/kitai/kitdef.js +2185 -0
  16. package/kits/kitansi/kitdef.js +1402 -0
  17. package/kits/kitcanvas/kitdef.js +914 -0
  18. package/kits/kitclippy/kitdef.js +925 -0
  19. package/kits/kitformat/kitdef.js +1485 -0
  20. package/kits/kitgps/kitdef.js +1862 -0
  21. package/kits/kitlibproc/kitdef.js +3 -2
  22. package/kits/kitmatrix/ex.js +19 -0
  23. package/kits/kitmatrix/kitdef.js +960 -0
  24. package/kits/kitmorse/kitdef.js +229 -0
  25. package/kits/kitmpatch/kitdef.js +906 -0
  26. package/kits/kitnet/kitdef.js +1401 -0
  27. package/kits/kitnovacweb/README.md +1416 -143
  28. package/kits/kitnovacweb/kitdef.js +92 -2
  29. package/kits/kitnovacweb/nvml/executor.js +578 -176
  30. package/kits/kitnovacweb/nvml/index.js +2 -2
  31. package/kits/kitnovacweb/nvml/lexer.js +72 -69
  32. package/kits/kitnovacweb/nvml/parser.js +328 -159
  33. package/kits/kitnovacweb/nvml/renderer.js +770 -270
  34. package/kits/kitparse/kitdef.js +1688 -0
  35. package/kits/kitproto/kitdef.js +613 -0
  36. package/kits/kitqr/kitdef.js +637 -0
  37. package/kits/kitregex++/kitdef.js +1353 -0
  38. package/kits/kitrequire/kitdef.js +1599 -0
  39. package/kits/kitx11/kitdef.js +1 -0
  40. package/kits/kitx11/kitx11.js +2472 -0
  41. package/kits/kitx11/kitx11_conn.js +948 -0
  42. package/kits/kitx11/kitx11_worker.js +121 -0
  43. package/kits/libtea/kitdef.js +2691 -0
  44. package/kits/libterm/ex.js +285 -0
  45. package/kits/libterm/kitdef.js +1927 -0
  46. package/novac/LICENSE +21 -0
  47. package/novac/README.md +1823 -0
  48. package/novac/bin/novac +950 -0
  49. package/novac/bin/nvc +522 -0
  50. package/novac/bin/nvml +542 -0
  51. package/novac/demo.nv +245 -0
  52. package/novac/demo_builtins.nv +209 -0
  53. package/novac/demo_http.nv +62 -0
  54. package/novac/examples/bf.nv +69 -0
  55. package/novac/examples/math.nv +21 -0
  56. package/novac/kits/kitai/kitdef.js +2185 -0
  57. package/novac/kits/kitansi/kitdef.js +1402 -0
  58. package/novac/kits/kitformat/kitdef.js +1485 -0
  59. package/novac/kits/kitgps/kitdef.js +1862 -0
  60. package/novac/kits/kitlibfs/kitdef.js +231 -0
  61. package/{examples/example-project/nova_modules → novac/kits}/kitlibproc/kitdef.js +3 -2
  62. package/novac/kits/kitmatrix/ex.js +19 -0
  63. package/novac/kits/kitmatrix/kitdef.js +960 -0
  64. package/novac/kits/kitmpatch/kitdef.js +906 -0
  65. package/novac/kits/kitnovacweb/README.md +1572 -0
  66. package/novac/kits/kitnovacweb/demo.nv +12 -0
  67. package/novac/kits/kitnovacweb/demo.nvml +71 -0
  68. package/novac/kits/kitnovacweb/index.nova +12 -0
  69. package/novac/kits/kitnovacweb/kitdef.js +692 -0
  70. package/novac/kits/kitnovacweb/nova.kit.json +8 -0
  71. package/novac/kits/kitnovacweb/nvml/executor.js +739 -0
  72. package/novac/kits/kitnovacweb/nvml/index.js +67 -0
  73. package/novac/kits/kitnovacweb/nvml/lexer.js +263 -0
  74. package/novac/kits/kitnovacweb/nvml/parser.js +508 -0
  75. package/novac/kits/kitnovacweb/nvml/renderer.js +924 -0
  76. package/novac/kits/kitparse/kitdef.js +1688 -0
  77. package/novac/kits/kitregex++/kitdef.js +1353 -0
  78. package/novac/kits/kitrequire/kitdef.js +1599 -0
  79. package/novac/kits/kitx11/kitdef.js +1 -0
  80. package/novac/kits/kitx11/kitx11.js +2472 -0
  81. package/novac/kits/kitx11/kitx11_conn.js +948 -0
  82. package/novac/kits/kitx11/kitx11_worker.js +121 -0
  83. package/novac/kits/libtea/tf.js +2691 -0
  84. package/novac/kits/libterm/ex.js +285 -0
  85. package/novac/kits/libterm/kitdef.js +1927 -0
  86. package/novac/node_modules/chalk/license +9 -0
  87. package/novac/node_modules/chalk/package.json +83 -0
  88. package/novac/node_modules/chalk/readme.md +297 -0
  89. package/novac/node_modules/chalk/source/index.d.ts +325 -0
  90. package/novac/node_modules/chalk/source/index.js +225 -0
  91. package/novac/node_modules/chalk/source/utilities.js +33 -0
  92. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.d.ts +236 -0
  93. package/novac/node_modules/chalk/source/vendor/ansi-styles/index.js +223 -0
  94. package/novac/node_modules/chalk/source/vendor/supports-color/browser.d.ts +1 -0
  95. package/novac/node_modules/chalk/source/vendor/supports-color/browser.js +34 -0
  96. package/novac/node_modules/chalk/source/vendor/supports-color/index.d.ts +55 -0
  97. package/novac/node_modules/chalk/source/vendor/supports-color/index.js +190 -0
  98. package/novac/node_modules/commander/LICENSE +22 -0
  99. package/novac/node_modules/commander/Readme.md +1176 -0
  100. package/novac/node_modules/commander/esm.mjs +16 -0
  101. package/novac/node_modules/commander/index.js +24 -0
  102. package/novac/node_modules/commander/lib/argument.js +150 -0
  103. package/novac/node_modules/commander/lib/command.js +2777 -0
  104. package/novac/node_modules/commander/lib/error.js +39 -0
  105. package/novac/node_modules/commander/lib/help.js +747 -0
  106. package/novac/node_modules/commander/lib/option.js +380 -0
  107. package/novac/node_modules/commander/lib/suggestSimilar.js +101 -0
  108. package/novac/node_modules/commander/package-support.json +19 -0
  109. package/novac/node_modules/commander/package.json +82 -0
  110. package/novac/node_modules/commander/typings/esm.d.mts +3 -0
  111. package/novac/node_modules/commander/typings/index.d.ts +1113 -0
  112. package/novac/node_modules/node-addon-api/LICENSE.md +9 -0
  113. package/novac/node_modules/node-addon-api/README.md +95 -0
  114. package/novac/node_modules/node-addon-api/common.gypi +21 -0
  115. package/novac/node_modules/node-addon-api/except.gypi +25 -0
  116. package/novac/node_modules/node-addon-api/index.js +14 -0
  117. package/novac/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
  118. package/novac/node_modules/node-addon-api/napi-inl.h +7165 -0
  119. package/novac/node_modules/node-addon-api/napi.h +3364 -0
  120. package/novac/node_modules/node-addon-api/node_addon_api.gyp +42 -0
  121. package/novac/node_modules/node-addon-api/node_api.gyp +9 -0
  122. package/novac/node_modules/node-addon-api/noexcept.gypi +26 -0
  123. package/novac/node_modules/node-addon-api/package-support.json +21 -0
  124. package/novac/node_modules/node-addon-api/package.json +480 -0
  125. package/novac/node_modules/node-addon-api/tools/README.md +73 -0
  126. package/novac/node_modules/node-addon-api/tools/check-napi.js +99 -0
  127. package/novac/node_modules/node-addon-api/tools/clang-format.js +71 -0
  128. package/novac/node_modules/node-addon-api/tools/conversion.js +301 -0
  129. package/novac/node_modules/serialize-javascript/LICENSE +27 -0
  130. package/novac/node_modules/serialize-javascript/README.md +149 -0
  131. package/novac/node_modules/serialize-javascript/index.js +297 -0
  132. package/novac/node_modules/serialize-javascript/package.json +33 -0
  133. package/novac/package.json +27 -0
  134. package/novac/scripts/update-bin.js +24 -0
  135. package/novac/src/core/bstd.js +1035 -0
  136. package/novac/src/core/config.js +155 -0
  137. package/novac/src/core/describe.js +187 -0
  138. package/novac/src/core/emitter.js +499 -0
  139. package/novac/src/core/error.js +86 -0
  140. package/novac/src/core/executor.js +5606 -0
  141. package/novac/src/core/formatter.js +686 -0
  142. package/novac/src/core/lexer.js +1026 -0
  143. package/novac/src/core/nova_builtins.js +717 -0
  144. package/novac/src/core/nova_thread_worker.js +166 -0
  145. package/novac/src/core/parser.js +2181 -0
  146. package/novac/src/core/types.js +112 -0
  147. package/novac/src/index.js +28 -0
  148. package/novac/src/runtime/stdlib.js +244 -0
  149. package/package.json +6 -3
  150. package/scripts/update-bin.js +0 -0
  151. package/src/core/bstd.js +838 -362
  152. package/src/core/executor.js +2578 -170
  153. package/src/core/lexer.js +502 -54
  154. package/src/core/nova_builtins.js +21 -3
  155. package/src/core/parser.js +413 -72
  156. package/src/core/types.js +30 -2
  157. package/src/index.js +0 -0
  158. package/examples/example-project/README.md +0 -3
  159. package/examples/example-project/src/main.nova +0 -3
  160. package/src/core/environment.js +0 -0
  161. /package/{examples/example-project/bin/example-project.nv → novac/node_modules/node-addon-api/nothing.c} +0 -0
@@ -0,0 +1,948 @@
1
+ 'use strict';
2
+ // ============================================================
3
+ // kitx11_conn.js — Synchronous X11 connection + drawing API
4
+ //
5
+ // Uses a Worker thread to own the async socket, then bridges
6
+ // back to the main thread with receiveMessageOnPort() so all
7
+ // calls appear synchronous to the caller.
8
+ //
9
+ // Usage:
10
+ // const X11 = require('./kitx11_conn');
11
+ // const conn = X11.connect(); // sync
12
+ // const win = conn.createWindow({...}); // sync
13
+ // conn.mapWindow(win); // sync
14
+ // conn.flush(); // sync — sends queued packets
15
+ // const ev = conn.nextEvent(); // sync — blocks until event
16
+ // ============================================================
17
+
18
+ const {
19
+ Worker,
20
+ MessageChannel,
21
+ receiveMessageOnPort,
22
+ isMainThread,
23
+ } = require('worker_threads');
24
+ const path = require('path');
25
+ const os = require('os');
26
+
27
+ const { kitdef } = require('./kitx11');
28
+ const {
29
+ Request, ByteOrder,
30
+ WindowClass, EventMask, EventType,
31
+ GCMask, GCFunction,
32
+ PropMode, PropFormat, Atom,
33
+ buildConnectionRequest,
34
+ buildCreateWindowRequest,
35
+ buildMapWindowRequest,
36
+ buildChangePropertyRequest,
37
+ buildInternAtomRequest,
38
+ buildQueryExtensionRequest,
39
+ parseQueryExtensionReply,
40
+ colorByName,
41
+ } = kitdef;
42
+
43
+ // ── Internal helpers ──────────────────────────────────────────
44
+
45
+ /** Pad a byte length up to the next 4-byte boundary. */
46
+ function pad4(n) { return (4 - (n % 4)) % 4; }
47
+
48
+ /** Read a 32-bit unsigned int from a Uint8Array (host endian). */
49
+ function u32(buf, off) {
50
+ const v = new DataView(buf.buffer, buf.byteOffset + off, 4);
51
+ return v.getUint32(0, kitdef.hostIsLittleEndian);
52
+ }
53
+ function u16(buf, off) {
54
+ const v = new DataView(buf.buffer, buf.byteOffset + off, 2);
55
+ return v.getUint16(0, kitdef.hostIsLittleEndian);
56
+ }
57
+ function u8(buf, off) { return buf[off]; }
58
+
59
+ // ── SyncSocket ───────────────────────────────────────────────
60
+ //
61
+ // Wraps the socket worker with a fully synchronous read/write API.
62
+ // Connect blocks via Atomics.wait on a SharedArrayBuffer signal.
63
+ // Reads block via receiveMessageOnPort() on a per-request MessageChannel.
64
+
65
+ class SyncSocket {
66
+ /**
67
+ * Synchronously create and connect a SyncSocket.
68
+ * Blocks the calling thread until the TCP/Unix connection is established.
69
+ * @param {string} display e.g. ':0', 'hostname:1'
70
+ * @returns {SyncSocket}
71
+ */
72
+ static create(display) {
73
+ // SAB[0]: 0 = waiting, 1 = connected, 2 = error
74
+ const sab = new SharedArrayBuffer(4);
75
+ const signal = new Int32Array(sab);
76
+
77
+ const worker = new Worker(path.join(__dirname, 'kitx11_worker.js'), {
78
+ workerData: { display, signalSab: sab },
79
+ });
80
+
81
+ // Block synchronously until the worker signals connect result
82
+ Atomics.wait(signal, 0, 0);
83
+
84
+ if (signal[0] === 2) {
85
+ // Worker posted an error message on parentPort before notifying
86
+ worker.terminate();
87
+ throw new Error(`X11 connection failed (DISPLAY=${display})`);
88
+ }
89
+
90
+ const sock = Object.create(SyncSocket.prototype);
91
+ sock._worker = worker;
92
+ return sock;
93
+ }
94
+
95
+ /** Write bytes WITHOUT transferring (buffer stays valid after call). */
96
+ writeCopy(uint8Array) {
97
+ const copy = uint8Array.slice();
98
+ this._worker.postMessage({ cmd: 'write', data: copy }, [copy.buffer]);
99
+ }
100
+
101
+ /** Synchronously read exactly n bytes. Blocks until available. */
102
+ read(n) {
103
+ const { port1, port2 } = new MessageChannel();
104
+ this._worker.postMessage({ cmd: 'read', n, port: port2 }, [port2]);
105
+ let msg;
106
+ // Spin on receiveMessageOnPort — this is the sync blocking point
107
+ do { msg = receiveMessageOnPort(port1); } while (!msg);
108
+ if (!msg.message.ok) throw new Error(`X11 read error: ${msg.message.error}`);
109
+ return msg.message.data; // Uint8Array
110
+ }
111
+
112
+ close() {
113
+ this._worker.postMessage({ cmd: 'close' });
114
+ this._worker.terminate();
115
+ }
116
+ }
117
+
118
+ // ── X11Connection ─────────────────────────────────────────────
119
+
120
+ class X11Connection {
121
+ constructor(sock, info) {
122
+ this._sock = sock;
123
+ this._seq = 0; // sequence number counter
124
+ this._xidBase = info.resourceIdBase;
125
+ this._xidMask = info.resourceIdMask;
126
+ this._xidNext = 0;
127
+ this._rootWin = info.rootWindow;
128
+ this._rootVisual = info.rootVisual;
129
+ this._rootDepth = info.rootDepth;
130
+ this._whitePixel = info.whitePixel;
131
+ this._blackPixel = info.blackPixel;
132
+ this._screen = info;
133
+ this._writeQueue = []; // buffered packets for flush()
134
+ this._atoms = {}; // intern cache name→id
135
+ this._extensions = {}; // extension info cache
136
+ }
137
+
138
+ // ── Resource ID allocation ──────────────────────────────────
139
+ _newXid() {
140
+ const id = this._xidBase | (this._xidNext & this._xidMask);
141
+ this._xidNext++;
142
+ return id;
143
+ }
144
+
145
+ // ── Low-level I/O ───────────────────────────────────────────
146
+
147
+ /** Queue a packet for the next flush(). */
148
+ _queue(buf) {
149
+ this._writeQueue.push(buf);
150
+ this._seq++;
151
+ }
152
+
153
+ /** Send all queued packets to the server immediately. */
154
+ flush() {
155
+ for (const buf of this._writeQueue) {
156
+ this._sock.writeCopy(buf);
157
+ }
158
+ this._writeQueue = [];
159
+ }
160
+
161
+ /** Send one packet immediately (bypasses the queue). */
162
+ _send(buf) {
163
+ this._sock.writeCopy(buf);
164
+ this._seq++;
165
+ }
166
+
167
+ /**
168
+ * Send a request and synchronously read back the reply.
169
+ * X11 replies are at least 32 bytes; additional data is indicated
170
+ * by bytes 4-7 (length in 4-byte units beyond the first 32 bytes).
171
+ */
172
+ _roundTrip(buf) {
173
+ this.flush(); // flush any pending writes first
174
+ this._send(buf);
175
+ return this._readReply();
176
+ }
177
+
178
+ _readReply() {
179
+ const header = this._sock.read(32);
180
+ const type = u8(header, 0);
181
+
182
+ if (type === 0) {
183
+ // Error reply
184
+ const code = u8(header, 1);
185
+ const seq = u16(header, 2);
186
+ const bad = u32(header, 4);
187
+ const minorOp = u16(header, 8);
188
+ const majorOp = u8(header, 10);
189
+ throw new Error(
190
+ `X11 error ${code} (major=${majorOp} minor=${minorOp} bad_resource=0x${bad.toString(16)} seq=${seq})`
191
+ );
192
+ }
193
+
194
+ if (type === 1) {
195
+ // Normal reply
196
+ const extraWords = u32(header, 4);
197
+ if (extraWords > 0) {
198
+ const extra = this._sock.read(extraWords * 4);
199
+ const full = new Uint8Array(32 + extra.length);
200
+ full.set(header, 0);
201
+ full.set(extra, 32);
202
+ return full;
203
+ }
204
+ return header;
205
+ }
206
+
207
+ // It's an event — push it aside and keep waiting for the reply
208
+ // (simplified: real client would queue events)
209
+ return this._readReply();
210
+ }
211
+
212
+ /** Read the next event from the server (blocks synchronously). */
213
+ nextEvent() {
214
+ this.flush();
215
+ const buf = this._sock.read(32);
216
+ const type = u8(buf, 0) & 0x7F; // strip SendEvent bit
217
+ return this._parseEvent(type, buf);
218
+ }
219
+
220
+ _parseEvent(type, buf) {
221
+ const ev = { type, typeName: kitdef.EventTypeName[type] || `unknown(${type})` };
222
+
223
+ switch (type) {
224
+ case EventType.KEY_PRESS:
225
+ case EventType.KEY_RELEASE:
226
+ ev.detail = u8(buf, 1); // keycode
227
+ ev.time = u32(buf, 4);
228
+ ev.root = u32(buf, 8);
229
+ ev.event = u32(buf, 12);
230
+ ev.child = u32(buf, 16);
231
+ ev.rootX = u16(buf, 20);
232
+ ev.rootY = u16(buf, 22);
233
+ ev.eventX = u16(buf, 24);
234
+ ev.eventY = u16(buf, 26);
235
+ ev.state = u16(buf, 28);
236
+ ev.keysym = kitdef.keysymToChar(ev.detail) ?? ev.detail;
237
+ break;
238
+
239
+ case EventType.BUTTON_PRESS:
240
+ case EventType.BUTTON_RELEASE:
241
+ ev.button = u8(buf, 1);
242
+ ev.time = u32(buf, 4);
243
+ ev.event = u32(buf, 12);
244
+ ev.eventX = u16(buf, 24);
245
+ ev.eventY = u16(buf, 26);
246
+ ev.state = u16(buf, 28);
247
+ break;
248
+
249
+ case EventType.MOTION_NOTIFY:
250
+ ev.time = u32(buf, 4);
251
+ ev.event = u32(buf, 12);
252
+ ev.eventX = u16(buf, 24);
253
+ ev.eventY = u16(buf, 26);
254
+ ev.state = u16(buf, 28);
255
+ break;
256
+
257
+ case EventType.EXPOSE:
258
+ ev.window = u32(buf, 4);
259
+ ev.x = u16(buf, 8);
260
+ ev.y = u16(buf, 10);
261
+ ev.width = u16(buf, 12);
262
+ ev.height = u16(buf, 14);
263
+ ev.count = u16(buf, 16);
264
+ break;
265
+
266
+ case EventType.CONFIGURE_NOTIFY:
267
+ ev.window = u32(buf, 4);
268
+ ev.x = u16(buf, 16);
269
+ ev.y = u16(buf, 18);
270
+ ev.width = u16(buf, 20);
271
+ ev.height = u16(buf, 22);
272
+ break;
273
+
274
+ case EventType.DESTROY_NOTIFY:
275
+ ev.window = u32(buf, 8);
276
+ break;
277
+
278
+ case EventType.CLIENT_MESSAGE:
279
+ ev.window = u32(buf, 4);
280
+ ev.type = u32(buf, 8);
281
+ ev.data = buf.slice(12, 32);
282
+ break;
283
+
284
+ default:
285
+ ev.raw = buf;
286
+ break;
287
+ }
288
+ return ev;
289
+ }
290
+
291
+ // ── Atom interning ──────────────────────────────────────────
292
+
293
+ /** Synchronously intern an atom name, with local cache. */
294
+ internAtom(name, onlyIfExists = false) {
295
+ if (this._atoms[name] !== undefined) return this._atoms[name];
296
+ const reply = this._roundTrip(buildInternAtomRequest(name, onlyIfExists));
297
+ const id = u32(reply, 8);
298
+ this._atoms[name] = id;
299
+ return id;
300
+ }
301
+
302
+ // ── Color resolution ────────────────────────────────────────
303
+
304
+ /**
305
+ * Resolve a color to a pixel value.
306
+ * Accepts:
307
+ * - An X11 named color string ('cornflowerblue')
308
+ * - '#RRGGBB' hex string
309
+ * - A pixel integer directly
310
+ */
311
+ color(spec) {
312
+ if (typeof spec === 'number') return spec;
313
+
314
+ const rgb = colorByName(spec) || kitdef.parseColor(spec);
315
+ if (!rgb) throw new Error(`Unknown color: ${spec}`);
316
+
317
+ const r = Array.isArray(rgb) ? rgb[0] : rgb.r;
318
+ const g = Array.isArray(rgb) ? rgb[1] : rgb.g;
319
+ const b = Array.isArray(rgb) ? rgb[2] : rgb.b;
320
+
321
+ // AllocColor request (opcode 84)
322
+ const colormapId = this._screen.defaultColormap;
323
+ const buf = new Uint8Array(16);
324
+ const v = new DataView(buf.buffer);
325
+ const le = kitdef.hostIsLittleEndian;
326
+ v.setUint8(0, 84); // ALLOC_COLOR
327
+ v.setUint8(1, 0);
328
+ v.setUint16(2, 4, le);
329
+ v.setUint32(4, colormapId, le);
330
+ v.setUint16(8, r * 257, le); // 8-bit → 16-bit
331
+ v.setUint16(10, g * 257, le);
332
+ v.setUint16(12, b * 257, le);
333
+ v.setUint16(14, 0, le);
334
+ const reply = this._roundTrip(buf);
335
+ return u32(reply, 16); // pixel
336
+ }
337
+
338
+ // ── Window management ───────────────────────────────────────
339
+
340
+ /**
341
+ * Create a top-level window.
342
+ * @param {object} opts
343
+ * @param {number} [opts.x=0]
344
+ * @param {number} [opts.y=0]
345
+ * @param {number} [opts.width=400]
346
+ * @param {number} [opts.height=300]
347
+ * @param {string|number} [opts.background='white']
348
+ * @param {string} [opts.title]
349
+ * @param {number} [opts.eventMask] defaults to common events
350
+ * @returns {number} window XID
351
+ */
352
+ createWindow(opts = {}) {
353
+ const {
354
+ x = 0, y = 0,
355
+ width = 400, height = 300,
356
+ background = 'white',
357
+ title,
358
+ eventMask = (
359
+ EventMask.EXPOSURE_MASK |
360
+ EventMask.KEY_PRESS_MASK |
361
+ EventMask.BUTTON_PRESS_MASK |
362
+ EventMask.BUTTON_RELEASE_MASK |
363
+ EventMask.POINTER_MOTION_MASK |
364
+ EventMask.STRUCTURE_NOTIFY_MASK
365
+ ),
366
+ } = opts;
367
+
368
+ const wid = this._newXid();
369
+ const bgPix = this.color(background);
370
+
371
+ const values = [bgPix, eventMask];
372
+ const valueMask = kitdef.CW.BACK_PIXEL | kitdef.CW.EVENT_MASK;
373
+
374
+ const req = buildCreateWindowRequest({
375
+ wid, parent: this._rootWin,
376
+ x, y, width, height,
377
+ borderWidth: 0,
378
+ windowClass: WindowClass.INPUT_OUTPUT,
379
+ visual: 0, // CopyFromParent
380
+ valueMask,
381
+ values,
382
+ });
383
+ this._queue(req);
384
+
385
+ if (title) this.setTitle(wid, title);
386
+
387
+ // Register WM_DELETE_WINDOW protocol so close button works
388
+ this._setupWMDelete(wid);
389
+
390
+ return wid;
391
+ }
392
+
393
+ _setupWMDelete(wid) {
394
+ const wmProto = this.internAtom('WM_PROTOCOLS');
395
+ const wmDelete = this.internAtom('WM_DELETE_WINDOW');
396
+ this._queue(buildChangePropertyRequest({
397
+ wid,
398
+ mode: PropMode.REPLACE,
399
+ property: wmProto,
400
+ type: Atom.ATOM,
401
+ format: 32,
402
+ data: [wmDelete],
403
+ }));
404
+ }
405
+
406
+ /** Map (show) a window. */
407
+ mapWindow(wid) {
408
+ this._queue(buildMapWindowRequest(wid));
409
+ }
410
+
411
+ /** Unmap (hide) a window. */
412
+ unmapWindow(wid) {
413
+ const buf = new Uint8Array(8);
414
+ const v = new DataView(buf.buffer);
415
+ const le = kitdef.hostIsLittleEndian;
416
+ v.setUint8(0, Request.UNMAP_WINDOW);
417
+ v.setUint8(1, 0);
418
+ v.setUint16(2, 2, le);
419
+ v.setUint32(4, wid, le);
420
+ this._queue(buf);
421
+ }
422
+
423
+ /** Destroy a window and free its resources. */
424
+ destroyWindow(wid) {
425
+ const buf = new Uint8Array(8);
426
+ const v = new DataView(buf.buffer);
427
+ const le = kitdef.hostIsLittleEndian;
428
+ v.setUint8(0, Request.DESTROY_WINDOW);
429
+ v.setUint8(1, 0);
430
+ v.setUint16(2, 2, le);
431
+ v.setUint32(4, wid, le);
432
+ this._queue(buf);
433
+ }
434
+
435
+ /** Set a window's title (_NET_WM_NAME + WM_NAME). */
436
+ setTitle(wid, title) {
437
+ const enc = new TextEncoder().encode(title);
438
+ const data = Array.from(enc);
439
+ // WM_NAME (legacy, Latin-1)
440
+ this._queue(buildChangePropertyRequest({
441
+ wid, mode: PropMode.REPLACE,
442
+ property: Atom.WM_NAME, type: Atom.STRING, format: 8, data,
443
+ }));
444
+ // _NET_WM_NAME (modern, UTF-8)
445
+ const netName = this.internAtom('_NET_WM_NAME');
446
+ const utf8Atom = this.internAtom('UTF8_STRING');
447
+ this._queue(buildChangePropertyRequest({
448
+ wid, mode: PropMode.REPLACE,
449
+ property: netName, type: utf8Atom, format: 8, data,
450
+ }));
451
+ }
452
+
453
+ /** Move and/or resize a window. */
454
+ configureWindow(wid, opts = {}) {
455
+ const fields = { x:1, y:2, width:4, height:8, borderWidth:16, sibling:32, stackMode:64 };
456
+ let mask = 0;
457
+ const values = [];
458
+ for (const [key, bit] of Object.entries(fields)) {
459
+ if (opts[key] !== undefined) { mask |= bit; values.push(opts[key]); }
460
+ }
461
+ const len = 3 + values.length;
462
+ const buf = new Uint8Array(len * 4);
463
+ const v = new DataView(buf.buffer);
464
+ const le = kitdef.hostIsLittleEndian;
465
+ v.setUint8(0, Request.CONFIGURE_WINDOW);
466
+ v.setUint8(1, 0);
467
+ v.setUint16(2, len, le);
468
+ v.setUint32(4, wid, le);
469
+ v.setUint16(8, mask, le);
470
+ values.forEach((val, i) => v.setUint32(12 + i*4, val, le));
471
+ this._queue(buf);
472
+ }
473
+
474
+ // ── Graphics Context ────────────────────────────────────────
475
+
476
+ /**
477
+ * Create a Graphics Context.
478
+ * @param {number} drawable window or pixmap XID
479
+ * @param {object} opts
480
+ * @param {string|number} [opts.foreground='black']
481
+ * @param {string|number} [opts.background='white']
482
+ * @param {number} [opts.lineWidth=1]
483
+ * @param {number} [opts.function] GCFunction value
484
+ * @returns {number} GC XID
485
+ */
486
+ createGC(drawable, opts = {}) {
487
+ const gcid = this._newXid();
488
+ const le = kitdef.hostIsLittleEndian;
489
+
490
+ const fg = this.color(opts.foreground ?? 'black');
491
+ const bg = this.color(opts.background ?? 'white');
492
+
493
+ let mask = GCMask.FOREGROUND | GCMask.BACKGROUND;
494
+ const values = [fg, bg];
495
+
496
+ if (opts.lineWidth !== undefined) {
497
+ mask |= GCMask.LINE_WIDTH; values.push(opts.lineWidth);
498
+ }
499
+ if (opts.function !== undefined) {
500
+ mask |= GCMask.FUNCTION; values.push(opts.function);
501
+ }
502
+ if (opts.lineStyle !== undefined) {
503
+ mask |= GCMask.LINE_STYLE; values.push(opts.lineStyle);
504
+ }
505
+ if (opts.fillStyle !== undefined) {
506
+ mask |= GCMask.FILL_STYLE; values.push(opts.fillStyle);
507
+ }
508
+ if (opts.graphicsExposures !== undefined) {
509
+ mask |= GCMask.GRAPHICS_EXPOSURES; values.push(opts.graphicsExposures ? 1 : 0);
510
+ }
511
+
512
+ const len = 4 + values.length;
513
+ const buf = new Uint8Array(len * 4);
514
+ const v = new DataView(buf.buffer);
515
+ v.setUint8(0, Request.CREATE_GC);
516
+ v.setUint8(1, 0);
517
+ v.setUint16(2, len, le);
518
+ v.setUint32(4, gcid, le);
519
+ v.setUint32(8, drawable, le);
520
+ v.setUint32(12, mask, le);
521
+ values.forEach((val, i) => v.setUint32(16 + i*4, val, le));
522
+ this._queue(buf);
523
+ return gcid;
524
+ }
525
+
526
+ /** Change attributes of an existing GC. */
527
+ changeGC(gcid, opts = {}) {
528
+ const le = kitdef.hostIsLittleEndian;
529
+ let mask = 0;
530
+ const values = [];
531
+ const add = (flag, val) => { mask |= flag; values.push(val); };
532
+
533
+ if (opts.foreground !== undefined) add(GCMask.FOREGROUND, this.color(opts.foreground));
534
+ if (opts.background !== undefined) add(GCMask.BACKGROUND, this.color(opts.background));
535
+ if (opts.lineWidth !== undefined) add(GCMask.LINE_WIDTH, opts.lineWidth);
536
+ if (opts.function !== undefined) add(GCMask.FUNCTION, opts.function);
537
+ if (opts.lineStyle !== undefined) add(GCMask.LINE_STYLE, opts.lineStyle);
538
+ if (opts.fillStyle !== undefined) add(GCMask.FILL_STYLE, opts.fillStyle);
539
+
540
+ if (!values.length) return;
541
+ const len = 3 + values.length;
542
+ const buf = new Uint8Array(len * 4);
543
+ const v = new DataView(buf.buffer);
544
+ v.setUint8(0, Request.CHANGE_GC);
545
+ v.setUint8(1, 0);
546
+ v.setUint16(2, len, le);
547
+ v.setUint32(4, gcid, le);
548
+ v.setUint32(8, mask, le);
549
+ values.forEach((val, i) => v.setUint32(12 + i*4, val, le));
550
+ this._queue(buf);
551
+ }
552
+
553
+ /** Free a GC. */
554
+ freeGC(gcid) {
555
+ const buf = new Uint8Array(8);
556
+ const v = new DataView(buf.buffer);
557
+ const le = kitdef.hostIsLittleEndian;
558
+ v.setUint8(0, Request.FREE_GC);
559
+ v.setUint8(1, 0);
560
+ v.setUint16(2, 2, le);
561
+ v.setUint32(4, gcid, le);
562
+ this._queue(buf);
563
+ }
564
+
565
+ // ── Drawing primitives ──────────────────────────────────────
566
+
567
+ /** Fill a rectangle with the GC's foreground color. */
568
+ fillRect(drawable, gc, x, y, width, height) {
569
+ const buf = new Uint8Array(20);
570
+ const v = new DataView(buf.buffer);
571
+ const le = kitdef.hostIsLittleEndian;
572
+ v.setUint8(0, Request.POLY_FILL_RECTANGLE);
573
+ v.setUint8(1, 0);
574
+ v.setUint16(2, 5, le);
575
+ v.setUint32(4, drawable, le);
576
+ v.setUint32(8, gc, le);
577
+ v.setInt16(12, x, le);
578
+ v.setInt16(14, y, le);
579
+ v.setUint16(16, width, le);
580
+ v.setUint16(18, height, le);
581
+ this._queue(buf);
582
+ }
583
+
584
+ /** Draw a rectangle outline. */
585
+ drawRect(drawable, gc, x, y, width, height) {
586
+ const buf = new Uint8Array(20);
587
+ const v = new DataView(buf.buffer);
588
+ const le = kitdef.hostIsLittleEndian;
589
+ v.setUint8(0, Request.POLY_RECTANGLE);
590
+ v.setUint8(1, 0);
591
+ v.setUint16(2, 5, le);
592
+ v.setUint32(4, drawable, le);
593
+ v.setUint32(8, gc, le);
594
+ v.setInt16(12, x, le);
595
+ v.setInt16(14, y, le);
596
+ v.setUint16(16, width, le);
597
+ v.setUint16(18, height, le);
598
+ this._queue(buf);
599
+ }
600
+
601
+ /** Draw a line. */
602
+ drawLine(drawable, gc, x1, y1, x2, y2) {
603
+ const buf = new Uint8Array(20);
604
+ const v = new DataView(buf.buffer);
605
+ const le = kitdef.hostIsLittleEndian;
606
+ v.setUint8(0, Request.POLY_LINE);
607
+ v.setUint8(1, 1); // CoordModeOrigin
608
+ v.setUint16(2, 5, le);
609
+ v.setUint32(4, drawable, le);
610
+ v.setUint32(8, gc, le);
611
+ v.setInt16(12, x1, le);
612
+ v.setInt16(14, y1, le);
613
+ v.setInt16(16, x2, le);
614
+ v.setInt16(18, y2, le);
615
+ this._queue(buf);
616
+ }
617
+
618
+ /** Draw a filled polygon. points = [{x,y}, ...] */
619
+ fillPoly(drawable, gc, points) {
620
+ const nPts = points.length;
621
+ const len = 4 + nPts; // 4 header words + 1 word per point
622
+ const buf = new Uint8Array(len * 4);
623
+ const v = new DataView(buf.buffer);
624
+ const le = kitdef.hostIsLittleEndian;
625
+ v.setUint8(0, Request.FILL_POLY);
626
+ v.setUint8(1, 0); // Complex shape
627
+ v.setUint16(2, len, le);
628
+ v.setUint32(4, drawable, le);
629
+ v.setUint32(8, gc, le);
630
+ v.setUint8(12, 0); // shape: Complex
631
+ v.setUint8(13, 0); // CoordModeOrigin
632
+ points.forEach(({ x, y }, i) => {
633
+ v.setInt16(16 + i*4, x, le);
634
+ v.setInt16(18 + i*4, y, le);
635
+ });
636
+ this._queue(buf);
637
+ }
638
+
639
+ /** Draw a filled arc (ellipse/circle segment). */
640
+ fillArc(drawable, gc, x, y, width, height, angle1 = 0, angle2 = 360 * 64) {
641
+ const buf = new Uint8Array(24);
642
+ const v = new DataView(buf.buffer);
643
+ const le = kitdef.hostIsLittleEndian;
644
+ v.setUint8(0, Request.POLY_FILL_ARC);
645
+ v.setUint8(1, 0);
646
+ v.setUint16(2, 6, le);
647
+ v.setUint32(4, drawable, le);
648
+ v.setUint32(8, gc, le);
649
+ v.setInt16(12, x, le);
650
+ v.setInt16(14, y, le);
651
+ v.setUint16(16, width, le);
652
+ v.setUint16(18, height, le);
653
+ v.setInt16(20, angle1, le);
654
+ v.setInt16(22, angle2, le);
655
+ this._queue(buf);
656
+ }
657
+
658
+ /** Draw an arc outline. */
659
+ drawArc(drawable, gc, x, y, width, height, angle1 = 0, angle2 = 360 * 64) {
660
+ const buf = new Uint8Array(24);
661
+ const v = new DataView(buf.buffer);
662
+ const le = kitdef.hostIsLittleEndian;
663
+ v.setUint8(0, Request.POLY_ARC);
664
+ v.setUint8(1, 0);
665
+ v.setUint16(2, 6, le);
666
+ v.setUint32(4, drawable, le);
667
+ v.setUint32(8, gc, le);
668
+ v.setInt16(12, x, le);
669
+ v.setInt16(14, y, le);
670
+ v.setUint16(16, width, le);
671
+ v.setUint16(18, height, le);
672
+ v.setInt16(20, angle1, le);
673
+ v.setInt16(22, angle2, le);
674
+ this._queue(buf);
675
+ }
676
+
677
+ /** Draw points. points = [{x,y}, ...] */
678
+ drawPoints(drawable, gc, points) {
679
+ const len = 3 + points.length;
680
+ const buf = new Uint8Array(len * 4);
681
+ const v = new DataView(buf.buffer);
682
+ const le = kitdef.hostIsLittleEndian;
683
+ v.setUint8(0, Request.POLY_POINT);
684
+ v.setUint8(1, 0); // CoordModeOrigin
685
+ v.setUint16(2, len, le);
686
+ v.setUint32(4, drawable, le);
687
+ v.setUint32(8, gc, le);
688
+ points.forEach(({ x, y }, i) => {
689
+ v.setInt16(12 + i*4, x, le);
690
+ v.setInt16(14 + i*4, y, le);
691
+ });
692
+ this._queue(buf);
693
+ }
694
+
695
+ /**
696
+ * Draw text using X11's core (bitmap) fonts.
697
+ * For scalable fonts, use the RENDER extension instead.
698
+ */
699
+ drawText(drawable, gc, x, y, text) {
700
+ const enc = new TextEncoder().encode(text);
701
+ if (enc.length > 255) throw new Error('drawText: text too long (max 255 bytes; split it up)');
702
+ const itemLen = 2 + enc.length;
703
+ const itemPad = pad4(itemLen);
704
+ const len = 4 + Math.ceil((itemLen + itemPad) / 4);
705
+ const buf = new Uint8Array(len * 4);
706
+ const v = new DataView(buf.buffer);
707
+ const le = kitdef.hostIsLittleEndian;
708
+ v.setUint8(0, Request.IMAGE_TEXT_8);
709
+ v.setUint8(1, enc.length); // string length
710
+ v.setUint16(2, len, le);
711
+ v.setUint32(4, drawable, le);
712
+ v.setUint32(8, gc, le);
713
+ v.setInt16(12, x, le);
714
+ v.setInt16(14, y, le);
715
+ buf.set(enc, 16);
716
+ this._queue(buf);
717
+ }
718
+
719
+ /** Load a named X11 core font and return its font XID. */
720
+ loadFont(name) {
721
+ const fid = this._newXid();
722
+ const enc = new TextEncoder().encode(name);
723
+ const pad = pad4(enc.length);
724
+ const len = 3 + Math.ceil((enc.length + pad) / 4);
725
+ const buf = new Uint8Array(len * 4);
726
+ const v = new DataView(buf.buffer);
727
+ const le = kitdef.hostIsLittleEndian;
728
+ v.setUint8(0, Request.OPEN_FONT);
729
+ v.setUint8(1, 0);
730
+ v.setUint16(2, len, le);
731
+ v.setUint32(4, fid, le);
732
+ v.setUint16(8, enc.length, le);
733
+ buf.set(enc, 12);
734
+ this._queue(buf);
735
+ return fid;
736
+ }
737
+
738
+ /** Assign a font to a GC. */
739
+ setFont(gcid, fontId) {
740
+ this.changeGC(gcid, {}); // no-op flush guard
741
+ const buf = new Uint8Array(16);
742
+ const v = new DataView(buf.buffer);
743
+ const le = kitdef.hostIsLittleEndian;
744
+ v.setUint8(0, Request.CHANGE_GC);
745
+ v.setUint8(1, 0);
746
+ v.setUint16(2, 4, le);
747
+ v.setUint32(4, gcid, le);
748
+ v.setUint32(8, GCMask.FONT, le);
749
+ v.setUint32(12, fontId, le);
750
+ this._queue(buf);
751
+ }
752
+
753
+ /** Clear the entire window to its background. */
754
+ clearWindow(wid) {
755
+ const buf = new Uint8Array(16);
756
+ const v = new DataView(buf.buffer);
757
+ const le = kitdef.hostIsLittleEndian;
758
+ v.setUint8(0, Request.CLEAR_AREA);
759
+ v.setUint8(1, 0); // exposures = false
760
+ v.setUint16(2, 4, le);
761
+ v.setUint32(4, wid, le);
762
+ // x=0, y=0, w=0, h=0 means entire window
763
+ v.setInt16(8, 0, le); v.setInt16(10, 0, le);
764
+ v.setUint16(12, 0, le); v.setUint16(14, 0, le);
765
+ this._queue(buf);
766
+ }
767
+
768
+ /** Copy a rectangle from one drawable to another (BitBlt). */
769
+ copyArea(src, dst, gc, srcX, srcY, dstX, dstY, width, height) {
770
+ const buf = new Uint8Array(28);
771
+ const v = new DataView(buf.buffer);
772
+ const le = kitdef.hostIsLittleEndian;
773
+ v.setUint8(0, Request.COPY_AREA);
774
+ v.setUint8(1, 0);
775
+ v.setUint16(2, 7, le);
776
+ v.setUint32(4, src, le);
777
+ v.setUint32(8, dst, le);
778
+ v.setUint32(12, gc, le);
779
+ v.setInt16(16, srcX, le); v.setInt16(18, srcY, le);
780
+ v.setInt16(20, dstX, le); v.setInt16(22, dstY, le);
781
+ v.setUint16(24, width, le); v.setUint16(26, height, le);
782
+ this._queue(buf);
783
+ }
784
+
785
+ // ── Pixmap (off-screen buffer) ──────────────────────────────
786
+
787
+ /** Create an off-screen pixmap (for double-buffering etc.) */
788
+ createPixmap(drawable, width, height, depth = this._rootDepth) {
789
+ const pid = this._newXid();
790
+ const buf = new Uint8Array(16);
791
+ const v = new DataView(buf.buffer);
792
+ const le = kitdef.hostIsLittleEndian;
793
+ v.setUint8(0, Request.CREATE_PIXMAP);
794
+ v.setUint8(1, depth);
795
+ v.setUint16(2, 4, le);
796
+ v.setUint32(4, pid, le);
797
+ v.setUint32(8, drawable, le);
798
+ v.setUint16(12, width, le);
799
+ v.setUint16(14, height, le);
800
+ this._queue(buf);
801
+ return pid;
802
+ }
803
+
804
+ /** Free a pixmap. */
805
+ freePixmap(pid) {
806
+ const buf = new Uint8Array(8);
807
+ const v = new DataView(buf.buffer);
808
+ const le = kitdef.hostIsLittleEndian;
809
+ v.setUint8(0, Request.FREE_PIXMAP);
810
+ v.setUint8(1, 0);
811
+ v.setUint16(2, 2, le);
812
+ v.setUint32(4, pid, le);
813
+ this._queue(buf);
814
+ }
815
+
816
+ // ── Extension probe ─────────────────────────────────────────
817
+
818
+ /** Synchronously query whether an extension is present. */
819
+ queryExtension(name) {
820
+ if (this._extensions[name] !== undefined) return this._extensions[name];
821
+ const reply = this._roundTrip(buildQueryExtensionRequest(name));
822
+ const result = parseQueryExtensionReply(reply);
823
+ this._extensions[name] = result;
824
+ return result;
825
+ }
826
+
827
+ // ── Convenience ─────────────────────────────────────────────
828
+
829
+ /** Expose screen/root info. */
830
+ get root() { return this._rootWin; }
831
+ get rootDepth() { return this._rootDepth; }
832
+ get rootVisual() { return this._rootVisual; }
833
+ get whitePixel() { return this._whitePixel; }
834
+ get blackPixel() { return this._blackPixel; }
835
+
836
+ /** Disconnect from the X server. */
837
+ close() { this._sock.close(); }
838
+ }
839
+
840
+ // ── Connection setup (parse server greeting) ──────────────────
841
+
842
+ function _parseSetupReply(sock, authName, authData) {
843
+ const req = buildConnectionRequest({ authName, authData });
844
+ sock.writeCopy(req);
845
+
846
+ // Read fixed 8-byte header first
847
+ const hdr = sock.read(8);
848
+ const status = u8(hdr, 0);
849
+
850
+ if (status === 0) {
851
+ // Failed
852
+ const reasonLen = u8(hdr, 1);
853
+ const extra = sock.read(Math.ceil((u16(hdr, 6) * 4)));
854
+ const reason = new TextDecoder().decode(extra.slice(0, reasonLen));
855
+ throw new Error(`X11 connection refused: ${reason}`);
856
+ }
857
+ if (status === 2) {
858
+ // Authenticate — we don't support SASL etc.
859
+ throw new Error('X11 server requires additional authentication');
860
+ }
861
+
862
+ // Status 1 = success. Read the rest.
863
+ const additionalWords = u16(hdr, 6);
864
+ const rest = sock.read(additionalWords * 4);
865
+
866
+ // Parse screen info from the greeting
867
+ // Offsets are relative to `rest` (the 8-byte header is already consumed)
868
+ const le = kitdef.hostIsLittleEndian;
869
+ const dv = new DataView(rest.buffer, rest.byteOffset, rest.byteLength);
870
+
871
+ const releaseNum = dv.getUint32(0, le);
872
+ const resourceIdBase = dv.getUint32(4, le);
873
+ const resourceIdMask = dv.getUint32(8, le);
874
+ const imageByteOrder = u8(rest, 30);
875
+ const bitmapBitOrder = u8(rest, 31);
876
+
877
+ const vendorLen = dv.getUint16(16, le);
878
+ const numFormats = u8(rest, 21);
879
+ // Skip vendor + formats to get to screen list
880
+ const vendorPad = pad4(vendorLen);
881
+ let off = 32 + vendorLen + vendorPad + numFormats * 8; // FORMAT is 8 bytes each
882
+
883
+ // Screen structure
884
+ const rootWindow = dv.getUint32(off, le); off += 4;
885
+ const defaultColormap= dv.getUint32(off, le); off += 4;
886
+ const whitePixel = dv.getUint32(off, le); off += 4;
887
+ const blackPixel = dv.getUint32(off, le); off += 4;
888
+ const currentInput = dv.getUint32(off, le); off += 4;
889
+ const widthPixels = dv.getUint16(off, le); off += 2;
890
+ const heightPixels = dv.getUint16(off, le); off += 2;
891
+ off += 4; // width/height in mm
892
+ off += 4; // min/max installed maps
893
+ const rootVisual = dv.getUint32(off, le); off += 4;
894
+ const backingStores = u8(rest, off); off += 1;
895
+ const saveUnders = u8(rest, off); off += 1;
896
+ const rootDepth = u8(rest, off); off += 1;
897
+ const numDepths = u8(rest, off); off += 1;
898
+
899
+ return {
900
+ releaseNum, resourceIdBase, resourceIdMask,
901
+ rootWindow, defaultColormap, whitePixel, blackPixel,
902
+ rootVisual, rootDepth, widthPixels, heightPixels,
903
+ imageByteOrder, bitmapBitOrder,
904
+ };
905
+ }
906
+
907
+ // ── Public API ────────────────────────────────────────────────
908
+
909
+ /**
910
+ * Synchronously connect to an X11 server.
911
+ *
912
+ * @param {object} [opts]
913
+ * @param {string} [opts.display] Defaults to $DISPLAY
914
+ * @param {string} [opts.authName] e.g. 'MIT-MAGIC-COOKIE-1'
915
+ * @param {Uint8Array} [opts.authData] Raw cookie bytes
916
+ * @returns {X11Connection}
917
+ *
918
+ * @example
919
+ * const X11 = require('./kitx11_conn');
920
+ * const conn = X11.connect();
921
+ * const win = conn.createWindow({ width: 640, height: 480, title: 'Hello' });
922
+ * const gc = conn.createGC(win, { foreground: 'cornflowerblue' });
923
+ * conn.mapWindow(win);
924
+ * conn.flush();
925
+ *
926
+ * // Event loop
927
+ * while (true) {
928
+ * const ev = conn.nextEvent();
929
+ * if (ev.typeName === 'EXPOSE') {
930
+ * conn.fillRect(win, gc, 0, 0, 640, 480);
931
+ * conn.drawText(win, gc, 50, 50, 'Hello X11!');
932
+ * conn.flush();
933
+ * }
934
+ * if (ev.typeName === 'CLIENT_MESSAGE') break; // WM close
935
+ * }
936
+ * conn.close();
937
+ */
938
+ function connect(opts = {}) {
939
+ const display = opts.display || process.env.DISPLAY || ':0';
940
+ const authName = opts.authName ?? '';
941
+ const authData = opts.authData ?? new Uint8Array(0);
942
+
943
+ const sock = SyncSocket.create(display);
944
+ const info = _parseSetupReply(sock, authName, authData);
945
+ return new X11Connection(sock, info);
946
+ }
947
+
948
+ module.exports = { connect, X11Connection, kitdef };