react-x11 2.18.5 → 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 +25 -2
- 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 +1 -1
- 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 +21 -0
- package/src/nodes/layout.js +4 -4
- package/src/nodes/paint.js +1 -1
- package/src/nodes/scrollable.js +5 -5
- package/src/nodes/text.js +1 -1
- package/src/nodes/window/capabilities.js +1 -1
- package/src/nodes/window/flush.js +7 -15
- 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/fonts.js +1 -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
|
|
|
@@ -558,13 +558,36 @@ export class BackendContext2D {
|
|
|
558
558
|
|
|
559
559
|
setLineDash(segments) {
|
|
560
560
|
this._state.dash = Array.isArray(segments) ? segments : [];
|
|
561
|
-
|
|
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
|
+
);
|
|
562
567
|
}
|
|
563
568
|
|
|
564
569
|
getLineDash() {
|
|
565
570
|
return [...this._state.dash];
|
|
566
571
|
}
|
|
567
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
|
+
|
|
568
591
|
save() {
|
|
569
592
|
// Sync before pushing: a fresh surface empties the stack on its way in,
|
|
570
593
|
// and a state pushed ahead of that sync was lost to it — the first
|
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 {
|
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
|
@@ -477,6 +477,27 @@ export class WindowInvalidate {
|
|
|
477
477
|
return WINDOW_ONLY;
|
|
478
478
|
}
|
|
479
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
|
+
|
|
480
501
|
/** One rect more for the panes, unless they repaint whole already. */
|
|
481
502
|
_addPaneDamage(rect) {
|
|
482
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/paint.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// window, background first.
|
|
4
4
|
|
|
5
5
|
import { isPlaced } from '../layouts.js';
|
|
6
|
-
import { Surface } from '../
|
|
6
|
+
import { Surface } from '../offscreen.js';
|
|
7
7
|
import { isPaintedColor } from './boxpaint.js';
|
|
8
8
|
import { DAMAGE_SLOP } from './damage.js';
|
|
9
9
|
import { DRAWN_KINDS } from './kinds.js';
|
package/src/nodes/scrollable.js
CHANGED
|
@@ -301,11 +301,11 @@ export const Scrollable = (Base) =>
|
|
|
301
301
|
// for a `<glarea>` child's move that this pane rides (issue
|
|
302
302
|
// #644): its box claimed nothing, and a pane laid out again may
|
|
303
303
|
// have moved rows the move's copy carries where they were.
|
|
304
|
-
if (clamped || layoutDiff.ride) outer(vp);
|
|
304
|
+
if (clamped || layoutDiff.ride) outer(vp, this);
|
|
305
305
|
} else {
|
|
306
|
-
layoutDiff.sink = (rect) => {
|
|
306
|
+
layoutDiff.sink = (rect, node) => {
|
|
307
307
|
const clipped = intersectRects(rect, vp);
|
|
308
|
-
if (clipped) outer(clipped);
|
|
308
|
+
if (clipped) outer(clipped, node);
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
311
|
this._claimThumbs(thumbsWere, outer);
|
|
@@ -863,8 +863,8 @@ export const Scrollable = (Base) =>
|
|
|
863
863
|
if (sameThumb(before, after)) continue;
|
|
864
864
|
// a pixel of slop for the rounded corners' antialiasing, as the
|
|
865
865
|
// blit's own thumb repair takes
|
|
866
|
-
if (before) sink(insetRect(before, -1));
|
|
867
|
-
if (after) sink(insetRect(after, -1));
|
|
866
|
+
if (before) sink(insetRect(before, -1), this);
|
|
867
|
+
if (after) sink(insetRect(after, -1), this);
|
|
868
868
|
}
|
|
869
869
|
}
|
|
870
870
|
|
package/src/nodes/text.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// selection paints, and the glyph strip.
|
|
3
3
|
|
|
4
4
|
import { localTextStyleChanged, TEXT_REMEASURE } from '../styles.js';
|
|
5
|
-
import { cssColorStraight } from 'ntk';
|
|
5
|
+
import { cssColorStraight } from 'ntk/color';
|
|
6
6
|
import { hooks as a11yHooks } from '../a11y.js';
|
|
7
7
|
import { codePointAtOffset, codeUnitOffsets } from '../textrange.js';
|
|
8
8
|
import { NO_DAMAGE } from './damage.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// What a window can paint: compositing and ARGB transparency, the window
|
|
2
2
|
// background, and the capabilities `@supports` blocks read.
|
|
3
3
|
|
|
4
|
-
import { cssColorStraight } from 'ntk';
|
|
4
|
+
import { cssColorStraight } from 'ntk/color';
|
|
5
5
|
import {
|
|
6
6
|
argbVisual,
|
|
7
7
|
compositingActive,
|
|
@@ -6,7 +6,7 @@ import { resolveFramePolicy } from '../../pacing.js';
|
|
|
6
6
|
import { paintCacheFor } from '../../paintcache.js';
|
|
7
7
|
import { hooks as traceHooks } from '../../trace-registry.js';
|
|
8
8
|
import { now } from '../animation.js';
|
|
9
|
-
import { FULL_DAMAGE, layoutDiff
|
|
9
|
+
import { FULL_DAMAGE, layoutDiff } from '../damage.js';
|
|
10
10
|
import { BLIT_POISONED } from '../scrollblit.js';
|
|
11
11
|
import { debugPaint } from './debugpaint.js';
|
|
12
12
|
|
|
@@ -204,12 +204,10 @@ export class WindowFlush {
|
|
|
204
204
|
(panes && this._paneDamage !== FULL_DAMAGE)
|
|
205
205
|
) {
|
|
206
206
|
const cap = this._damageRectCap();
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
if (panes) this._addPaneDamage(rect);
|
|
207
|
+
// each to the lists that paint the node that moved
|
|
208
|
+
// (`_claimLayoutMove`): a move inside a `<glarea>` is its panes'
|
|
209
|
+
layoutDiff.sink = (rect, node) => {
|
|
210
|
+
if (this._claimLayoutMove(rect, node, cap)) layoutMoved = true;
|
|
213
211
|
};
|
|
214
212
|
}
|
|
215
213
|
try {
|
|
@@ -259,14 +257,8 @@ export class WindowFlush {
|
|
|
259
257
|
if (sv && !sv._recordBlitClaim(after)) {
|
|
260
258
|
sv._pendingBlitFrom = BLIT_POISONED;
|
|
261
259
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
this._damage,
|
|
265
|
-
after,
|
|
266
|
-
this._damageRectCap(),
|
|
267
|
-
);
|
|
268
|
-
}
|
|
269
|
-
if (panes) this._addPaneDamage(after);
|
|
260
|
+
// to the lists that paint it, as the layout diff's claims go
|
|
261
|
+
this._claimLayoutMove(after, node, this._damageRectCap());
|
|
270
262
|
}
|
|
271
263
|
this._reflowed.clear();
|
|
272
264
|
} else if (this._reflowed.size) {
|
package/src/ntk.js
CHANGED
|
@@ -22,43 +22,20 @@
|
|
|
22
22
|
// `@react-x11/components` (`<Markdown>`, `<Formula>`). `SvgView` is still
|
|
23
23
|
// here; a drawing is not a document.
|
|
24
24
|
//
|
|
25
|
-
// One name is not a plain re-export. `Surface`
|
|
25
|
+
// One name is not a plain re-export. `Surface` (src/offscreen.js) asks the app it is
|
|
26
26
|
// handed for the implementation, because ntk's own is a pixmap and a
|
|
27
27
|
// Picture — an X connection's — and a component allocates its buffer
|
|
28
28
|
// without knowing which backend it was mounted on. This subpath is where a
|
|
29
29
|
// drawing-adjacent name gets its backend-neutral answer; the X-only names
|
|
30
30
|
// (`createClient`, `Pixmap`, `Picture`, `XEmbedSocket`) stay X-only.
|
|
31
|
-
import
|
|
31
|
+
import * as ntk from 'ntk';
|
|
32
|
+
|
|
33
|
+
import { adoptNtk } from './ntkroot.js';
|
|
32
34
|
|
|
33
35
|
export * from 'ntk';
|
|
34
36
|
export { default } from 'ntk';
|
|
37
|
+
export { Surface } from './offscreen.js';
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* An app that makes its own surfaces answers `createSurface(options)` —
|
|
40
|
-
* the Cocoa app does, over a CG bitmap (src/cocoa/surface.js), the Wayland
|
|
41
|
-
* app over a GL render target (src/wayland/surface.js) — and an ntk
|
|
42
|
-
* connection has no such method and gets ntk's pixmap. The result is
|
|
43
|
-
* whichever implementation answered, not an instance of this class: the
|
|
44
|
-
* contract is the shape — `width`/`height`, `getContext('2d')`, `render`,
|
|
45
|
-
* `clear`, `copyWithin`, `destroy`, and `ctx.drawImage(surface, …)` —
|
|
46
|
-
* (docs/extending.md "Scrolling the pixels, not just the offset"), and
|
|
47
|
-
* nothing needs `instanceof`.
|
|
48
|
-
*
|
|
49
|
-
* Part of that shape is that a context may be **held**: ntk tells a caller
|
|
50
|
-
* doing many draws to take `getContext('2d')` once rather than one per
|
|
51
|
-
* frame, and that holds on every backend here, including the ones where a
|
|
52
|
-
* surface is a GPU target sharing a device with the window (#566). Draws
|
|
53
|
-
* through a held context land in the surface whenever they are made, and
|
|
54
|
-
* leave nothing for the caller to restore; `render(fn)` is the scoped form,
|
|
55
|
-
* for a caller that wants a frame's clean transform and clip.
|
|
56
|
-
*/
|
|
57
|
-
export class Surface {
|
|
58
|
-
constructor(app, options) {
|
|
59
|
-
if (typeof app?.createSurface === 'function') {
|
|
60
|
-
return app.createSurface(options);
|
|
61
|
-
}
|
|
62
|
-
return new NtkSurface(app, options);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
39
|
+
// what src/ntkroot.js would load, already loaded: handed over rather than
|
|
40
|
+
// imported twice
|
|
41
|
+
adoptNtk(ntk);
|
package/src/ntkroot.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ntk's package root — the X11 client, ntk's windows and their contexts, the
|
|
2
|
+
// GL rungs, fontconfig and the rest of what the X11 backend draws with —
|
|
3
|
+
// loaded where an X connection is made rather than wherever react-x11 is
|
|
4
|
+
// imported.
|
|
5
|
+
//
|
|
6
|
+
// Only that backend needs it. The Cocoa, Windows and Wayland backends use a
|
|
7
|
+
// colour parser, the shadow arithmetic, the image and SVG decoders and the
|
|
8
|
+
// font parser of ntk's, each from a subpath of its own (`ntk/color`,
|
|
9
|
+
// `ntk/shadow-math`, `ntk/image`, `ntk/svg`, `ntk/font`, …), and importing
|
|
10
|
+
// the root for them was ~210 ms of module loading on Windows before the first
|
|
11
|
+
// window of an app that never opens an X connection.
|
|
12
|
+
//
|
|
13
|
+
// `createRoot` loads it on the X11 path, before the connection, and for an
|
|
14
|
+
// ntk app it is handed rather than makes — an app that could only have come
|
|
15
|
+
// from ntk's own `createClient`, so the import is already done and this is a
|
|
16
|
+
// lookup. `react-x11/ntk` hands over the root it imports. After that, the
|
|
17
|
+
// code that only ever runs against an X connection reads it synchronously.
|
|
18
|
+
let root = null;
|
|
19
|
+
let loading = null;
|
|
20
|
+
|
|
21
|
+
/** Load ntk's root, once; resolves to the module. */
|
|
22
|
+
export function loadNtk() {
|
|
23
|
+
return (loading ??= import('ntk').then((mod) => (root = mod)));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A root imported some other way (`react-x11/ntk`): the same module, so
|
|
27
|
+
* nothing is loaded twice by taking it. */
|
|
28
|
+
export function adoptNtk(mod) {
|
|
29
|
+
root ??= mod;
|
|
30
|
+
loading ??= Promise.resolve(mod);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** ntk's root, or null while no X connection has been made. */
|
|
34
|
+
export function ntkRoot() {
|
|
35
|
+
return root;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* ntk's root, for code that only runs against an X connection — which
|
|
40
|
+
* `createRoot` has loaded it for. Throws where it is not: reaching for an X
|
|
41
|
+
* resource on a backend that has none is the mistake, and this says so
|
|
42
|
+
* rather than failing inside ntk on an app that is not its own.
|
|
43
|
+
*/
|
|
44
|
+
export function x11Ntk(what) {
|
|
45
|
+
if (root) return root;
|
|
46
|
+
throw new Error(
|
|
47
|
+
`react-x11: ${what} needs an X connection, and this process has none ` +
|
|
48
|
+
'(ntk loads with the X11 backend, in createRoot).',
|
|
49
|
+
);
|
|
50
|
+
}
|
package/src/offscreen.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// An offscreen surface on whichever backend an app is — what the paint cache,
|
|
2
|
+
// a group's opacity and the symbol icons draw into, and what `react-x11/ntk`
|
|
3
|
+
// hands a component as `Surface`.
|
|
4
|
+
//
|
|
5
|
+
// An app that makes its own surfaces answers `createSurface(options)` — the
|
|
6
|
+
// Cocoa app over a CG bitmap (src/cocoa/surface.js), the Windows app over a
|
|
7
|
+
// Direct2D bitmap, the Wayland app over a GL render target
|
|
8
|
+
// (src/wayland/surface.js) — and an X connection has no such method and gets
|
|
9
|
+
// ntk's pixmap, from ntk's root, which only an X connection loads
|
|
10
|
+
// (src/ntkroot.js). The result is whichever implementation answered, not an
|
|
11
|
+
// instance of this class: the contract is the shape — `width`/`height`,
|
|
12
|
+
// `getContext('2d')`, `render`, `clear`, `copyWithin`, `destroy`, and
|
|
13
|
+
// `ctx.drawImage(surface, …)` — (docs/extending.md "Scrolling the pixels, not
|
|
14
|
+
// just the offset"), and nothing needs `instanceof`.
|
|
15
|
+
//
|
|
16
|
+
// Part of that shape is that a context may be **held**: ntk tells a caller
|
|
17
|
+
// doing many draws to take `getContext('2d')` once rather than one per frame,
|
|
18
|
+
// and that holds on every backend here, including the ones where a surface is
|
|
19
|
+
// a GPU target sharing a device with the window (#566). Draws through a held
|
|
20
|
+
// context land in the surface whenever they are made, and leave nothing for
|
|
21
|
+
// the caller to restore; `render(fn)` is the scoped form, for a caller that
|
|
22
|
+
// wants a frame's clean transform and clip.
|
|
23
|
+
import { x11Ntk } from './ntkroot.js';
|
|
24
|
+
|
|
25
|
+
export class Surface {
|
|
26
|
+
constructor(app, options) {
|
|
27
|
+
if (typeof app?.createSurface === 'function') {
|
|
28
|
+
return app.createSurface(options);
|
|
29
|
+
}
|
|
30
|
+
const { Surface: NtkSurface } = x11Ntk('an offscreen surface');
|
|
31
|
+
return new NtkSurface(app, options);
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/paintcache.js
CHANGED
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
// this package pins, and a named import of something absent is a *load-time*
|
|
43
43
|
// SyntaxError — the whole renderer, not just the cache. Same shape as the
|
|
44
44
|
// `typeof wnd?.scrollRegion !== 'function'` guard for ntk without #139.
|
|
45
|
-
import * as ntk from 'ntk';
|
|
46
45
|
|
|
47
46
|
// The surface this cache draws into is `react-x11/ntk`'s: ntk's pixmap on
|
|
48
47
|
// an X connection, the app's own (`app.createSurface`) on a backend that
|
|
49
48
|
// makes them — the Cocoa backend's CG bitmap. One import, so the cache
|
|
50
49
|
// names neither.
|
|
51
|
-
import {
|
|
50
|
+
import { ntkRoot } from './ntkroot.js';
|
|
51
|
+
import { Surface } from './offscreen.js';
|
|
52
52
|
|
|
53
53
|
/** Stale pixels are undebuggable, and every other optimization here has an
|
|
54
54
|
* escape hatch — see NO_SCROLL_BLIT. */
|
|
@@ -415,15 +415,19 @@ export class PaintCache {
|
|
|
415
415
|
}
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
-
/** Whether the installed ntk can make the surfaces this needs
|
|
419
|
-
*
|
|
420
|
-
|
|
418
|
+
/** Whether the installed ntk can make the surfaces this needs on an X
|
|
419
|
+
* connection — ntk's root, which only an X connection loads
|
|
420
|
+
* (src/ntkroot.js). Older ones cannot, and the answer is simply that nothing
|
|
421
|
+
* is cached there. */
|
|
422
|
+
export const paintCacheSupported = () =>
|
|
423
|
+
typeof ntkRoot()?.Surface === 'function';
|
|
421
424
|
|
|
422
425
|
/** Whether `app` can make an offscreen surface at all: an X connection
|
|
423
426
|
* with the Render extension (ntk's pixmap surface), or a backend with a
|
|
424
427
|
* surface of its own — the Cocoa app's `createSurface` (docs/macos.md). */
|
|
425
428
|
const canMakeSurfaces = (app) =>
|
|
426
|
-
Boolean(app?.display?.Render)
|
|
429
|
+
(Boolean(app?.display?.Render) && paintCacheSupported()) ||
|
|
430
|
+
typeof app?.createSurface === 'function';
|
|
427
431
|
|
|
428
432
|
/**
|
|
429
433
|
* The cache for an app, created on first use. Null when caching is off, when
|
|
@@ -432,6 +436,6 @@ const canMakeSurfaces = (app) =>
|
|
|
432
436
|
* there must paint live.
|
|
433
437
|
*/
|
|
434
438
|
export function paintCacheFor(app) {
|
|
435
|
-
if (DISABLED || !
|
|
439
|
+
if (DISABLED || !canMakeSurfaces(app)) return null;
|
|
436
440
|
return (app._paintCache ??= new PaintCache(app));
|
|
437
441
|
}
|
package/src/refresh/loader.js
CHANGED
|
@@ -326,6 +326,28 @@ function buildTransformer({ babel, jsxTransform, refreshTransform }, options) {
|
|
|
326
326
|
|
|
327
327
|
let registered = false;
|
|
328
328
|
|
|
329
|
+
/**
|
|
330
|
+
* Which files stay out of the hot graph: every package in node_modules,
|
|
331
|
+
* react-x11's own src/, and whatever the app's `ignore` names.
|
|
332
|
+
*
|
|
333
|
+
* Compared with forward slashes, whichever the path arrives with: on
|
|
334
|
+
* Windows a file path's separators are backslashes, and a forward-slash
|
|
335
|
+
* test let every package in node_modules into the hot graph — where the
|
|
336
|
+
* HMR layer's rewrite of a package's modules could leave one reading an
|
|
337
|
+
* import before it was bound (entities, under htmlparser2).
|
|
338
|
+
*/
|
|
339
|
+
export function hotIgnoreFor(ignore, srcDir = SRC_DIR) {
|
|
340
|
+
const src = srcDir.replaceAll('\\', '/');
|
|
341
|
+
return (path) => {
|
|
342
|
+
const slashed = path.replaceAll('\\', '/');
|
|
343
|
+
return (
|
|
344
|
+
slashed.includes('/node_modules/') ||
|
|
345
|
+
slashed.startsWith(src) ||
|
|
346
|
+
(ignore ? ignore(path) === true : false)
|
|
347
|
+
);
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
329
351
|
/**
|
|
330
352
|
* Register the hot-reload loader. `react-x11/refresh/register` calls this
|
|
331
353
|
* with no options; a tool that needs the seams writes its own two-line
|
|
@@ -362,10 +384,7 @@ export async function registerRefresh(options = {}) {
|
|
|
362
384
|
const { ignore } = validated;
|
|
363
385
|
registered = true;
|
|
364
386
|
|
|
365
|
-
const hotIgnore = (
|
|
366
|
-
path.includes('/node_modules/') ||
|
|
367
|
-
path.startsWith(SRC_DIR) ||
|
|
368
|
-
(ignore ? ignore(path) === true : false);
|
|
387
|
+
const hotIgnore = hotIgnoreFor(ignore);
|
|
369
388
|
|
|
370
389
|
nodeModule.registerHooks({
|
|
371
390
|
load(url, context, nextLoad) {
|
package/src/screencolor.js
CHANGED
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
// and losing the grab to someone else is detected (the server says so, with
|
|
57
57
|
// a LeaveNotify of mode Ungrab) rather than waited out.
|
|
58
58
|
|
|
59
|
-
import { pixelLayout, toStraightRgba } from 'ntk';
|
|
59
|
+
import { pixelLayout, toStraightRgba } from 'ntk/imagedata';
|
|
60
60
|
|
|
61
61
|
import { sessionBus } from './bus.js';
|
|
62
62
|
import { XK_ESCAPE, XK_KP_ENTER, XK_RETURN, XK_SPACE } from './keysyms.js';
|
package/src/styles.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Numbers are pixels; strings like '50%' / 'auto' pass through to yoga.
|
|
4
4
|
// The engine is ours (`./yoga.js`) — the enum tables below are built at
|
|
5
5
|
// module scope, which is what that module's synchronous half is for.
|
|
6
|
-
import { cssColorStraight } from 'ntk';
|
|
6
|
+
import { cssColorStraight } from 'ntk/color';
|
|
7
7
|
|
|
8
8
|
import { parseBoxShadow, parseLinearGradient } from './decorations.js';
|
|
9
9
|
import {
|
package/src/svgnodes.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// `<Formula>`), composed from public host elements. `<svg>` stays here
|
|
11
11
|
// because it is not a document — it is a drawing, and one node is the right
|
|
12
12
|
// grain for it.
|
|
13
|
-
import
|
|
13
|
+
import SvgView from 'ntk/svg';
|
|
14
14
|
|
|
15
15
|
import { Node } from './nodes/node.js';
|
|
16
16
|
import { intrinsicSize } from './nodes/layout.js';
|
package/src/symbols.js
CHANGED
|
@@ -17,11 +17,13 @@
|
|
|
17
17
|
// the same bargain a tray icon's name makes — so an app that runs on both
|
|
18
18
|
// picks its names by platform, as it does for the tray.
|
|
19
19
|
|
|
20
|
-
import { cssColorStraight
|
|
20
|
+
import { cssColorStraight } from 'ntk/color';
|
|
21
|
+
import { decodeImage } from 'ntk/image';
|
|
22
|
+
import SvgView from 'ntk/svg';
|
|
21
23
|
import * as nodeFs from 'node:fs';
|
|
22
24
|
|
|
23
25
|
import { IconTheme } from './icontheme.js';
|
|
24
|
-
import { Surface } from './
|
|
26
|
+
import { Surface } from './offscreen.js';
|
|
25
27
|
import { xsettings } from './xsettings.js';
|
|
26
28
|
|
|
27
29
|
/** A `fontWeight` as the number both icon systems configure with. */
|
package/src/trayhooks.js
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
// frame is not available for anything that has to ask the desktop.
|
|
55
55
|
|
|
56
56
|
import { useEffect, useRef, useState } from 'react';
|
|
57
|
-
import { decodeImage } from 'ntk';
|
|
57
|
+
import { decodeImage } from 'ntk/image';
|
|
58
58
|
|
|
59
59
|
import { useAppOrNull } from './appcontext.js';
|
|
60
60
|
import { currentRegistration } from './application.js';
|
package/src/wayland/context2d.js
CHANGED
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
import { createRequire } from 'node:module';
|
|
43
43
|
import path from 'node:path';
|
|
44
44
|
import { pathToFileURL } from 'node:url';
|
|
45
|
-
import {
|
|
45
|
+
import { Image as NtkImage } from 'ntk/image';
|
|
46
|
+
import { Path2D } from 'ntk/path';
|
|
46
47
|
import { glDevice } from './device.js';
|
|
47
48
|
import { GlyphAtlas } from './glyphatlas.js';
|
|
48
49
|
import { GLTarget } from './target.js';
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
// buttons do what they say. input.js routes; this answers where things are.
|
|
33
33
|
|
|
34
34
|
import fs from 'node:fs';
|
|
35
|
-
import { Path2D } from 'ntk';
|
|
35
|
+
import { Path2D } from 'ntk/path';
|
|
36
36
|
import { RESIZE_EDGE, TOPLEVEL_STATE } from './window.js';
|
|
37
37
|
import { DEFAULTS, parseButtonLayout, titleFont } from './framestyle.js';
|
|
38
38
|
|
package/src/win32/fonts.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// `indexAt()` returns code points, which is what the selection and the caret
|
|
16
16
|
// speak. DirectWrite is UTF-16 end to end, like CoreText, so the conversion
|
|
17
17
|
// happens at this boundary and nowhere else.
|
|
18
|
-
import { cssColorStraight } from 'ntk';
|
|
18
|
+
import { cssColorStraight } from 'ntk/color';
|
|
19
19
|
|
|
20
20
|
// REACT_X11_WIN32_DEBUG=1 reports the size each paragraph is shaped at, which
|
|
21
21
|
// is how to tell a scale that never reached the text engine from one that did.
|