react-x11 2.18.4 → 2.18.6
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/package.json +3 -3
- package/src/Reconciler.js +9 -2
- package/src/backend/context2d.js +90 -20
- package/src/cocoa/app.js +1 -1
- package/src/cocoa/fonts.js +2 -1
- package/src/cocoa/presenter.js +1 -1
- package/src/decorations.js +1 -1
- package/src/fonts.js +1 -1
- package/src/foreignnodes.js +6 -1
- package/src/glbackend.js +1 -1
- package/src/glnodes.js +1 -1
- package/src/gloverlay.js +48 -25
- package/src/imagesource.js +4 -1
- package/src/node.d.ts +3 -0
- package/src/nodes/boxpaint.js +5 -3
- package/src/nodes/image.js +3 -3
- package/src/nodes/invalidate.js +31 -2
- package/src/nodes/layout.js +4 -4
- package/src/nodes/node.js +15 -3
- package/src/nodes/paint.js +1 -1
- package/src/nodes/scrollable.js +5 -5
- package/src/nodes/scrollblit.js +12 -2
- package/src/nodes/text.js +1 -1
- package/src/nodes/window/capabilities.js +1 -1
- package/src/nodes/window/flush.js +27 -18
- package/src/ntk.js +8 -31
- package/src/ntkroot.js +50 -0
- package/src/offscreen.js +33 -0
- package/src/paintcache.js +11 -7
- package/src/refresh/loader.js +23 -4
- package/src/screencolor.js +1 -1
- package/src/styles.js +1 -1
- package/src/svgnodes.js +1 -1
- package/src/symbols.js +4 -2
- package/src/trayhooks.js +1 -1
- package/src/wayland/context2d.js +2 -1
- package/src/wayland/decorations.js +1 -1
- package/src/win32/a11y.js +66 -6
- package/src/win32/app.js +83 -9
- package/src/win32/fonts.js +27 -8
- package/src/win32/overlay.js +244 -0
- package/src/win32/panewindow.js +6 -0
- package/src/win32/window.js +71 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.18.
|
|
3
|
+
"version": "2.18.6",
|
|
4
4
|
"description": "react renderer with X11 as a target",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -103,14 +103,14 @@
|
|
|
103
103
|
"dependencies": {
|
|
104
104
|
"ical.js": "^2.2.1",
|
|
105
105
|
"linebreak": "^1.1.0",
|
|
106
|
-
"ntk": "^8.
|
|
106
|
+
"ntk": "^8.10.0",
|
|
107
107
|
"react-reconciler": "^0.34.0",
|
|
108
108
|
"yoga-layout": "^3.2.1"
|
|
109
109
|
},
|
|
110
110
|
"optionalDependencies": {
|
|
111
111
|
"@windowkit/appkit": "^0.12.0",
|
|
112
112
|
"@windowkit/wayland": "^3.1.1",
|
|
113
|
-
"@windowkit/win32": "^0.0.
|
|
113
|
+
"@windowkit/win32": "^0.0.2",
|
|
114
114
|
"dbus-native": "^0.15.1",
|
|
115
115
|
"x11-dri": "^0.9.0"
|
|
116
116
|
},
|
package/src/Reconciler.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
import React from 'react';
|
|
9
9
|
import ReactReconciler from 'react-reconciler';
|
|
10
|
-
import { createClient } from 'ntk';
|
|
11
10
|
|
|
12
11
|
import {
|
|
13
12
|
ConcurrentRoot,
|
|
@@ -22,6 +21,7 @@ import {
|
|
|
22
21
|
flushWindowMaps,
|
|
23
22
|
flushWindowRestacks,
|
|
24
23
|
} from './nodes/window/window.js';
|
|
24
|
+
import { loadNtk } from './ntkroot.js';
|
|
25
25
|
import { PopupNode } from './nodes/window/popup.js';
|
|
26
26
|
import { BoxNode } from './nodes/box.js';
|
|
27
27
|
import { THEME_SCOPE } from './nodes/kinds.js';
|
|
@@ -629,6 +629,8 @@ function loadIntegrations() {
|
|
|
629
629
|
* anyone hits and the least self-explanatory failure in the library.
|
|
630
630
|
*/
|
|
631
631
|
async function connect(options) {
|
|
632
|
+
// ntk's root is the X11 backend's, and loads with it (src/ntkroot.js)
|
|
633
|
+
const { createClient } = await loadNtk();
|
|
632
634
|
try {
|
|
633
635
|
return await createClient(options);
|
|
634
636
|
} catch (err) {
|
|
@@ -822,7 +824,12 @@ export async function createRoot(options = {}) {
|
|
|
822
824
|
const win32Asked =
|
|
823
825
|
rest.backend === 'win32' || process.env.REACT_X11_BACKEND === 'win32';
|
|
824
826
|
const connecting = !owned
|
|
825
|
-
?
|
|
827
|
+
? // an ntk app could only have come from ntk's own createClient: the
|
|
828
|
+
// root is imported already, and this hands it to the code that reads
|
|
829
|
+
// it synchronously (src/ntkroot.js)
|
|
830
|
+
isNtkApp(borrowed)
|
|
831
|
+
? loadNtk().then(() => borrowed)
|
|
832
|
+
: Promise.resolve(borrowed)
|
|
826
833
|
: backend === 'wayland'
|
|
827
834
|
? // Opt-in only, never through 'auto': X11 stays the default on Linux
|
|
828
835
|
// because the remote case (docs/remote.md) is the flagship reason
|
package/src/backend/context2d.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// class keeps the JS-visible state (fillStyle strings, gradient objects,
|
|
24
24
|
// dash arrays) and re-syncs it when the backing surface is replaced after a
|
|
25
25
|
// resize — `_gen` is that generation.
|
|
26
|
-
import { cssColorStraight } from 'ntk';
|
|
26
|
+
import { cssColorStraight } from 'ntk/color';
|
|
27
27
|
|
|
28
28
|
const BLACK = [0, 0, 0, 1];
|
|
29
29
|
|
|
@@ -324,6 +324,17 @@ export class BackendContext2D {
|
|
|
324
324
|
// chunked stroke leaves only its last chunk behind)
|
|
325
325
|
this._cmds = [];
|
|
326
326
|
this._pathStale = false;
|
|
327
|
+
// A bridge that takes a whole path in one call (`ctxPath`, the Windows
|
|
328
|
+
// bridge's) is handed it that way when something paints it: the verbs
|
|
329
|
+
// below only record. A graph's edges are tens of thousands of `lineTo`s
|
|
330
|
+
// a frame, and a call across the boundary for each was milliseconds of
|
|
331
|
+
// every frame spent on the crossing. A bridge without it is handed the
|
|
332
|
+
// path a point at a time, as it is built, exactly as before.
|
|
333
|
+
// Asked with `in` as well: a test's bridge that answers every name with
|
|
334
|
+
// a no-op must not look like one that takes paths whole.
|
|
335
|
+
this._bulkPaths =
|
|
336
|
+
'ctxPath' in native && typeof native.ctxPath === 'function';
|
|
337
|
+
this._pathBuf = null;
|
|
327
338
|
this._strokeChunking = !NO_STROKE_CHUNKING;
|
|
328
339
|
}
|
|
329
340
|
|
|
@@ -547,13 +558,36 @@ export class BackendContext2D {
|
|
|
547
558
|
|
|
548
559
|
setLineDash(segments) {
|
|
549
560
|
this._state.dash = Array.isArray(segments) ? segments : [];
|
|
550
|
-
|
|
561
|
+
// the offset is state of its own, and a new pattern keeps it
|
|
562
|
+
this._native.ctxSetLineDash(
|
|
563
|
+
this._s(),
|
|
564
|
+
this._state.dash,
|
|
565
|
+
this._state.dashOffset,
|
|
566
|
+
);
|
|
551
567
|
}
|
|
552
568
|
|
|
553
569
|
getLineDash() {
|
|
554
570
|
return [...this._state.dash];
|
|
555
571
|
}
|
|
556
572
|
|
|
573
|
+
/**
|
|
574
|
+
* Where along the pattern a stroke starts — the phase a marching dash
|
|
575
|
+
* moves. Missing here, a caller that asks `'lineDashOffset' in ctx` found
|
|
576
|
+
* no such thing and every stroke started its pattern at nought: the
|
|
577
|
+
* dashes of an animated edge were repainted sixteen times a second and
|
|
578
|
+
* never moved.
|
|
579
|
+
*/
|
|
580
|
+
get lineDashOffset() {
|
|
581
|
+
return this._state.dashOffset;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
set lineDashOffset(value) {
|
|
585
|
+
// canvas ignores anything that is not a finite number
|
|
586
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) return;
|
|
587
|
+
this._state.dashOffset = value;
|
|
588
|
+
this._native.ctxSetLineDash(this._s(), this._state.dash, value);
|
|
589
|
+
}
|
|
590
|
+
|
|
557
591
|
save() {
|
|
558
592
|
// Sync before pushing: a fresh surface empties the stack on its way in,
|
|
559
593
|
// and a state pushed ahead of that sync was lost to it — the first
|
|
@@ -670,6 +704,21 @@ export class BackendContext2D {
|
|
|
670
704
|
_emit(surface, from, to) {
|
|
671
705
|
const c = this._cmds;
|
|
672
706
|
const n = this._native;
|
|
707
|
+
if (this._bulkPaths) {
|
|
708
|
+
// one call, through a buffer kept between paths — the record is the
|
|
709
|
+
// same stream the bridge parses, arguments and all
|
|
710
|
+
const count = to - from;
|
|
711
|
+
if (count <= 0) return;
|
|
712
|
+
let buf = this._pathBuf;
|
|
713
|
+
if (!buf || buf.length < count) {
|
|
714
|
+
buf = this._pathBuf = new Float64Array(
|
|
715
|
+
Math.max(count, (buf?.length ?? 512) * 2),
|
|
716
|
+
);
|
|
717
|
+
}
|
|
718
|
+
for (let i = 0; i < count; i++) buf[i] = c[from + i];
|
|
719
|
+
n.ctxPath(surface, buf.subarray(0, count));
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
673
722
|
for (let i = from; i < to;) {
|
|
674
723
|
const op = c[i];
|
|
675
724
|
const a = i + 1;
|
|
@@ -720,26 +769,44 @@ export class BackendContext2D {
|
|
|
720
769
|
|
|
721
770
|
beginPath() {
|
|
722
771
|
this._cmds.length = 0;
|
|
772
|
+
if (this._bulkPaths) {
|
|
773
|
+
this._s();
|
|
774
|
+
// uploaded, empty or not, when something paints it
|
|
775
|
+
this._pathStale = true;
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
723
778
|
this._pathStale = false;
|
|
724
779
|
this._native.ctxBeginPath(this._s());
|
|
725
780
|
}
|
|
726
781
|
|
|
782
|
+
/**
|
|
783
|
+
* The surface to add a path command to now, or null where the path is
|
|
784
|
+
* only recorded and handed over whole when painted (`_bulkPaths`). Either
|
|
785
|
+
* way a fresh surface drops the path first, as `_s` has always done.
|
|
786
|
+
*/
|
|
787
|
+
_pathFor() {
|
|
788
|
+
if (!this._bulkPaths) return this._path();
|
|
789
|
+
this._s();
|
|
790
|
+
this._pathStale = true;
|
|
791
|
+
return null;
|
|
792
|
+
}
|
|
793
|
+
|
|
727
794
|
moveTo(x, y) {
|
|
728
|
-
const surface = this.
|
|
795
|
+
const surface = this._pathFor();
|
|
729
796
|
this._cmds.push(P_MOVE, x, y);
|
|
730
|
-
this._native.ctxMoveTo(surface, x, y);
|
|
797
|
+
if (surface !== null) this._native.ctxMoveTo(surface, x, y);
|
|
731
798
|
}
|
|
732
799
|
|
|
733
800
|
lineTo(x, y) {
|
|
734
|
-
const surface = this.
|
|
801
|
+
const surface = this._pathFor();
|
|
735
802
|
this._cmds.push(P_LINE, x, y);
|
|
736
|
-
this._native.ctxLineTo(surface, x, y);
|
|
803
|
+
if (surface !== null) this._native.ctxLineTo(surface, x, y);
|
|
737
804
|
}
|
|
738
805
|
|
|
739
806
|
rect(x, y, w, h) {
|
|
740
|
-
const surface = this.
|
|
807
|
+
const surface = this._pathFor();
|
|
741
808
|
this._cmds.push(P_RECT, x, y, w, h);
|
|
742
|
-
this._native.ctxRect(surface, x, y, w, h);
|
|
809
|
+
if (surface !== null) this._native.ctxRect(surface, x, y, w, h);
|
|
743
810
|
}
|
|
744
811
|
|
|
745
812
|
roundRect(x, y, w, h, radii) {
|
|
@@ -750,7 +817,7 @@ export class BackendContext2D {
|
|
|
750
817
|
else if (r.length === 3) r = [r[0], r[1], r[2], r[1]];
|
|
751
818
|
const cap = Math.min(Math.abs(w) / 2, Math.abs(h) / 2);
|
|
752
819
|
const clamp = (v) => Math.max(0, Math.min(Number(v) || 0, cap));
|
|
753
|
-
const surface = this.
|
|
820
|
+
const surface = this._pathFor();
|
|
754
821
|
const [r0, r1, r2, r3] = [
|
|
755
822
|
clamp(r[0]),
|
|
756
823
|
clamp(r[1]),
|
|
@@ -758,37 +825,40 @@ export class BackendContext2D {
|
|
|
758
825
|
clamp(r[3]),
|
|
759
826
|
];
|
|
760
827
|
this._cmds.push(P_ROUND, x, y, w, h, r0, r1, r2, r3);
|
|
761
|
-
|
|
828
|
+
if (surface !== null)
|
|
829
|
+
this._native.ctxRoundRect(surface, x, y, w, h, r0, r1, r2, r3);
|
|
762
830
|
}
|
|
763
831
|
|
|
764
832
|
arc(x, y, radius, start, end, anticlockwise = false) {
|
|
765
|
-
const surface = this.
|
|
833
|
+
const surface = this._pathFor();
|
|
766
834
|
this._cmds.push(P_ARC, x, y, radius, start, end, anticlockwise ? 1 : 0);
|
|
767
|
-
|
|
835
|
+
if (surface !== null)
|
|
836
|
+
this._native.ctxArc(surface, x, y, radius, start, end, anticlockwise);
|
|
768
837
|
}
|
|
769
838
|
|
|
770
839
|
ellipse(x, y, rx, ry) {
|
|
771
|
-
const surface = this.
|
|
840
|
+
const surface = this._pathFor();
|
|
772
841
|
this._cmds.push(P_ELLIPSE, x, y, rx, ry);
|
|
773
|
-
this._native.ctxEllipse(surface, x, y, rx, ry);
|
|
842
|
+
if (surface !== null) this._native.ctxEllipse(surface, x, y, rx, ry);
|
|
774
843
|
}
|
|
775
844
|
|
|
776
845
|
bezierCurveTo(c1x, c1y, c2x, c2y, x, y) {
|
|
777
|
-
const surface = this.
|
|
846
|
+
const surface = this._pathFor();
|
|
778
847
|
this._cmds.push(P_CURVE, c1x, c1y, c2x, c2y, x, y);
|
|
779
|
-
|
|
848
|
+
if (surface !== null)
|
|
849
|
+
this._native.ctxCurveTo(surface, c1x, c1y, c2x, c2y, x, y);
|
|
780
850
|
}
|
|
781
851
|
|
|
782
852
|
quadraticCurveTo(cx, cy, x, y) {
|
|
783
|
-
const surface = this.
|
|
853
|
+
const surface = this._pathFor();
|
|
784
854
|
this._cmds.push(P_QUAD, cx, cy, x, y);
|
|
785
|
-
this._native.ctxQuadTo(surface, cx, cy, x, y);
|
|
855
|
+
if (surface !== null) this._native.ctxQuadTo(surface, cx, cy, x, y);
|
|
786
856
|
}
|
|
787
857
|
|
|
788
858
|
closePath() {
|
|
789
|
-
const surface = this.
|
|
859
|
+
const surface = this._pathFor();
|
|
790
860
|
this._cmds.push(P_CLOSE);
|
|
791
|
-
this._native.ctxClosePath(surface);
|
|
861
|
+
if (surface !== null) this._native.ctxClosePath(surface);
|
|
792
862
|
}
|
|
793
863
|
|
|
794
864
|
// --- painting ------------------------------------------------------------
|
package/src/cocoa/app.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// (`_routeBatch`) with no pump at all (docs/macos.md §"JS on a worker").
|
|
17
17
|
import { isMainThread } from 'node:worker_threads';
|
|
18
18
|
|
|
19
|
-
import { cssColorStraight } from 'ntk';
|
|
19
|
+
import { cssColorStraight } from 'ntk/color';
|
|
20
20
|
|
|
21
21
|
import { deliverActivate, deliverOpen } from '../application.js';
|
|
22
22
|
import { flushPendingFrames } from '../frames.js';
|
package/src/cocoa/fonts.js
CHANGED
|
@@ -26,7 +26,8 @@ import { readFileSync } from 'node:fs';
|
|
|
26
26
|
import { inflateSync } from 'node:zlib';
|
|
27
27
|
|
|
28
28
|
import LineBreaker from 'linebreak';
|
|
29
|
-
import { cssColorStraight
|
|
29
|
+
import { cssColorStraight } from 'ntk/color';
|
|
30
|
+
import Font from 'ntk/font';
|
|
30
31
|
|
|
31
32
|
import { loadNative } from './native.js';
|
|
32
33
|
|
package/src/cocoa/presenter.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
// named repaints everything" rule the X11 damage model has), and geometry
|
|
25
25
|
// is re-diffed every frame because comparing four numbers is cheaper than
|
|
26
26
|
// knowing.
|
|
27
|
-
import { cssColorStraight } from 'ntk';
|
|
27
|
+
import { cssColorStraight } from 'ntk/color';
|
|
28
28
|
|
|
29
29
|
import { Node } from '../nodes/node.js';
|
|
30
30
|
import { addDamageRect, damageToPaint } from '../nodes/damage.js';
|
package/src/decorations.js
CHANGED
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
// derives is the padding a coverage surface has to carry. Both are pure
|
|
35
35
|
// functions — no connection, nothing drawn — so this module stays testable
|
|
36
36
|
// without a server.
|
|
37
|
-
import { shadowReach, shadowSigma } from 'ntk';
|
|
37
|
+
import { shadowReach, shadowSigma } from 'ntk/shadow-math';
|
|
38
38
|
|
|
39
39
|
/** A number, with or without the `px` CSS wants and this vocabulary does not. */
|
|
40
40
|
const LENGTH = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:px)?$/i;
|
package/src/fonts.js
CHANGED
package/src/foreignnodes.js
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
// save set, **never destroyed**. Destroying somebody else's window because a
|
|
31
31
|
// React tree changed is the failure mode this element is most able to cause,
|
|
32
32
|
// and `test/foreign.test.js` asserts against it first.
|
|
33
|
-
import {
|
|
33
|
+
import { x11Ntk } from './ntkroot.js';
|
|
34
34
|
|
|
35
35
|
import { isFocusable } from './a11y.js';
|
|
36
36
|
import { canEmbed } from './embedding.js';
|
|
@@ -161,6 +161,8 @@ export class ForeignNode extends Node {
|
|
|
161
161
|
if (!canEmbed(this.app)) return this._refuse();
|
|
162
162
|
const rect = this._geometry();
|
|
163
163
|
this.rect = rect;
|
|
164
|
+
// `<foreign>` embeds on an X connection only, where ntk's root is loaded
|
|
165
|
+
const { XEmbedSocket } = x11Ntk('<foreign>');
|
|
164
166
|
const socket = new XEmbedSocket(parent, {
|
|
165
167
|
...rect,
|
|
166
168
|
// react-x11 has a focus manager; a socket that answered
|
|
@@ -318,6 +320,8 @@ export class ForeignNode extends Node {
|
|
|
318
320
|
defaultFocus(info) {
|
|
319
321
|
const socket = this.socket;
|
|
320
322
|
if (!socket || !this.client) return;
|
|
323
|
+
// a socket is an X connection's, so ntk's root is loaded (src/ntkroot.js)
|
|
324
|
+
const { XEMBED } = x11Ntk('<foreign>');
|
|
321
325
|
const time = lastInputTime(this.app);
|
|
322
326
|
socket.activate(true, { time });
|
|
323
327
|
const detail =
|
|
@@ -338,6 +342,7 @@ export class ForeignNode extends Node {
|
|
|
338
342
|
if (!socket) return;
|
|
339
343
|
const time = lastInputTime(this.app);
|
|
340
344
|
if (this._focusSent) {
|
|
345
|
+
const { XEMBED } = x11Ntk('<foreign>');
|
|
341
346
|
socket.send(XEMBED.FOCUS_OUT, 0, 0, 0, { time });
|
|
342
347
|
this._focusSent = false;
|
|
343
348
|
}
|
package/src/glbackend.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
// `appcontext.js` (for `useSupports('shaders')`) and the node layer can use
|
|
20
20
|
// it without the two importing each other.
|
|
21
21
|
|
|
22
|
-
import { GLError } from 'ntk';
|
|
22
|
+
import { GLError } from 'ntk/gl';
|
|
23
23
|
|
|
24
24
|
/** Does this connection's policy ever choose the direct backend? */
|
|
25
25
|
const wantsDirect = (app) => {
|
package/src/glnodes.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// (NEXT_STEPS §4), sized and positioned by the parent's yoga layout like any
|
|
6
6
|
// other drawn node. Everything about the surface is here; the scene graph
|
|
7
7
|
// that draws into it comes later (docs/glx-plan.md).
|
|
8
|
-
import { cssColorStraight } from 'ntk';
|
|
8
|
+
import { cssColorStraight } from 'ntk/color';
|
|
9
9
|
|
|
10
10
|
// re-exported so the GL element layer stays one import for consumers
|
|
11
11
|
export { directGLFailure, hasDirectGL } from './glbackend.js';
|
package/src/gloverlay.js
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
// A leaf module, like src/embedding.js: `appcontext.js` asks `canOverlay`
|
|
61
61
|
// for `useSupports('glOverlay')`, and it imports nothing of ours but the
|
|
62
62
|
// damage model's arithmetic, which imports nothing at all.
|
|
63
|
-
import { cssColorStraight } from 'ntk';
|
|
63
|
+
import { cssColorStraight } from 'ntk/color';
|
|
64
64
|
|
|
65
65
|
import { FULL_DAMAGE, addDamageRect } from './nodes/damage.js';
|
|
66
66
|
import {
|
|
@@ -114,6 +114,18 @@ export function overlayRefusal(app) {
|
|
|
114
114
|
return (app && refusals.get(app)) ?? null;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* A backend saying nothing can be drawn over a GL surface on this app, and
|
|
119
|
+
* why: one whose child windows are not X windows, and that has no
|
|
120
|
+
* composited pane either — on Windows a window with a parent is a GL
|
|
121
|
+
* surface of its own (src/win32/glarea.js), and a bridge without layers has
|
|
122
|
+
* nothing else to offer. Before the first render, like `beginGlOverlay`, so
|
|
123
|
+
* `useSupports('glOverlay')` never changes its answer.
|
|
124
|
+
*/
|
|
125
|
+
export function refuseOverlay(app, reason) {
|
|
126
|
+
if (app) refusals.set(app, reason);
|
|
127
|
+
}
|
|
128
|
+
|
|
117
129
|
/**
|
|
118
130
|
* Is this an X server where nothing can be drawn over a GL surface? Asked
|
|
119
131
|
* in `createRoot`, with the other startup probes and before the first
|
|
@@ -654,35 +666,46 @@ export class GlOverlay {
|
|
|
654
666
|
_paintPane(pane, passes) {
|
|
655
667
|
const area = this.area;
|
|
656
668
|
const root = area.root;
|
|
657
|
-
|
|
658
|
-
if (!ctx || !root) return;
|
|
669
|
+
if (!root) return;
|
|
659
670
|
// an opaque pane is filled with what the surface starts its frames from;
|
|
660
671
|
// a composited one is cleared, and shows the frame itself
|
|
661
672
|
const ground = pane.transparent ? null : groundOf(area.props);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
ctx.
|
|
665
|
-
|
|
666
|
-
ctx.
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
ctx.
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
} else {
|
|
675
|
-
ctx.clearRect(pass.x, pass.y, pass.width, pass.height);
|
|
676
|
-
}
|
|
677
|
-
root._paintDamage = pass;
|
|
678
|
-
area._paintChildren(ctx);
|
|
679
|
-
} finally {
|
|
680
|
-
root._paintDamage = null;
|
|
681
|
-
ctx.restore();
|
|
673
|
+
// one pass, drawn by a context in window coordinates
|
|
674
|
+
const paintPass = (ctx, pass) => {
|
|
675
|
+
ctx.save();
|
|
676
|
+
try {
|
|
677
|
+
ctx.beginPath();
|
|
678
|
+
ctx.rect(pass.x, pass.y, pass.width, pass.height);
|
|
679
|
+
ctx.clip();
|
|
680
|
+
if (ground) {
|
|
681
|
+
ctx.fillStyle = ground;
|
|
682
|
+
ctx.fillRect(pass.x, pass.y, pass.width, pass.height);
|
|
683
|
+
} else {
|
|
684
|
+
ctx.clearRect(pass.x, pass.y, pass.width, pass.height);
|
|
682
685
|
}
|
|
686
|
+
root._paintDamage = pass;
|
|
687
|
+
area._paintChildren(ctx);
|
|
688
|
+
} finally {
|
|
689
|
+
root._paintDamage = null;
|
|
690
|
+
ctx.restore();
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
if (typeof pane.wnd.paintPasses === 'function') {
|
|
694
|
+
// A pane drawn a rect at a time — a DirectComposition surface hands
|
|
695
|
+
// out a context per `BeginDraw`, valid until `EndDraw` — opens each
|
|
696
|
+
// pass itself (src/win32/overlay.js), the way such a window takes its
|
|
697
|
+
// frame through `presentFrame`.
|
|
698
|
+
pane.wnd.paintPasses(passes, paintPass);
|
|
699
|
+
} else {
|
|
700
|
+
const ctx = pane.context();
|
|
701
|
+
if (!ctx) return;
|
|
702
|
+
ctx.save();
|
|
703
|
+
try {
|
|
704
|
+
ctx.translate(-pane.rect.x, -pane.rect.y);
|
|
705
|
+
for (const pass of passes) paintPass(ctx, pass);
|
|
706
|
+
} finally {
|
|
707
|
+
ctx.restore();
|
|
683
708
|
}
|
|
684
|
-
} finally {
|
|
685
|
-
ctx.restore();
|
|
686
709
|
}
|
|
687
710
|
// A layer's contents are a copy the bitmap is pushed to; an X window's
|
|
688
711
|
// backing store is blitted by ntk on its own, from the paint above
|
package/src/imagesource.js
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
// and the `cacheKey` cache live here, where a test needs no server; the node
|
|
7
7
|
// half in nodes/image.js is only lifecycle — when to resolve, when to claim
|
|
8
8
|
// damage, when to let go.
|
|
9
|
-
import { Image,
|
|
9
|
+
import { Image, decodeImage } from 'ntk/image';
|
|
10
|
+
|
|
11
|
+
import { x11Ntk } from './ntkroot.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* A client-side object the 2d context composites as-is: an ntk `Image` or
|
|
@@ -429,6 +431,7 @@ export class DrawableSource {
|
|
|
429
431
|
picture() {
|
|
430
432
|
if (!this._picture) {
|
|
431
433
|
const Render = this.app.display.Render;
|
|
434
|
+
const { Picture } = x11Ntk('<image> on a server-side picture');
|
|
432
435
|
this._picture = new Picture(this.app, {
|
|
433
436
|
drawable: { id: this.id },
|
|
434
437
|
format: Render[DEPTH_FORMATS[this.depth]],
|
package/src/node.d.ts
CHANGED
|
@@ -100,6 +100,9 @@ export interface Context2D {
|
|
|
100
100
|
};
|
|
101
101
|
setLineDash(segments: number[]): void;
|
|
102
102
|
getLineDash(): number[];
|
|
103
|
+
/** Where along the dash pattern a stroke starts, in the same units as the
|
|
104
|
+
* pattern — the phase a marching dash moves. */
|
|
105
|
+
lineDashOffset: number;
|
|
103
106
|
beginPath(): void;
|
|
104
107
|
closePath(): void;
|
|
105
108
|
moveTo(x: number, y: number): void;
|
package/src/nodes/boxpaint.js
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
// `paintcache.js`: a named import of something an older ntk does not export
|
|
19
19
|
// is a *load-time* SyntaxError, which would take the renderer down rather
|
|
20
20
|
// than the one feature that needs it.
|
|
21
|
-
import
|
|
21
|
+
import { ntkRoot, x11Ntk } from '../ntkroot.js';
|
|
22
22
|
import { isFocusable as a11yFocusable } from '../a11y.js';
|
|
23
23
|
import { DEV } from './util.js';
|
|
24
24
|
|
|
@@ -320,7 +320,7 @@ export class NodeBoxPaint {
|
|
|
320
320
|
);
|
|
321
321
|
sctx.fill();
|
|
322
322
|
},
|
|
323
|
-
after: (surface) =>
|
|
323
|
+
after: (surface) => x11Ntk('a baked shadow').blurCoverage(surface, sigma),
|
|
324
324
|
live: () => this._paintShadowLive(ctx, plan),
|
|
325
325
|
};
|
|
326
326
|
const cache = this.root?._paintCache;
|
|
@@ -338,7 +338,9 @@ export class NodeBoxPaint {
|
|
|
338
338
|
* a k x k kernel, by the ratio of 2k to k squared.
|
|
339
339
|
*/
|
|
340
340
|
_paintShadowLive(ctx, plan) {
|
|
341
|
-
|
|
341
|
+
const ntk = ntkRoot();
|
|
342
|
+
if (typeof ntk?.Surface !== 'function' || !this.app?.display?.Render)
|
|
343
|
+
return;
|
|
342
344
|
let surface = null;
|
|
343
345
|
try {
|
|
344
346
|
surface = new ntk.Surface(this.app, {
|
package/src/nodes/image.js
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
// `paintcache.js`: a named import of something an older ntk does not export
|
|
21
21
|
// is a *load-time* SyntaxError, which would take the renderer down rather
|
|
22
22
|
// than the one feature that needs it.
|
|
23
|
-
import
|
|
23
|
+
import { loadImage } from 'ntk/image';
|
|
24
24
|
import { symbolWeight, symbolsFor, warnOnce } from '../symbols.js';
|
|
25
25
|
import { intrinsicSize } from './layout.js';
|
|
26
26
|
import { Node } from './node.js';
|
|
@@ -164,7 +164,7 @@ export class ImageNode extends Node {
|
|
|
164
164
|
_loadEntry(src) {
|
|
165
165
|
if (isPathImageSource(src)) {
|
|
166
166
|
return {
|
|
167
|
-
promise:
|
|
167
|
+
promise: loadImage(toLoadablePath(src)).then(
|
|
168
168
|
(image) => image,
|
|
169
169
|
(err) => {
|
|
170
170
|
console.error(
|
|
@@ -196,7 +196,7 @@ export class ImageNode extends Node {
|
|
|
196
196
|
async _loadFile(src) {
|
|
197
197
|
const token = ++this._loadToken;
|
|
198
198
|
try {
|
|
199
|
-
const image = await
|
|
199
|
+
const image = await loadImage(toLoadablePath(src));
|
|
200
200
|
if (token !== this._loadToken || this.destroyed) return;
|
|
201
201
|
this._ownedImage = image;
|
|
202
202
|
this._setImage(image);
|
package/src/nodes/invalidate.js
CHANGED
|
@@ -314,8 +314,16 @@ export class WindowInvalidate {
|
|
|
314
314
|
// the whole reason this is not just `needsLayout`: a scroll moves an
|
|
315
315
|
// offset applied during `absolutize` and leaves every yoga node exactly
|
|
316
316
|
// as it was, at input rate, on the biggest trees in any app.
|
|
317
|
-
|
|
318
|
-
|
|
317
|
+
//
|
|
318
|
+
// Nor does a box that only moved (`'position'`, from `applyProps`):
|
|
319
|
+
// absolutely positioned and sized by its own width and height, it is
|
|
320
|
+
// out of every ancestor's flow and sizes nothing inside it by its
|
|
321
|
+
// insets. A card dragged across a graph — or a thousand of them
|
|
322
|
+
// panned — was a full measurement of the content floors a step.
|
|
323
|
+
if (reason !== 'scroll' && reason !== 'position') {
|
|
324
|
+
this._floorsDirty = true;
|
|
325
|
+
}
|
|
326
|
+
if (reason !== 'scroll' && reason !== 'resize' && reason !== 'position') {
|
|
319
327
|
this._floorsContentDirty = true;
|
|
320
328
|
}
|
|
321
329
|
}
|
|
@@ -469,6 +477,27 @@ export class WindowInvalidate {
|
|
|
469
477
|
return WINDOW_ONLY;
|
|
470
478
|
}
|
|
471
479
|
|
|
480
|
+
/**
|
|
481
|
+
* One rect the layout diff says a node moved through (`layoutDiff.sink`,
|
|
482
|
+
* nodes/window/flush.js), to the lists that paint that node — the same
|
|
483
|
+
* reach a claim of it has (`_paneReach`). A node inside a surface is the
|
|
484
|
+
* panes' alone: the window's pass under the surface would repaint pixels
|
|
485
|
+
* no child of it ever had, and it went to both, so a card dragged across
|
|
486
|
+
* a graph drawn through GL was a window pass under the surface on every
|
|
487
|
+
* step. Answers whether the window's list took it.
|
|
488
|
+
*/
|
|
489
|
+
_claimLayoutMove(rect, node, cap) {
|
|
490
|
+
const reach =
|
|
491
|
+
this._overlaid.size !== 0 ? this._paneReach(node ?? null) : WINDOW_ONLY;
|
|
492
|
+
let windowTook = false;
|
|
493
|
+
if (reach !== PANES_ONLY && this._damage !== FULL_DAMAGE) {
|
|
494
|
+
this._damage = addDamageRect(this._damage, rect, cap);
|
|
495
|
+
windowTook = true;
|
|
496
|
+
}
|
|
497
|
+
if (reach !== WINDOW_ONLY) this._addPaneDamage(rect);
|
|
498
|
+
return windowTook;
|
|
499
|
+
}
|
|
500
|
+
|
|
472
501
|
/** One rect more for the panes, unless they repaint whole already. */
|
|
473
502
|
_addPaneDamage(rect) {
|
|
474
503
|
if (this._paneDamage === FULL_DAMAGE) return;
|
package/src/nodes/layout.js
CHANGED
|
@@ -313,17 +313,17 @@ export class NodeLayout {
|
|
|
313
313
|
) {
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
|
-
if (had) layoutDiff.sink(insetRect(was, -grow));
|
|
316
|
+
if (had) layoutDiff.sink(insetRect(was, -grow), this);
|
|
317
317
|
if (width > 0 && height > 0) {
|
|
318
|
-
layoutDiff.sink(insetRect(this.abs, -grow));
|
|
318
|
+
layoutDiff.sink(insetRect(this.abs, -grow), this);
|
|
319
319
|
}
|
|
320
320
|
return;
|
|
321
321
|
}
|
|
322
322
|
if (had) {
|
|
323
|
-
layoutDiff.sink(insetRect(old, -grow));
|
|
323
|
+
layoutDiff.sink(insetRect(old, -grow), this);
|
|
324
324
|
}
|
|
325
325
|
if (width > 0 && height > 0) {
|
|
326
|
-
layoutDiff.sink(insetRect(this.abs, -grow));
|
|
326
|
+
layoutDiff.sink(insetRect(this.abs, -grow), this);
|
|
327
327
|
}
|
|
328
328
|
}
|
|
329
329
|
}
|
package/src/nodes/node.js
CHANGED
|
@@ -605,6 +605,18 @@ export class Node {
|
|
|
605
605
|
// which is what keeps a commit from widening the region to every node
|
|
606
606
|
// it touched.
|
|
607
607
|
if (layoutChanged) {
|
|
608
|
+
const moved = onlyInsetsChanged(style, prevStyle);
|
|
609
|
+
// An absolutely positioned box of a set size that moved and changed
|
|
610
|
+
// nothing else is out of its ancestors' flow and sized by its width
|
|
611
|
+
// and height rather than its insets, so no content floor anywhere
|
|
612
|
+
// moved with it (src/nodes/invalidate.js, `'position'`).
|
|
613
|
+
const reason =
|
|
614
|
+
moved &&
|
|
615
|
+
style.position === 'absolute' &&
|
|
616
|
+
typeof style.width === 'number' &&
|
|
617
|
+
typeof style.height === 'number'
|
|
618
|
+
? 'position'
|
|
619
|
+
: 'props';
|
|
608
620
|
// A child of a `<glarea>` that only moved (issue #644): its pixels are
|
|
609
621
|
// on a pane, one shift away, and the layout pass reports the move to
|
|
610
622
|
// the overlay rather than claiming it (src/glnodes.js). A claim of the
|
|
@@ -613,12 +625,12 @@ export class Node {
|
|
|
613
625
|
// whenever the overlay cannot move the pixels.
|
|
614
626
|
if (
|
|
615
627
|
this.parent?.isGlArea &&
|
|
616
|
-
|
|
628
|
+
moved &&
|
|
617
629
|
!this.paintChanged(newProps, prev)
|
|
618
630
|
) {
|
|
619
|
-
this.invalidate(true, NO_DAMAGE,
|
|
631
|
+
this.invalidate(true, NO_DAMAGE, reason);
|
|
620
632
|
} else {
|
|
621
|
-
this._invalidateLayout(
|
|
633
|
+
this._invalidateLayout(reason);
|
|
622
634
|
}
|
|
623
635
|
} else {
|
|
624
636
|
// The style half is asked here rather than inside `paintChanged`, and
|