@gjsify/webgl 0.36.0 → 0.38.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.
@@ -6,22 +6,15 @@ import { HTMLCanvasElement as OurHTMLCanvasElement } from './html-canvas-element
6
6
  import { WebGLRenderingContext as OurWebGLRenderingContext } from './webgl-rendering-context.js';
7
7
  type WebGLReadyCallback = (canvas: globalThis.HTMLCanvasElement, gl: globalThis.WebGLRenderingContext) => void;
8
8
  /**
9
- * A `Gtk.GLArea` subclass that handles WebGL bootstrapping:
10
- * - Sets up OpenGL ES 3.2 context, depth buffer, stencil buffer
11
- * - Creates an `HTMLCanvasElement` wrapping the GLArea on first render
12
- * - Fires `onReady()` callbacks with (canvas, gl) once the context is available
13
- * - Provides `requestAnimationFrame()` backed by GTK frame clock (vsync) + render signal
14
- * - `installGlobals()` sets `globalThis.{requestAnimationFrame, cancelAnimationFrame,
15
- * performance, WebGLRenderingContext, WebGL2RenderingContext}`
9
+ * A `Gtk.GLArea` subclass that negotiates the GL context, wraps it in an
10
+ * `HTMLCanvasElement` on first render, and drives `requestAnimationFrame` off the GTK
11
+ * frame clock.
16
12
  *
17
- * Usage:
13
+ * `installGlobals()` must run BEFORE the app code that expects the browser globals:
18
14
  * ```ts
19
15
  * const widget = new WebGLBridge();
20
- * widget.installGlobals(); // sets globalThis.requestAnimationFrame
21
- * widget.onReady((canvas, gl) => {
22
- * gl.clearColor(0, 0, 0, 1);
23
- * // requestAnimationFrame is now available globally
24
- * });
16
+ * widget.installGlobals();
17
+ * widget.onReady((canvas, gl) => { … });
25
18
  * window.set_child(widget);
26
19
  * ```
27
20
  */
@@ -46,95 +39,68 @@ export declare const WebGLBridge: {
46
39
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
47
40
  get canvas(): globalThis.HTMLCanvasElement | null;
48
41
  /**
49
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
50
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
51
- * until the context exists (i.e. before the first `onReady`).
42
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
43
+ * `null` until the context exists.
52
44
  *
53
- * Read it to REPORT what a host provides. It is not a capability check:
54
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
55
- * slower depends entirely on the workload. Anything that switches
56
- * renderers should key on a measured frame budget, not on this string.
45
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
46
+ * GPU does, only slower, and by how much depends on the workload. Anything that
47
+ * switches renderers should key on a measured frame budget, not on this string.
57
48
  */
58
49
  get rendererInfo(): {
59
50
  vendor: string;
60
51
  renderer: string;
61
52
  } | null;
62
- /**
63
- * Registers a callback to be called once the WebGL context is ready.
64
- * If the context is already available, the callback fires synchronously.
65
- */
53
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
66
54
  onReady(cb: WebGLReadyCallback): void;
67
55
  /**
68
56
  * @deprecated Use `onReady()` instead.
69
57
  */
70
58
  onWebGLReady(cb: WebGLReadyCallback): void;
71
59
  /**
72
- * Records the negotiated GL implementation and, when it rasterises on the
73
- * CPU, says so ONCE on stderr.
60
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
61
+ * says so ONCE on stderr.
74
62
  *
75
- * A GPU-less host is invisible from inside the app: context creation
76
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
77
- * is a frame budget blown by three orders of magnitude — which presents as
78
- * a window that sits there. Naming the renderer at the one place that
79
- * knows it turns that into a line anyone can act on, for every consumer,
80
- * without any of them asking.
63
+ * A GPU-less host is otherwise invisible from inside the app: the context is
64
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
65
+ * three orders of magnitude, which presents as a window that just sits there.
81
66
  *
82
- * Diagnostic only: it changes no behaviour, because a software renderer is
83
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
84
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
85
- * comfortably on the same driver that needs 1.1 s for one textured
86
- * full-screen draw in a 2D game).
67
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
68
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
69
+ * demand-driven three.js scene ran comfortably on the same driver that needed
70
+ * 1.1 s for a single textured full-screen draw in a 2D game.
87
71
  */
88
72
  _reportRenderer(gl: OurWebGLRenderingContext): void;
89
73
  /**
90
- * Register a callback invoked whenever the GTK widget is resized.
91
- * The callback fires alongside the native 'resize' GObject signal and
92
- * after the DOM 'resize' event has been dispatched on the canvas.
93
- * Canvas buffer dimensions are NOT automatically updated — consumers
94
- * should set `canvas.width`/`canvas.height` themselves if desired.
74
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
75
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
76
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
95
77
  */
96
78
  onResize(cb: (width: number, height: number) => void): void;
97
79
  /**
98
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
99
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
100
- * render signal handler. Both are installed once at construction / first render respectively,
101
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
80
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
102
81
  *
103
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
104
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
105
- * fired replaces the pending callback (single-slot model — matching the existing rAF
106
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
107
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
82
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
83
+ * callback (latest wins), which makes the older handle stale and a
84
+ * `cancelAnimationFrame` with it a no-op.
108
85
  */
109
86
  requestAnimationFrame(cb: FrameRequestCallback): number;
110
87
  /**
111
- * Cancels a previously-scheduled animation frame callback, matching the browser
112
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
113
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
114
- * handle (from a callback that already fired, or was replaced by a later
115
- * `requestAnimationFrame`) is a no-op.
88
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
89
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
90
+ * no-op.
116
91
  *
117
- * Per-ID matching is load-bearing: without it any consumer that calls
118
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
119
- * transient parent `unmap` in a responsive layout) would clear the loop's
120
- * latest pending callback and freeze rendering until the next manual
121
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
122
- * the caller actually owns.
92
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
93
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
94
+ * kills the loop's current callback and freezes rendering.
123
95
  */
124
96
  cancelAnimationFrame(id: number): void;
125
97
  /**
126
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
127
- * `cancelAnimationFrame`, `performance` and the two WebGL context
128
- * constructors — so that browser-targeted code (e.g. Three.js) works
129
- * unchanged on GJS.
98
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
99
+ * runs unchanged on GJS.
130
100
  *
131
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
132
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
133
- * and its `/register` install the same set, `installGlobals()` being the
134
- * explicit imperative path (`@gjsify/webgl/register` keeps the
135
- * if-undefined guard instead). They used to reach `globalThis` for free
136
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
137
- * write into `register.ts` must not shrink what this method does.
101
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
102
+ * `installGlobals()` is the explicit imperative path and installs the same set as
103
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
138
104
  */
139
105
  installGlobals(): void;
140
106
  get allowed_apis(): Gdk.GLAPI;
@@ -577,95 +543,68 @@ export declare const WebGLBridge: {
577
543
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
578
544
  get canvas(): globalThis.HTMLCanvasElement | null;
579
545
  /**
580
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
581
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
582
- * until the context exists (i.e. before the first `onReady`).
546
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
547
+ * `null` until the context exists.
583
548
  *
584
- * Read it to REPORT what a host provides. It is not a capability check:
585
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
586
- * slower depends entirely on the workload. Anything that switches
587
- * renderers should key on a measured frame budget, not on this string.
549
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
550
+ * GPU does, only slower, and by how much depends on the workload. Anything that
551
+ * switches renderers should key on a measured frame budget, not on this string.
588
552
  */
589
553
  get rendererInfo(): {
590
554
  vendor: string;
591
555
  renderer: string;
592
556
  } | null;
593
- /**
594
- * Registers a callback to be called once the WebGL context is ready.
595
- * If the context is already available, the callback fires synchronously.
596
- */
557
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
597
558
  onReady(cb: WebGLReadyCallback): void;
598
559
  /**
599
560
  * @deprecated Use `onReady()` instead.
600
561
  */
601
562
  onWebGLReady(cb: WebGLReadyCallback): void;
602
563
  /**
603
- * Records the negotiated GL implementation and, when it rasterises on the
604
- * CPU, says so ONCE on stderr.
564
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
565
+ * says so ONCE on stderr.
605
566
  *
606
- * A GPU-less host is invisible from inside the app: context creation
607
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
608
- * is a frame budget blown by three orders of magnitude — which presents as
609
- * a window that sits there. Naming the renderer at the one place that
610
- * knows it turns that into a line anyone can act on, for every consumer,
611
- * without any of them asking.
567
+ * A GPU-less host is otherwise invisible from inside the app: the context is
568
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
569
+ * three orders of magnitude, which presents as a window that just sits there.
612
570
  *
613
- * Diagnostic only: it changes no behaviour, because a software renderer is
614
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
615
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
616
- * comfortably on the same driver that needs 1.1 s for one textured
617
- * full-screen draw in a 2D game).
571
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
572
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
573
+ * demand-driven three.js scene ran comfortably on the same driver that needed
574
+ * 1.1 s for a single textured full-screen draw in a 2D game.
618
575
  */
619
576
  _reportRenderer(gl: OurWebGLRenderingContext): void;
620
577
  /**
621
- * Register a callback invoked whenever the GTK widget is resized.
622
- * The callback fires alongside the native 'resize' GObject signal and
623
- * after the DOM 'resize' event has been dispatched on the canvas.
624
- * Canvas buffer dimensions are NOT automatically updated — consumers
625
- * should set `canvas.width`/`canvas.height` themselves if desired.
578
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
579
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
580
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
626
581
  */
627
582
  onResize(cb: (width: number, height: number) => void): void;
628
583
  /**
629
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
630
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
631
- * render signal handler. Both are installed once at construction / first render respectively,
632
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
584
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
633
585
  *
634
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
635
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
636
- * fired replaces the pending callback (single-slot model — matching the existing rAF
637
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
638
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
586
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
587
+ * callback (latest wins), which makes the older handle stale and a
588
+ * `cancelAnimationFrame` with it a no-op.
639
589
  */
640
590
  requestAnimationFrame(cb: FrameRequestCallback): number;
641
591
  /**
642
- * Cancels a previously-scheduled animation frame callback, matching the browser
643
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
644
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
645
- * handle (from a callback that already fired, or was replaced by a later
646
- * `requestAnimationFrame`) is a no-op.
592
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
593
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
594
+ * no-op.
647
595
  *
648
- * Per-ID matching is load-bearing: without it any consumer that calls
649
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
650
- * transient parent `unmap` in a responsive layout) would clear the loop's
651
- * latest pending callback and freeze rendering until the next manual
652
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
653
- * the caller actually owns.
596
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
597
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
598
+ * kills the loop's current callback and freezes rendering.
654
599
  */
655
600
  cancelAnimationFrame(id: number): void;
656
601
  /**
657
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
658
- * `cancelAnimationFrame`, `performance` and the two WebGL context
659
- * constructors — so that browser-targeted code (e.g. Three.js) works
660
- * unchanged on GJS.
602
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
603
+ * runs unchanged on GJS.
661
604
  *
662
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
663
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
664
- * and its `/register` install the same set, `installGlobals()` being the
665
- * explicit imperative path (`@gjsify/webgl/register` keeps the
666
- * if-undefined guard instead). They used to reach `globalThis` for free
667
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
668
- * write into `register.ts` must not shrink what this method does.
605
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
606
+ * `installGlobals()` is the explicit imperative path and installs the same set as
607
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
669
608
  */
670
609
  installGlobals(): void;
671
610
  get allowed_apis(): Gdk.GLAPI;
@@ -1108,95 +1047,68 @@ export declare const WebGLBridge: {
1108
1047
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
1109
1048
  get canvas(): globalThis.HTMLCanvasElement | null;
1110
1049
  /**
1111
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
1112
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
1113
- * until the context exists (i.e. before the first `onReady`).
1050
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
1051
+ * `null` until the context exists.
1114
1052
  *
1115
- * Read it to REPORT what a host provides. It is not a capability check:
1116
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
1117
- * slower depends entirely on the workload. Anything that switches
1118
- * renderers should key on a measured frame budget, not on this string.
1053
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
1054
+ * GPU does, only slower, and by how much depends on the workload. Anything that
1055
+ * switches renderers should key on a measured frame budget, not on this string.
1119
1056
  */
1120
1057
  get rendererInfo(): {
1121
1058
  vendor: string;
1122
1059
  renderer: string;
1123
1060
  } | null;
1124
- /**
1125
- * Registers a callback to be called once the WebGL context is ready.
1126
- * If the context is already available, the callback fires synchronously.
1127
- */
1061
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
1128
1062
  onReady(cb: WebGLReadyCallback): void;
1129
1063
  /**
1130
1064
  * @deprecated Use `onReady()` instead.
1131
1065
  */
1132
1066
  onWebGLReady(cb: WebGLReadyCallback): void;
1133
1067
  /**
1134
- * Records the negotiated GL implementation and, when it rasterises on the
1135
- * CPU, says so ONCE on stderr.
1068
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
1069
+ * says so ONCE on stderr.
1136
1070
  *
1137
- * A GPU-less host is invisible from inside the app: context creation
1138
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
1139
- * is a frame budget blown by three orders of magnitude — which presents as
1140
- * a window that sits there. Naming the renderer at the one place that
1141
- * knows it turns that into a line anyone can act on, for every consumer,
1142
- * without any of them asking.
1071
+ * A GPU-less host is otherwise invisible from inside the app: the context is
1072
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
1073
+ * three orders of magnitude, which presents as a window that just sits there.
1143
1074
  *
1144
- * Diagnostic only: it changes no behaviour, because a software renderer is
1145
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
1146
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
1147
- * comfortably on the same driver that needs 1.1 s for one textured
1148
- * full-screen draw in a 2D game).
1075
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
1076
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
1077
+ * demand-driven three.js scene ran comfortably on the same driver that needed
1078
+ * 1.1 s for a single textured full-screen draw in a 2D game.
1149
1079
  */
1150
1080
  _reportRenderer(gl: OurWebGLRenderingContext): void;
1151
1081
  /**
1152
- * Register a callback invoked whenever the GTK widget is resized.
1153
- * The callback fires alongside the native 'resize' GObject signal and
1154
- * after the DOM 'resize' event has been dispatched on the canvas.
1155
- * Canvas buffer dimensions are NOT automatically updated — consumers
1156
- * should set `canvas.width`/`canvas.height` themselves if desired.
1082
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
1083
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
1084
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
1157
1085
  */
1158
1086
  onResize(cb: (width: number, height: number) => void): void;
1159
1087
  /**
1160
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
1161
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
1162
- * render signal handler. Both are installed once at construction / first render respectively,
1163
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
1088
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
1164
1089
  *
1165
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
1166
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
1167
- * fired replaces the pending callback (single-slot model — matching the existing rAF
1168
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
1169
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
1090
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
1091
+ * callback (latest wins), which makes the older handle stale and a
1092
+ * `cancelAnimationFrame` with it a no-op.
1170
1093
  */
1171
1094
  requestAnimationFrame(cb: FrameRequestCallback): number;
1172
1095
  /**
1173
- * Cancels a previously-scheduled animation frame callback, matching the browser
1174
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
1175
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
1176
- * handle (from a callback that already fired, or was replaced by a later
1177
- * `requestAnimationFrame`) is a no-op.
1096
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
1097
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
1098
+ * no-op.
1178
1099
  *
1179
- * Per-ID matching is load-bearing: without it any consumer that calls
1180
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
1181
- * transient parent `unmap` in a responsive layout) would clear the loop's
1182
- * latest pending callback and freeze rendering until the next manual
1183
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
1184
- * the caller actually owns.
1100
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
1101
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
1102
+ * kills the loop's current callback and freezes rendering.
1185
1103
  */
1186
1104
  cancelAnimationFrame(id: number): void;
1187
1105
  /**
1188
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
1189
- * `cancelAnimationFrame`, `performance` and the two WebGL context
1190
- * constructors — so that browser-targeted code (e.g. Three.js) works
1191
- * unchanged on GJS.
1106
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
1107
+ * runs unchanged on GJS.
1192
1108
  *
1193
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
1194
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
1195
- * and its `/register` install the same set, `installGlobals()` being the
1196
- * explicit imperative path (`@gjsify/webgl/register` keeps the
1197
- * if-undefined guard instead). They used to reach `globalThis` for free
1198
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
1199
- * write into `register.ts` must not shrink what this method does.
1109
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
1110
+ * `installGlobals()` is the explicit imperative path and installs the same set as
1111
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
1200
1112
  */
1201
1113
  installGlobals(): void;
1202
1114
  get allowed_apis(): Gdk.GLAPI;
@@ -1639,95 +1551,68 @@ export declare const WebGLBridge: {
1639
1551
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
1640
1552
  get canvas(): globalThis.HTMLCanvasElement | null;
1641
1553
  /**
1642
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
1643
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
1644
- * until the context exists (i.e. before the first `onReady`).
1554
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
1555
+ * `null` until the context exists.
1645
1556
  *
1646
- * Read it to REPORT what a host provides. It is not a capability check:
1647
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
1648
- * slower depends entirely on the workload. Anything that switches
1649
- * renderers should key on a measured frame budget, not on this string.
1557
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
1558
+ * GPU does, only slower, and by how much depends on the workload. Anything that
1559
+ * switches renderers should key on a measured frame budget, not on this string.
1650
1560
  */
1651
1561
  get rendererInfo(): {
1652
1562
  vendor: string;
1653
1563
  renderer: string;
1654
1564
  } | null;
1655
- /**
1656
- * Registers a callback to be called once the WebGL context is ready.
1657
- * If the context is already available, the callback fires synchronously.
1658
- */
1565
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
1659
1566
  onReady(cb: WebGLReadyCallback): void;
1660
1567
  /**
1661
1568
  * @deprecated Use `onReady()` instead.
1662
1569
  */
1663
1570
  onWebGLReady(cb: WebGLReadyCallback): void;
1664
1571
  /**
1665
- * Records the negotiated GL implementation and, when it rasterises on the
1666
- * CPU, says so ONCE on stderr.
1572
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
1573
+ * says so ONCE on stderr.
1667
1574
  *
1668
- * A GPU-less host is invisible from inside the app: context creation
1669
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
1670
- * is a frame budget blown by three orders of magnitude — which presents as
1671
- * a window that sits there. Naming the renderer at the one place that
1672
- * knows it turns that into a line anyone can act on, for every consumer,
1673
- * without any of them asking.
1575
+ * A GPU-less host is otherwise invisible from inside the app: the context is
1576
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
1577
+ * three orders of magnitude, which presents as a window that just sits there.
1674
1578
  *
1675
- * Diagnostic only: it changes no behaviour, because a software renderer is
1676
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
1677
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
1678
- * comfortably on the same driver that needs 1.1 s for one textured
1679
- * full-screen draw in a 2D game).
1579
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
1580
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
1581
+ * demand-driven three.js scene ran comfortably on the same driver that needed
1582
+ * 1.1 s for a single textured full-screen draw in a 2D game.
1680
1583
  */
1681
1584
  _reportRenderer(gl: OurWebGLRenderingContext): void;
1682
1585
  /**
1683
- * Register a callback invoked whenever the GTK widget is resized.
1684
- * The callback fires alongside the native 'resize' GObject signal and
1685
- * after the DOM 'resize' event has been dispatched on the canvas.
1686
- * Canvas buffer dimensions are NOT automatically updated — consumers
1687
- * should set `canvas.width`/`canvas.height` themselves if desired.
1586
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
1587
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
1588
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
1688
1589
  */
1689
1590
  onResize(cb: (width: number, height: number) => void): void;
1690
1591
  /**
1691
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
1692
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
1693
- * render signal handler. Both are installed once at construction / first render respectively,
1694
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
1592
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
1695
1593
  *
1696
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
1697
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
1698
- * fired replaces the pending callback (single-slot model — matching the existing rAF
1699
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
1700
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
1594
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
1595
+ * callback (latest wins), which makes the older handle stale and a
1596
+ * `cancelAnimationFrame` with it a no-op.
1701
1597
  */
1702
1598
  requestAnimationFrame(cb: FrameRequestCallback): number;
1703
1599
  /**
1704
- * Cancels a previously-scheduled animation frame callback, matching the browser
1705
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
1706
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
1707
- * handle (from a callback that already fired, or was replaced by a later
1708
- * `requestAnimationFrame`) is a no-op.
1600
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
1601
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
1602
+ * no-op.
1709
1603
  *
1710
- * Per-ID matching is load-bearing: without it any consumer that calls
1711
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
1712
- * transient parent `unmap` in a responsive layout) would clear the loop's
1713
- * latest pending callback and freeze rendering until the next manual
1714
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
1715
- * the caller actually owns.
1604
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
1605
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
1606
+ * kills the loop's current callback and freezes rendering.
1716
1607
  */
1717
1608
  cancelAnimationFrame(id: number): void;
1718
1609
  /**
1719
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
1720
- * `cancelAnimationFrame`, `performance` and the two WebGL context
1721
- * constructors — so that browser-targeted code (e.g. Three.js) works
1722
- * unchanged on GJS.
1610
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
1611
+ * runs unchanged on GJS.
1723
1612
  *
1724
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
1725
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
1726
- * and its `/register` install the same set, `installGlobals()` being the
1727
- * explicit imperative path (`@gjsify/webgl/register` keeps the
1728
- * if-undefined guard instead). They used to reach `globalThis` for free
1729
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
1730
- * write into `register.ts` must not shrink what this method does.
1613
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
1614
+ * `installGlobals()` is the explicit imperative path and installs the same set as
1615
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
1731
1616
  */
1732
1617
  installGlobals(): void;
1733
1618
  get allowed_apis(): Gdk.GLAPI;
@@ -2170,95 +2055,68 @@ export declare const WebGLBridge: {
2170
2055
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
2171
2056
  get canvas(): globalThis.HTMLCanvasElement | null;
2172
2057
  /**
2173
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
2174
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
2175
- * until the context exists (i.e. before the first `onReady`).
2058
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
2059
+ * `null` until the context exists.
2176
2060
  *
2177
- * Read it to REPORT what a host provides. It is not a capability check:
2178
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
2179
- * slower depends entirely on the workload. Anything that switches
2180
- * renderers should key on a measured frame budget, not on this string.
2061
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
2062
+ * GPU does, only slower, and by how much depends on the workload. Anything that
2063
+ * switches renderers should key on a measured frame budget, not on this string.
2181
2064
  */
2182
2065
  get rendererInfo(): {
2183
2066
  vendor: string;
2184
2067
  renderer: string;
2185
2068
  } | null;
2186
- /**
2187
- * Registers a callback to be called once the WebGL context is ready.
2188
- * If the context is already available, the callback fires synchronously.
2189
- */
2069
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
2190
2070
  onReady(cb: WebGLReadyCallback): void;
2191
2071
  /**
2192
2072
  * @deprecated Use `onReady()` instead.
2193
2073
  */
2194
2074
  onWebGLReady(cb: WebGLReadyCallback): void;
2195
2075
  /**
2196
- * Records the negotiated GL implementation and, when it rasterises on the
2197
- * CPU, says so ONCE on stderr.
2076
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
2077
+ * says so ONCE on stderr.
2198
2078
  *
2199
- * A GPU-less host is invisible from inside the app: context creation
2200
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
2201
- * is a frame budget blown by three orders of magnitude — which presents as
2202
- * a window that sits there. Naming the renderer at the one place that
2203
- * knows it turns that into a line anyone can act on, for every consumer,
2204
- * without any of them asking.
2079
+ * A GPU-less host is otherwise invisible from inside the app: the context is
2080
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
2081
+ * three orders of magnitude, which presents as a window that just sits there.
2205
2082
  *
2206
- * Diagnostic only: it changes no behaviour, because a software renderer is
2207
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
2208
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
2209
- * comfortably on the same driver that needs 1.1 s for one textured
2210
- * full-screen draw in a 2D game).
2083
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
2084
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
2085
+ * demand-driven three.js scene ran comfortably on the same driver that needed
2086
+ * 1.1 s for a single textured full-screen draw in a 2D game.
2211
2087
  */
2212
2088
  _reportRenderer(gl: OurWebGLRenderingContext): void;
2213
2089
  /**
2214
- * Register a callback invoked whenever the GTK widget is resized.
2215
- * The callback fires alongside the native 'resize' GObject signal and
2216
- * after the DOM 'resize' event has been dispatched on the canvas.
2217
- * Canvas buffer dimensions are NOT automatically updated — consumers
2218
- * should set `canvas.width`/`canvas.height` themselves if desired.
2090
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
2091
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
2092
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
2219
2093
  */
2220
2094
  onResize(cb: (width: number, height: number) => void): void;
2221
2095
  /**
2222
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
2223
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
2224
- * render signal handler. Both are installed once at construction / first render respectively,
2225
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
2096
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
2226
2097
  *
2227
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
2228
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
2229
- * fired replaces the pending callback (single-slot model — matching the existing rAF
2230
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
2231
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
2098
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
2099
+ * callback (latest wins), which makes the older handle stale and a
2100
+ * `cancelAnimationFrame` with it a no-op.
2232
2101
  */
2233
2102
  requestAnimationFrame(cb: FrameRequestCallback): number;
2234
2103
  /**
2235
- * Cancels a previously-scheduled animation frame callback, matching the browser
2236
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
2237
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
2238
- * handle (from a callback that already fired, or was replaced by a later
2239
- * `requestAnimationFrame`) is a no-op.
2104
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
2105
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
2106
+ * no-op.
2240
2107
  *
2241
- * Per-ID matching is load-bearing: without it any consumer that calls
2242
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
2243
- * transient parent `unmap` in a responsive layout) would clear the loop's
2244
- * latest pending callback and freeze rendering until the next manual
2245
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
2246
- * the caller actually owns.
2108
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
2109
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
2110
+ * kills the loop's current callback and freezes rendering.
2247
2111
  */
2248
2112
  cancelAnimationFrame(id: number): void;
2249
2113
  /**
2250
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
2251
- * `cancelAnimationFrame`, `performance` and the two WebGL context
2252
- * constructors — so that browser-targeted code (e.g. Three.js) works
2253
- * unchanged on GJS.
2114
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
2115
+ * runs unchanged on GJS.
2254
2116
  *
2255
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
2256
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
2257
- * and its `/register` install the same set, `installGlobals()` being the
2258
- * explicit imperative path (`@gjsify/webgl/register` keeps the
2259
- * if-undefined guard instead). They used to reach `globalThis` for free
2260
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
2261
- * write into `register.ts` must not shrink what this method does.
2117
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
2118
+ * `installGlobals()` is the explicit imperative path and installs the same set as
2119
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
2262
2120
  */
2263
2121
  installGlobals(): void;
2264
2122
  get allowed_apis(): Gdk.GLAPI;
@@ -2701,95 +2559,68 @@ export declare const WebGLBridge: {
2701
2559
  /** The HTMLCanvasElement wrapping this GLArea. Available after the first render. */
2702
2560
  get canvas(): globalThis.HTMLCanvasElement | null;
2703
2561
  /**
2704
- * The GL implementation GDK negotiated, unmasked — `{vendor, renderer}`
2705
- * straight from `glGetString`, e.g. `Apple Software Renderer`. `null`
2706
- * until the context exists (i.e. before the first `onReady`).
2562
+ * The GL implementation GDK negotiated, unmasked, straight from `glGetString`;
2563
+ * `null` until the context exists.
2707
2564
  *
2708
- * Read it to REPORT what a host provides. It is not a capability check:
2709
- * a CPU rasteriser draws everything a GPU does, only slower, and how much
2710
- * slower depends entirely on the workload. Anything that switches
2711
- * renderers should key on a measured frame budget, not on this string.
2565
+ * For REPORTING, not as a capability check: a CPU rasteriser draws everything a
2566
+ * GPU does, only slower, and by how much depends on the workload. Anything that
2567
+ * switches renderers should key on a measured frame budget, not on this string.
2712
2568
  */
2713
2569
  get rendererInfo(): {
2714
2570
  vendor: string;
2715
2571
  renderer: string;
2716
2572
  } | null;
2717
- /**
2718
- * Registers a callback to be called once the WebGL context is ready.
2719
- * If the context is already available, the callback fires synchronously.
2720
- */
2573
+ /** Runs `cb` once the WebGL context is ready — synchronously if it already is. */
2721
2574
  onReady(cb: WebGLReadyCallback): void;
2722
2575
  /**
2723
2576
  * @deprecated Use `onReady()` instead.
2724
2577
  */
2725
2578
  onWebGLReady(cb: WebGLReadyCallback): void;
2726
2579
  /**
2727
- * Records the negotiated GL implementation and, when it rasterises on the
2728
- * CPU, says so ONCE on stderr.
2580
+ * Records the negotiated GL implementation and, when it rasterises on the CPU,
2581
+ * says so ONCE on stderr.
2729
2582
  *
2730
- * A GPU-less host is invisible from inside the app: context creation
2731
- * succeeds, every draw call succeeds, nothing rejects, and the only tell
2732
- * is a frame budget blown by three orders of magnitude — which presents as
2733
- * a window that sits there. Naming the renderer at the one place that
2734
- * knows it turns that into a line anyone can act on, for every consumer,
2735
- * without any of them asking.
2583
+ * A GPU-less host is otherwise invisible from inside the app: the context is
2584
+ * created, every draw call succeeds, and the only tell is a frame budget blown by
2585
+ * three orders of magnitude, which presents as a window that just sits there.
2736
2586
  *
2737
- * Diagnostic only: it changes no behaviour, because a software renderer is
2738
- * fine for plenty of workloads (measured on one GPU-less macOS host: a
2739
- * demand-driven three.js scene and a flat-shaded full-screen quad both run
2740
- * comfortably on the same driver that needs 1.1 s for one textured
2741
- * full-screen draw in a 2D game).
2587
+ * Diagnostic only, and deliberately changes no behaviour: a software renderer is
2588
+ * fine for plenty of workloads. Measured on one GPU-less macOS host, a
2589
+ * demand-driven three.js scene ran comfortably on the same driver that needed
2590
+ * 1.1 s for a single textured full-screen draw in a 2D game.
2742
2591
  */
2743
2592
  _reportRenderer(gl: OurWebGLRenderingContext): void;
2744
2593
  /**
2745
- * Register a callback invoked whenever the GTK widget is resized.
2746
- * The callback fires alongside the native 'resize' GObject signal and
2747
- * after the DOM 'resize' event has been dispatched on the canvas.
2748
- * Canvas buffer dimensions are NOT automatically updated — consumers
2749
- * should set `canvas.width`/`canvas.height` themselves if desired.
2594
+ * Runs `cb` on every GTK resize, after the DOM 'resize' event has been dispatched
2595
+ * on the canvas. Canvas buffer dimensions are NOT updated for you: set
2596
+ * `canvas.width`/`canvas.height` yourself if the drawing buffer must follow.
2750
2597
  */
2751
2598
  onResize(cb: (width: number, height: number) => void): void;
2752
2599
  /**
2753
- * Schedules a single animation frame callback, matching the browser `requestAnimationFrame` API.
2754
- * Backed by a persistent GTK frame clock tick callback (vsync-synced) + a persistent GLArea
2755
- * render signal handler. Both are installed once at construction / first render respectively,
2756
- * eliminating per-frame GLib.Source allocation and GObject signal connect/disconnect overhead.
2600
+ * Browser `requestAnimationFrame`, driven by the vsync-synced GTK frame clock.
2757
2601
  *
2758
- * Returns a monotonically-increasing handle; pass it to `cancelAnimationFrame(id)` to cancel
2759
- * the specific pending callback. A second `requestAnimationFrame` call before the first has
2760
- * fired replaces the pending callback (single-slot model — matching the existing rAF
2761
- * convention of "queue 1 callback per frame, the latest wins"); the older handle becomes
2762
- * stale and a `cancelAnimationFrame` with that handle is a no-op.
2602
+ * SINGLE SLOT: a second call before the first has fired REPLACES the pending
2603
+ * callback (latest wins), which makes the older handle stale and a
2604
+ * `cancelAnimationFrame` with it a no-op.
2763
2605
  */
2764
2606
  requestAnimationFrame(cb: FrameRequestCallback): number;
2765
2607
  /**
2766
- * Cancels a previously-scheduled animation frame callback, matching the browser
2767
- * `cancelAnimationFrame` API. Only clears the pending callback when `id` matches
2768
- * the handle returned by the most recent `requestAnimationFrame` call — a stale
2769
- * handle (from a callback that already fired, or was replaced by a later
2770
- * `requestAnimationFrame`) is a no-op.
2608
+ * Browser `cancelAnimationFrame`. Clears the pending callback only when `id` is
2609
+ * the handle from the most recent `requestAnimationFrame`; a stale handle is a
2610
+ * no-op.
2771
2611
  *
2772
- * Per-ID matching is load-bearing: without it any consumer that calls
2773
- * `cancelAnimationFrame` (e.g. Excalibur's `Engine.stop()`, called from a
2774
- * transient parent `unmap` in a responsive layout) would clear the loop's
2775
- * latest pending callback and freeze rendering until the next manual
2776
- * `requestAnimationFrame`. With it, the cancel only fires for the handle
2777
- * the caller actually owns.
2612
+ * That matching is load-bearing: without it any stray cancel (Excalibur's
2613
+ * `Engine.stop()`, fired from a transient parent `unmap` in a responsive layout)
2614
+ * kills the loop's current callback and freezes rendering.
2778
2615
  */
2779
2616
  cancelAnimationFrame(id: number): void;
2780
2617
  /**
2781
- * Sets the browser globals this bridge owns — `requestAnimationFrame`,
2782
- * `cancelAnimationFrame`, `performance` and the two WebGL context
2783
- * constructors — so that browser-targeted code (e.g. Three.js) works
2784
- * unchanged on GJS.
2618
+ * Sets the browser globals this bridge owns, so browser-targeted code (Three.js)
2619
+ * runs unchanged on GJS.
2785
2620
  *
2786
- * `WebGLRenderingContext` / `WebGL2RenderingContext` are installed
2787
- * UNCONDITIONALLY here: per ADR 0012 rule 5 a bridge's `installGlobals()`
2788
- * and its `/register` install the same set, `installGlobals()` being the
2789
- * explicit imperative path (`@gjsify/webgl/register` keeps the
2790
- * if-undefined guard instead). They used to reach `globalThis` for free
2791
- * as a side effect of importing the `@gjsify/webgl` barrel; moving that
2792
- * write into `register.ts` must not shrink what this method does.
2621
+ * The two context constructors are installed UNCONDITIONALLY: per ADR 0012 rule 5
2622
+ * `installGlobals()` is the explicit imperative path and installs the same set as
2623
+ * `@gjsify/webgl/register`, which keeps the if-undefined guard instead.
2793
2624
  */
2794
2625
  installGlobals(): void;
2795
2626
  get allowed_apis(): Gdk.GLAPI;