ntk 3.0.0 → 3.1.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.
package/lib/app.js CHANGED
@@ -18,6 +18,16 @@ export default class App {
18
18
  this.X = display.client;
19
19
  this.options = options;
20
20
  this._fonts = null;
21
+ // node-x11 emits X errors it cannot route to a request callback as
22
+ // 'error' on the client — from inside its packet parser. With no
23
+ // listener that emit throws and the parser never re-arms, silently
24
+ // wedging the connection (every later reply is dropped). Benign races
25
+ // (e.g. a request landing after its window was destroyed) become a
26
+ // warning instead; pass { onXError } to handle them yourself.
27
+ this.X.on('error', (err) => {
28
+ if (this.options.onXError) this.options.onXError(err);
29
+ else console.warn(`ntk: unhandled X error: ${err.message} (opcode ${err.majorOpcode}, seq ${err.seq})`);
30
+ });
21
31
  }
22
32
 
23
33
  /** the text API entry point: font matching/loading, shaping, layout */
package/lib/index.js CHANGED
@@ -38,8 +38,10 @@ import './renderingcontext_opengl.js';
38
38
  * @param {object} [options] passed through to x11.createClient
39
39
  * (e.g. { display: ':1' }); ntk additionally understands
40
40
  * { fontSource } — a pluggable system-font lookup (see docs/fonts.md) —
41
- * and { glxVisual } — the visual id for getContext('opengl') in
42
- * environments where `glxinfo` cannot be shelled out to
41
+ * { glxVisual } — the visual id for getContext('opengl') in
42
+ * environments where `glxinfo` cannot be shelled out to — and
43
+ * { onXError } — called with X protocol errors no request callback
44
+ * claimed (default: console.warn)
43
45
  * @param {function} [callback] optional node-style callback
44
46
  * @returns {Promise<App>}
45
47
  */
@@ -120,4 +122,8 @@ export {
120
122
  configureTex,
121
123
  highlightCode
122
124
  };
125
+ // the yoga-layout instance ntk lays HtmlView out with — downstream layout
126
+ // consumers (e.g. the react-x11 renderer) must share it to avoid loading a
127
+ // second WASM copy with potentially mismatched enums
128
+ export { default as Yoga } from 'yoga-layout';
123
129
  export default { createClient };
package/lib/window.js CHANGED
@@ -6,6 +6,31 @@ import Drawable from './drawable.js';
6
6
  import Pixmap from './pixmap.js';
7
7
  import * as xevents from './events_map.js';
8
8
 
9
+ // X window attributes forwarded verbatim from constructor args into the
10
+ // CreateWindow value list (node-x11 valueMask names, see x11
11
+ // lib/corereqs.js). Deliberately not forwarded:
12
+ // - eventMask: computed from the onXxx handler args (an explicit
13
+ // args.eventMask is OR-ed into the computed mask instead)
14
+ // - backingStore: ntk's `backingStore: false` creation option opts out of
15
+ // client-side double buffering (see docs/window.md) and predates this
16
+ // list — it is a different concept from the X backing-store attribute,
17
+ // which is therefore not forwarded to avoid a silent collision
18
+ const forwardedXAttributes = [
19
+ 'backgroundPixmap',
20
+ 'backgroundPixel',
21
+ 'borderPixmap',
22
+ 'borderPixel',
23
+ 'bitGravity',
24
+ 'winGravity',
25
+ 'backingPlanes',
26
+ 'backingPixel',
27
+ 'overrideRedirect',
28
+ 'saveUnder',
29
+ 'doNotPropagateMask',
30
+ 'colormap',
31
+ 'cursor'
32
+ ];
33
+
9
34
  export default class Window extends Drawable {
10
35
  // one wrapper per X window id and connection: constructing a Window for a
11
36
  // known id returns the cached instance
@@ -25,6 +50,8 @@ export default class Window extends Drawable {
25
50
  this.app = app;
26
51
  this.display = app.display;
27
52
  this._mapped = false;
53
+ this._destroyed = false;
54
+ this._titleSerial = 0;
28
55
  this._readyPromiseResolve = null;
29
56
  this._readyPromise = new Promise((resolve) => {
30
57
  this._readyPromiseResolve = resolve;
@@ -68,17 +95,25 @@ export default class Window extends Drawable {
68
95
  }
69
96
  }
70
97
 
98
+ if (typeof args.eventMask === 'number') {
99
+ this.eventMask |= args.eventMask;
100
+ }
101
+
71
102
  this.width = args.width ?? 800;
72
103
  this.height = args.height ?? 800;
73
104
  this.x = args.x ?? 0;
74
105
  this.y = args.y ?? 0;
75
- X.CreateWindow(this.id, parentId, this.x, this.y, this.width, this.height, 0, 0, 0, 0, {
106
+ const values = {
76
107
  // NorthWest bit gravity: keep the old content anchored during
77
108
  // resize instead of discarding it (less flicker between the resize
78
109
  // and our redraw)
79
110
  bitGravity: 1,
80
111
  eventMask: this.eventMask
81
- });
112
+ };
113
+ for (const name of forwardedXAttributes) {
114
+ if (args[name] !== undefined) values[name] = args[name];
115
+ }
116
+ X.CreateWindow(this.id, parentId, this.x, this.y, this.width, this.height, 0, 0, 0, 0, values);
82
117
  } else {
83
118
  this.id = args.id;
84
119
  this.eventMask = 0;
@@ -171,6 +206,7 @@ export default class Window extends Drawable {
171
206
  this.on('map', () => (this._mapped = true));
172
207
  this.on('unmap', () => (this._mapped = false));
173
208
  this.on('destroy', () => {
209
+ this._destroyed = true;
174
210
  this._forget();
175
211
  this._teardownFrame();
176
212
  // the server frees window-backed pictures with the window — let
@@ -537,14 +573,26 @@ export default class Window extends Drawable {
537
573
  }
538
574
 
539
575
  setTitle(title) {
540
- this.X.ChangeProperty(
541
- 0,
542
- this.id,
543
- this.X.atoms.WM_NAME,
544
- this.X.atoms.STRING,
545
- 8,
546
- title
547
- );
576
+ const X = this.X;
577
+ const wid = this.id;
578
+ // legacy WM_NAME is latin-1 only (node-x11 encodes plain strings as
579
+ // latin1) — kept for old window managers
580
+ X.ChangeProperty(0, wid, X.atoms.WM_NAME, X.atoms.STRING, 8, title);
581
+ // modern WMs prefer the EWMH _NET_WM_NAME property, which is UTF-8;
582
+ // interned atoms are cached by node-x11, so this round-trips only once.
583
+ // The lookups are async: by the time they resolve the window may be
584
+ // destroyed (ChangeProperty would BadWindow) or retitled (an older
585
+ // write would clobber a newer one) — the serial/destroyed guards drop
586
+ // the stale write in both cases.
587
+ const serial = ++this._titleSerial;
588
+ X.InternAtom(false, '_NET_WM_NAME', (err, netWmName) => {
589
+ X.InternAtom(false, 'UTF8_STRING', (err2, utf8String) => {
590
+ if (err || err2 || this._destroyed || serial !== this._titleSerial) return;
591
+ safeRelease(X, () => {
592
+ X.ChangeProperty(0, wid, netWmName, utf8String, 8, Buffer.from(title, 'utf8'));
593
+ });
594
+ });
595
+ });
548
596
  return this;
549
597
  }
550
598
 
@@ -622,6 +670,7 @@ export default class Window extends Drawable {
622
670
  }
623
671
 
624
672
  destroy() {
673
+ this._destroyed = true;
625
674
  this._forget();
626
675
  this._teardownFrame();
627
676
  this.emit('_destroyed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ntk",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "Desktop UI toolkit for X11 with canvas-like 2d and OpenGL rendering",
5
5
  "author": "Andrey Sidorov <sidorares@yandex.ru>",
6
6
  "license": "MIT",