react-x11 2.52.0 → 2.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/docs/README.md +160 -314
- package/docs/accessibility.md +176 -229
- package/docs/appearance.md +77 -176
- package/docs/click-to-component.md +18 -51
- package/docs/clipboard.md +40 -59
- package/docs/components.md +518 -1036
- package/docs/dbus.md +61 -133
- package/docs/debugging.md +152 -245
- package/docs/desktop-calendar.md +51 -88
- package/docs/desktop.md +222 -347
- package/docs/devtools.md +50 -167
- package/docs/drag-and-drop.md +126 -204
- package/docs/ecosystem/animation.md +91 -125
- package/docs/ecosystem/data-fetching.md +38 -53
- package/docs/ecosystem/dev-tooling.md +159 -202
- package/docs/ecosystem/forms.md +61 -97
- package/docs/ecosystem/headless.md +64 -89
- package/docs/ecosystem/i18n.md +18 -21
- package/docs/ecosystem/icons.md +93 -126
- package/docs/ecosystem/layout.md +42 -52
- package/docs/ecosystem/routing.md +18 -29
- package/docs/ecosystem/state.md +71 -88
- package/docs/ecosystem/testing.md +131 -157
- package/docs/ecosystem/theming.md +119 -165
- package/docs/ecosystem.md +145 -179
- package/docs/elements.md +1091 -1774
- package/docs/embedding.md +83 -132
- package/docs/events.md +412 -695
- package/docs/extending.md +784 -1326
- package/docs/eyedropper.md +63 -122
- package/docs/filedialog.md +71 -107
- package/docs/frame.md +195 -339
- package/docs/gl.md +65 -62
- package/docs/globalmenu.md +63 -146
- package/docs/macos.md +531 -3234
- package/docs/notifications.md +33 -47
- package/docs/packaging.md +166 -309
- package/docs/permissions.md +9 -14
- package/docs/react-features.md +81 -106
- package/docs/remote.md +32 -76
- package/docs/scale.md +72 -186
- package/docs/styling.md +329 -547
- package/docs/system.md +200 -280
- package/docs/testing.md +63 -83
- package/docs/typescript.md +6 -48
- package/docs/uri-schemes.md +67 -107
- package/docs/wayland-backend.md +200 -459
- package/docs/windows-integrations.md +75 -131
- package/docs/windows.md +343 -1367
- package/package.json +3 -2
- package/src/Reconciler.js +2 -2
- package/src/a11y.js +1 -1
- package/src/backend/context2d.js +3 -3
- package/src/bootstrap.js +1 -1
- package/src/cocoa/app.js +6 -6
- package/src/cocoa/bezels.js +1 -1
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/globalmenu.js +1 -1
- package/src/cocoa/keymap.js +1 -1
- package/src/cocoa/panewindow.js +1 -1
- package/src/cocoa/player.js +1 -1
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/promotion.js +1 -1
- package/src/cocoa/relaunch.js +1 -1
- package/src/cocoa/threaded.js +1 -1
- package/src/cocoa/video.js +1 -1
- package/src/cocoa/window.js +4 -4
- package/src/components/Menu.js +7 -2
- package/src/components/Switch.js +1 -1
- package/src/components/native.js +1 -1
- package/src/dnd.js +2 -2
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/layouts.js +1 -1
- package/src/nodes/animation.js +1 -1
- package/src/nodes/invalidate.js +1 -1
- package/src/nodes/layouthost.js +1 -1
- package/src/nodes/video.js +19 -8
- package/src/nodes/window/droptarget.js +1 -1
- package/src/pacing.js +1 -1
- package/src/refresh/index.js +2 -1
- package/src/types/video.d.ts +5 -2
- package/src/videobackend.js +28 -0
- package/src/videoframes.js +29 -16
- package/src/wayland/app.js +27 -0
- package/src/wayland/video.js +376 -0
- package/src/win32/a11y.js +1 -1
- package/src/win32/app.js +2 -2
- package/src/win32/fonts.js +1 -1
- package/src/win32/glarea.js +4 -4
- package/src/win32/overlay.js +1 -1
- package/src/win32/panewindow.js +3 -3
- package/src/win32/shell.js +1 -1
- package/src/win32/window.js +2 -2
- package/src/windowid.js +1 -1
- package/src/xvideo.js +189 -0
- package/docs/architecture/accessibility.md +0 -470
- package/docs/architecture/animation.md +0 -707
- package/docs/architecture/container-queries.md +0 -525
- package/docs/architecture/custom-layout.md +0 -334
- package/docs/architecture/drag-and-drop.md +0 -941
- package/docs/architecture/element-layer-contents.md +0 -622
- package/docs/architecture/frame-pacing.md +0 -255
- package/docs/architecture/grid-layout.md +0 -358
- package/docs/architecture/protocol-efficiency.md +0 -357
- package/docs/architecture/video.md +0 -1149
- package/docs/architecture/windowed-regions.md +0 -854
- package/docs/glx.md +0 -137
- package/docs/wayland.md +0 -545
- package/docs/windows-embedding.md +0 -271
- package/docs/windows-gl.md +0 -221
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-x11",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.53.0",
|
|
4
4
|
"description": "react renderer with X11 as a target",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -72,6 +72,7 @@
|
|
|
72
72
|
"labs:direct-gl": "tsx examples/labs/direct-gl.jsx",
|
|
73
73
|
"labs:gl-overlay": "tsx examples/labs/gl-overlay.jsx",
|
|
74
74
|
"examples:viewer3d": "tsx examples/viewer3d.jsx",
|
|
75
|
+
"examples:video": "tsx examples/video.jsx",
|
|
75
76
|
"examples:transparent": "tsx examples/transparent.jsx",
|
|
76
77
|
"examples:windows": "tsx examples/windows.jsx",
|
|
77
78
|
"examples:wm": "tsx examples/wm.jsx",
|
|
@@ -110,7 +111,7 @@
|
|
|
110
111
|
"ical.js": "^2.2.1",
|
|
111
112
|
"image-in-browser": "^3.6.0",
|
|
112
113
|
"linebreak": "^1.1.0",
|
|
113
|
-
"ntk": "^8.
|
|
114
|
+
"ntk": "^8.24.0",
|
|
114
115
|
"react-reconciler": "^0.34.0",
|
|
115
116
|
"yoga-layout": "^3.2.1"
|
|
116
117
|
},
|
package/src/Reconciler.js
CHANGED
|
@@ -678,7 +678,7 @@ const isNtkApp = (v) =>
|
|
|
678
678
|
*
|
|
679
679
|
* `'auto'` — the default — is one rule: macOS gets the native Core
|
|
680
680
|
* Animation backend, everything else gets X11 via `$DISPLAY`. (More rungs —
|
|
681
|
-
* Wayland — join this ladder, not a new one;
|
|
681
|
+
* Wayland — join this ladder, not a new one; design/wayland.md.) On a mac
|
|
682
682
|
* without the `@windowkit/appkit` bridge installed, auto falls back to X11 so
|
|
683
683
|
* an XQuartz setup keeps working; asking for `'cocoa'` by name means the
|
|
684
684
|
* bridge is required, and its absence is an error that says how to fix it.
|
|
@@ -809,7 +809,7 @@ export async function createRoot(options = {}) {
|
|
|
809
809
|
// The cocoa backend's desktop is not freedesktop's: the AT-SPI bridge and
|
|
810
810
|
// the D-Bus global menu have nothing to register against there, so they
|
|
811
811
|
// default off (their macOS replacements are their own workstream —
|
|
812
|
-
//
|
|
812
|
+
// design/macos.md). The appearance ladder stays on: its macOS rung is the
|
|
813
813
|
// one that answers. An explicit `desktop` option still wins.
|
|
814
814
|
setDesktopIntegration(
|
|
815
815
|
rest.desktop ??
|
package/src/a11y.js
CHANGED
|
@@ -1289,7 +1289,7 @@ export function startA11y(app) {
|
|
|
1289
1289
|
// presence *is* the capability — the same rule the rest of the library
|
|
1290
1290
|
// uses. The Windows backend installs one, because UI Automation asks
|
|
1291
1291
|
// synchronously and has to be answered from a pushed copy rather than from
|
|
1292
|
-
// the live tree (
|
|
1292
|
+
// the live tree (design/windows.md §Accessibility); AT-SPI's asynchronous
|
|
1293
1293
|
// questions are answered from the tree itself, below.
|
|
1294
1294
|
if (typeof app?.startAccessibility === 'function') {
|
|
1295
1295
|
startPromise = Promise.resolve()
|
package/src/backend/context2d.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// A canvas-shaped 2d context over a **verb table** — the drawing dialect
|
|
2
2
|
// every native backend answers, and the one place it is written down.
|
|
3
3
|
//
|
|
4
|
-
// This is macos.md §"The split" step 4, taken when a second native backend
|
|
4
|
+
// This is design/macos.md §"The split" step 4, taken when a second native backend
|
|
5
5
|
// started: the class takes its `native` as a constructor argument and calls
|
|
6
6
|
// nothing else, so one wrapper drives @windowkit/appkit's CoreGraphics verbs
|
|
7
7
|
// and @windowkit/win32's Direct2D verbs alike. A bridge that lacks an
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
//
|
|
17
17
|
// On the surface presenter this is the whole drawing path; on the layer
|
|
18
18
|
// presenter it stays as the fallback every painted-code node (<canvas>,
|
|
19
|
-
// <svg>, registered elements) rasters through —
|
|
19
|
+
// <svg>, registered elements) rasters through — design/macos.md §"Custom
|
|
20
20
|
// drawing on a layer tree".
|
|
21
21
|
//
|
|
22
22
|
// The native surface holds the real graphics state (paths, CTM, clip); this
|
|
@@ -322,7 +322,7 @@ const NON_RECT = Symbol('non-rectangular clip');
|
|
|
322
322
|
* linear in it: 14ms, 29ms, 56ms, 113ms. The driver is the subpath count,
|
|
323
323
|
* not the vertex count — one 26,000-vertex subpath strokes in 4ms where
|
|
324
324
|
* two thousand 13-vertex ones take 208. The full table, the two shapes
|
|
325
|
-
* left whole and why, are in
|
|
325
|
+
* left whole and why, are in design/macos.md
|
|
326
326
|
* §"Stroking a path with many subpaths".
|
|
327
327
|
*
|
|
328
328
|
* Note the X11 context wants the opposite — there a stroke is an a8
|
package/src/bootstrap.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// an app on macOS moves onto a worker (src/cocoa/relaunch.js), and on that
|
|
5
5
|
// worker the same import sets it up. A Windows backend's UI thread will be
|
|
6
6
|
// the addon's own, and X11 has none, so neither needs this step
|
|
7
|
-
// (
|
|
7
|
+
// (design/windows.md §"Three shapes on Windows"). Imported first by
|
|
8
8
|
// src/index.js, so that it runs before the rest of the package.
|
|
9
9
|
import { isMainThread, workerData } from 'node:worker_threads';
|
|
10
10
|
|
package/src/cocoa/app.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// that stands in for the X socket.
|
|
6
6
|
//
|
|
7
7
|
// Two run loops. By default Node's loop is master and a timer pumps AppKit
|
|
8
|
-
// (
|
|
8
|
+
// (design/macos.md §"Input and the run loop", option 1): input waits for the
|
|
9
9
|
// pump, and AppKit's modal loops (live resize, menu tracking, a drag) stall
|
|
10
10
|
// JS timers — but not the delegate callbacks, which is why live resize
|
|
11
11
|
// still relayouts: the resize event flushes the frame synchronously on its
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// Under the launcher (`node --import react-x11/cocoa-main`, src/cocoa/
|
|
14
14
|
// main.js) the app runs on a worker instead, AppKit keeps the main thread
|
|
15
15
|
// in a real `[NSApp run]`, and this object is fed by the bridge's batches
|
|
16
|
-
// (`_routeBatch`) with no pump at all (
|
|
16
|
+
// (`_routeBatch`) with no pump at all (design/macos.md §"JS on a worker").
|
|
17
17
|
import { isMainThread } from 'node:worker_threads';
|
|
18
18
|
|
|
19
19
|
import { cssColorStraight } from 'ntk/color';
|
|
@@ -64,11 +64,11 @@ const PUMP_INTERVAL_MS = 8;
|
|
|
64
64
|
// The edge moves when the delegate returns, so the budget is how long a
|
|
65
65
|
// slow frame may hold the drag back. 50 covers what a large tree costs —
|
|
66
66
|
// the Cocoa `resize` cell measures 28ms a tick at 3,662 nodes
|
|
67
|
-
// (
|
|
67
|
+
// (design/macos.md) — with room, where the bridge's own test met every tick
|
|
68
68
|
// in 3.1ms against 3ms of layout. Past three 60Hz frames an edge that waits
|
|
69
69
|
// reads as the drag stuttering, so a tree slower than that lets the edge
|
|
70
70
|
// go first and its frame land after (Flutter's Windows embedder waits up
|
|
71
|
-
// to 100,
|
|
71
|
+
// to 100, design/windows.md §"Resize: where the threads meet"). `createRoot({ cocoa: { resizeWait } })`
|
|
72
72
|
// sets it; 0 is no handshake at all.
|
|
73
73
|
const RESIZE_WAIT_MS = 50;
|
|
74
74
|
// What `createRoot({ cocoa: { paneStretch } })` and
|
|
@@ -272,7 +272,7 @@ export class CocoaApp {
|
|
|
272
272
|
}
|
|
273
273
|
|
|
274
274
|
// 'surface' (the measured default) or 'layers' — the retained CALayer
|
|
275
|
-
// presenter, opt-in while
|
|
275
|
+
// presenter, opt-in while design/macos.md's measure-first gate is open.
|
|
276
276
|
this._presenterMode =
|
|
277
277
|
options.cocoa?.presenter ??
|
|
278
278
|
process.env.REACT_X11_COCOA_PRESENTER ??
|
|
@@ -391,7 +391,7 @@ export class CocoaApp {
|
|
|
391
391
|
|
|
392
392
|
// the global-menu exports, newest active: the macOS menu bar is one per
|
|
393
393
|
// app, so the most recent MenuBar owns it (per-focused-window switching
|
|
394
|
-
// is the follow-up —
|
|
394
|
+
// is the follow-up — design/macos.md §Menus)
|
|
395
395
|
this._globalMenus = [];
|
|
396
396
|
this._activeGlobalMenu = null;
|
|
397
397
|
|
package/src/cocoa/bezels.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Native control bezels — AppKit's own pixels for the core controls, cached
|
|
2
|
-
// as surfaces the ordinary 2d paint path can blit (
|
|
2
|
+
// as surfaces the ordinary 2d paint path can blit (design/macos.md §Native
|
|
3
3
|
// controls). The mechanism is offscreen NSCell/NSControl rendering, the
|
|
4
4
|
// WebKit/Gecko form-control technique: interaction, focus and keyboard stay
|
|
5
5
|
// the shared component implementation, and only the *bezel* — the pixels of
|
package/src/cocoa/fonts.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The Cocoa text engine: CoreText behind the same `app.fonts` contract ntk's
|
|
2
2
|
// FontManager answers on the X11 backend. The renderer touches exactly this
|
|
3
|
-
// surface (
|
|
3
|
+
// surface (design/macos.md §"Text: the engine contract"):
|
|
4
4
|
//
|
|
5
5
|
// fonts.layout(spans, base, { maxWidth, align, lineHeight, maxLines,
|
|
6
6
|
// overflow, direction })
|
package/src/cocoa/globalmenu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The macOS menu bar as a global-menu export (
|
|
1
|
+
// The macOS menu bar as a global-menu export (design/macos.md §Menus).
|
|
2
2
|
//
|
|
3
3
|
// The Linux global menu was built for exactly this moment: the item
|
|
4
4
|
// vocabulary is data, and the pure snapshot/IdAllocator machinery in
|
package/src/cocoa/keymap.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
//
|
|
24
24
|
// There is no input method behind this. The bridge's view swallows
|
|
25
25
|
// `keyDown:` without `interpretKeyEvents:` and is no `NSTextInputClient`
|
|
26
|
-
// (
|
|
26
|
+
// (design/macos.md, "IME"), so these fields are all the text there is. Two
|
|
27
27
|
// consequences, both handled below:
|
|
28
28
|
//
|
|
29
29
|
// - **A dead key types nothing.** ⌥E on a US layout leaves `chars` empty, and
|
package/src/cocoa/panewindow.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The pane process's "window" on the Cocoa backend —
|
|
1
|
+
// The pane process's "window" on the Cocoa backend — design/frame.md's child
|
|
2
2
|
// half, with the X server replaced by shared memory.
|
|
3
3
|
//
|
|
4
4
|
// On X11 a pane renders into a real window of its own and the host embeds
|
package/src/cocoa/player.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// `<video src>` on macOS (
|
|
1
|
+
// `<video src>` on macOS (design/architecture/video.md §3.2, §9): a file or URL
|
|
2
2
|
// played by AVFoundation through @windowkit/appkit's player verbs — the
|
|
3
3
|
// platform's decoder, audio, seeking and HLS — with the picture on the
|
|
4
4
|
// player's own AVPlayerLayer.
|
package/src/cocoa/presenter.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The retained layer presenter — Tier L of
|
|
1
|
+
// The retained layer presenter — Tier L of design/macos.md: one CALayer per
|
|
2
2
|
// drawn node, React commits landing as property sets inside a single
|
|
3
3
|
// disabled-actions CATransaction per frame, the WindowServer compositing.
|
|
4
4
|
//
|
|
@@ -350,7 +350,7 @@ export function stylePaintsPlain(node, style = node.style ?? {}) {
|
|
|
350
350
|
/**
|
|
351
351
|
* Style property → the key path on a PropBox's own layer, for the
|
|
352
352
|
* animations the presenter takes off the frame clock and hands to the
|
|
353
|
-
* render server (
|
|
353
|
+
* render server (design/architecture/animation.md §4.2). Only what the layer
|
|
354
354
|
* expresses as a property of itself qualifies: a colour on `<text>` is a
|
|
355
355
|
* re-raster per frame, a layout property moves the siblings, and both stay
|
|
356
356
|
* on the JS loop. `scaled` values go out in points, as `_syncPropBox`
|
package/src/cocoa/promotion.js
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
// overlapped, hidden, clipped, faded or non-plain returns to the bitmap in
|
|
37
37
|
// the frame that finds it, the animation handed back to the clock — and a
|
|
38
38
|
// loop the clock runs is offered again once the scene would take it
|
|
39
|
-
// (`wouldTake`), where it is in its cycle.
|
|
39
|
+
// (`wouldTake`), where it is in its cycle. design/macos.md §"Layer
|
|
40
40
|
// promotion" is the account.
|
|
41
41
|
import { now } from '../nodes/animation.js';
|
|
42
42
|
import { BoxNode } from '../nodes/box.js';
|
package/src/cocoa/relaunch.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Threaded mode by default (
|
|
1
|
+
// Threaded mode by default (design/macos.md §"JS on a worker"). On macOS the
|
|
2
2
|
// process main thread is AppKit's — the only thread it lets own a window —
|
|
3
3
|
// and while the app's JS runs there, every menu, drag, live resize and
|
|
4
4
|
// modal panel stops it. So the first import of react-x11 on the main thread
|
package/src/cocoa/threaded.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Threaded mode's worker half (
|
|
1
|
+
// Threaded mode's worker half (design/macos.md §"JS on a worker: a UI thread
|
|
2
2
|
// of the bridge's own"): with the launcher (src/cocoa/main.js) holding the
|
|
3
3
|
// process main thread in the bridge's `runMain()`, the app's JS runs on a
|
|
4
4
|
// `worker_threads` Worker, and this module is what that worker needs to
|
package/src/cocoa/video.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// A video's frames on a layer of their own (
|
|
1
|
+
// A video's frames on a layer of their own (design/architecture/video.md §7):
|
|
2
2
|
// what the surface presenter's sprites (src/cocoa/sprites.js) do with a part
|
|
3
3
|
// whose `contents` is a `VideoFrames` sink instead of a `paint`.
|
|
4
4
|
//
|
package/src/cocoa/window.js
CHANGED
|
@@ -39,7 +39,7 @@ const FENCE_TIMEOUT_MS = 100;
|
|
|
39
39
|
// (`_takeBack`). Three, a `CAMetalLayer`'s depth, is what an animation
|
|
40
40
|
// mostly needs; an input answered between two of its frames puts two flips
|
|
41
41
|
// inside one refresh, and four kept every write of a clicked 120Hz animation
|
|
42
|
-
// off a held buffer (
|
|
42
|
+
// off a held buffer (design/macos.md, "Measured: the swapchain and the
|
|
43
43
|
// WindowServer's hold").
|
|
44
44
|
const MAX_BUFFERS = 4;
|
|
45
45
|
// Past this many rects, what a buffer missed is copied whole: a buffer left
|
|
@@ -235,14 +235,14 @@ export class CocoaWindow {
|
|
|
235
235
|
// change.
|
|
236
236
|
this.damageRectCap = 16;
|
|
237
237
|
|
|
238
|
-
// The retained layer presenter (
|
|
238
|
+
// The retained layer presenter (design/macos.md Tier L), behind
|
|
239
239
|
// REACT_X11_COCOA_PRESENTER=layers while the surface path is the
|
|
240
240
|
// measured default. Its hooks exist only in this mode, so the feature
|
|
241
241
|
// detection in src/nodes/ keeps the surface path byte-identical; the
|
|
242
242
|
// scroll blit is shadowed off because a layer frame has no backing
|
|
243
243
|
// bitmap to blit. The last two are the animation seam: a transition or
|
|
244
244
|
// a loop the presenter takes runs in the render server and schedules no
|
|
245
|
-
// frames here (
|
|
245
|
+
// frames here (design/architecture/animation.md §4).
|
|
246
246
|
if (app._presenterMode === 'layers') {
|
|
247
247
|
this._presenter = new CocoaLayerPresenter(this);
|
|
248
248
|
this.presentFrame = (windowNode) => this._presenter.frame(windowNode);
|
|
@@ -659,7 +659,7 @@ export class CocoaWindow {
|
|
|
659
659
|
* A new size retires the chain, released on the spot (`_releaseBacking`):
|
|
660
660
|
* left to the handles' finalizers, a forty-tick drag of the two-buffer
|
|
661
661
|
* chain this used to be held 800MB until a collection happened to run —
|
|
662
|
-
* the `rss +80MB`
|
|
662
|
+
* the `rss +80MB` design/macos.md measured. The layer keeps its own
|
|
663
663
|
* reference to whichever IOSurface it is still showing, so the free is
|
|
664
664
|
* safe while that frame is on glass. A new chain starts with the one
|
|
665
665
|
* buffer its first frame paints: a live-resize tick paints its size once,
|
package/src/components/Menu.js
CHANGED
|
@@ -984,7 +984,9 @@ function useMenuAccelerators(
|
|
|
984
984
|
*
|
|
985
985
|
* The wrapper is focusable and takes focus when the menu opens, because the
|
|
986
986
|
* popup is override-redirect and never gets focus itself — the same trick
|
|
987
|
-
* `Select` uses to keep arrow keys working while its menu is open.
|
|
987
|
+
* `Select` uses to keep arrow keys working while its menu is open. It is not
|
|
988
|
+
* a Tab stop: it has no role or name, so Tab landing on it would announce
|
|
989
|
+
* nothing, and the controls inside are stops of their own.
|
|
988
990
|
*/
|
|
989
991
|
export function ContextMenu({
|
|
990
992
|
items = [],
|
|
@@ -1087,7 +1089,10 @@ export function ContextMenu({
|
|
|
1087
1089
|
'box',
|
|
1088
1090
|
{
|
|
1089
1091
|
ref,
|
|
1090
|
-
focusable
|
|
1092
|
+
// focusable by a press and by `focus()` above, but out of the Tab
|
|
1093
|
+
// cycle: a stop here is a nameless "filler" around content whose own
|
|
1094
|
+
// controls are already reachable
|
|
1095
|
+
tabIndex: -1,
|
|
1091
1096
|
onMouseDown: (ev) => {
|
|
1092
1097
|
if (ev.button !== 3 && rect) close();
|
|
1093
1098
|
},
|
package/src/components/Switch.js
CHANGED
|
@@ -98,7 +98,7 @@ export function Switch({
|
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
// NSSwitch's own pixels, on/off as two cached bezels. The knob slide is
|
|
101
|
-
// the fidelity this loses for now (
|
|
101
|
+
// the fidelity this loses for now (design/macos.md flags the switch as the
|
|
102
102
|
// one control worth revisiting if that disappoints); the press keeps its
|
|
103
103
|
// answer as the wash, since NSSwitch renders no pressed state offscreen.
|
|
104
104
|
if (nativeControls) {
|
package/src/components/native.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The native half of the widget set (
|
|
1
|
+
// The native half of the widget set (design/macos.md §Native controls).
|
|
2
2
|
//
|
|
3
3
|
// House rule: **default native, seam out.** On a backend that renders the
|
|
4
4
|
// platform's own control bezels — today the Cocoa backend — the core
|
package/src/dnd.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// XDND drag and drop, the drop-target half: react-x11 windows accept drags
|
|
2
2
|
// from other X11 applications (file managers, editors, browsers).
|
|
3
3
|
//
|
|
4
|
-
// Design:
|
|
4
|
+
// Design: design/architecture/drag-and-drop.md; protocol: XDND version 5.
|
|
5
5
|
// The advertisement (`XdndAware`) is a 4-byte property written
|
|
6
6
|
// unconditionally on every top-level window at realize time — it creates no
|
|
7
7
|
// X windows, so it is orthogonal to the windowed/windowless split.
|
|
@@ -1080,7 +1080,7 @@ export class DropSession {
|
|
|
1080
1080
|
// ---------------------------------------------------------------------------
|
|
1081
1081
|
// The drag source.
|
|
1082
1082
|
//
|
|
1083
|
-
// One drag session, two transports (
|
|
1083
|
+
// One drag session, two transports (design/architecture/drag-and-drop.md §6.3):
|
|
1084
1084
|
// while the pointer is over this process's own windows the drag is local —
|
|
1085
1085
|
// no wire, live JS payloads, the DropSession above delivering to the same
|
|
1086
1086
|
// handlers with `e.source === 'internal'`. The moment it leaves them, the
|
package/src/glnodes.js
CHANGED
|
@@ -95,7 +95,7 @@ export function glxConfig(app, spec) {
|
|
|
95
95
|
process.platform === 'win32'
|
|
96
96
|
? 'react-x11: <glarea> is not built on the win32 backend yet — it ' +
|
|
97
97
|
'needs ANGLE (EGL and GLES over Direct3D 11), which ' +
|
|
98
|
-
'
|
|
98
|
+
'design/windows.md plans as an optional dependency the way ' +
|
|
99
99
|
'x11-dri is on X11. Everything else on this backend works ' +
|
|
100
100
|
'without it; for GL content today, use the X11 backend.'
|
|
101
101
|
: 'react-x11: <glarea> needs ntk >= 3.6.0 (app.chooseGLConfig)',
|
package/src/grid.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// CSS grid, built in (docs/styling.md "Grid"; the design record is
|
|
2
|
-
//
|
|
2
|
+
// design/architecture/grid-layout.md). A box with `display: 'grid'` — or
|
|
3
3
|
// `layout: 'grid'`, the same request under the name every layout goes by —
|
|
4
4
|
// arranges its children by CSS Grid's placement and track sizing algorithms
|
|
5
5
|
// (css-grid-2 §8.5 and §12), inside the pass that lays the window out. It is
|
package/src/layouts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Layout algorithms and positions from outside the renderer — the two seams
|
|
2
|
-
//
|
|
2
|
+
// design/architecture/custom-layout.md is the design record for.
|
|
3
3
|
//
|
|
4
4
|
// A **layout** arranges a box's children. It is asked how big the box is for
|
|
5
5
|
// the room on offer, and then where each child goes in the room the box got.
|
package/src/nodes/animation.js
CHANGED
|
@@ -310,7 +310,7 @@ export class NodeAnimation {
|
|
|
310
310
|
// loop-stop rule and `sameAnimation` all see it — but it contributes no
|
|
311
311
|
// value to the style, is skipped by the tick, and keeps the node out of the
|
|
312
312
|
// window's animating set. The X11 path has none of these hooks and is
|
|
313
|
-
// byte-identical (
|
|
313
|
+
// byte-identical (design/architecture/animation.md §4).
|
|
314
314
|
|
|
315
315
|
_offload(prop, entry) {
|
|
316
316
|
const wnd = this.root?.window;
|
package/src/nodes/invalidate.js
CHANGED
|
@@ -361,7 +361,7 @@ export class WindowInvalidate {
|
|
|
361
361
|
}
|
|
362
362
|
// A retained presenter keeps a per-node diff instead of damage rects,
|
|
363
363
|
// and this is the one channel every change already announces itself on
|
|
364
|
-
// (
|
|
364
|
+
// (design/macos.md §"One renderer, two presenters"). Feature-detected: an
|
|
365
365
|
// ntk window has no ear here and the X11 path is byte-identical. A
|
|
366
366
|
// presenter that answers `true` has taken the claim onto a layer of its
|
|
367
367
|
// own (src/cocoa/promotion.js): the bitmap owes nothing for it, and the
|
package/src/nodes/layouthost.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// in layouts.js arranges its children itself. Each child is a yoga root of
|
|
3
3
|
// its own, and the host is a measured leaf in its parent — so what the
|
|
4
4
|
// algorithm decides is on screen in the frame that asked.
|
|
5
|
-
//
|
|
5
|
+
// design/architecture/custom-layout.md is the design record.
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
createLayoutNode,
|
package/src/nodes/video.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// <video>: frames an application decodes (`frames`, a VideoFrames sink), or a
|
|
2
2
|
// file or URL the platform plays (`src`, where there is a player), fitted
|
|
3
|
-
// into the node's box (
|
|
3
|
+
// into the node's box (design/architecture/video.md §6–7).
|
|
4
4
|
//
|
|
5
5
|
// A drawn node like `<image>`: it joins yoga, it is in the paint order and it
|
|
6
6
|
// is hit like anything else — which is the whole point. A video in a form
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
// - **drawn** — everywhere else, and in every frame the presenter declines
|
|
19
19
|
// the part (something is painted over it, it fades, a rounded card does
|
|
20
20
|
// not hold it): the newest frame is copied into a surface of the node's
|
|
21
|
-
// own at paint, natively where the backend can (`
|
|
22
|
-
// through `frameToRGBA` where it cannot, and
|
|
23
|
-
// rect like an `<image>` whose pixels
|
|
21
|
+
// own at paint, natively where the backend can (`writeVideoFrame`,
|
|
22
|
+
// src/videobackend.js) and through `frameToRGBA` where it cannot, and
|
|
23
|
+
// composited at the picture's rect like an `<image>` whose pixels
|
|
24
|
+
// changed. A push claims the picture.
|
|
24
25
|
//
|
|
25
26
|
// `src` needs a platform player, which this backend has when
|
|
26
27
|
// `useSupports('mediaPlayback')` says so — the app's `createPlayer`, on macOS
|
|
@@ -41,6 +42,7 @@ import {
|
|
|
41
42
|
} from '../imagesource.js';
|
|
42
43
|
import { canPlayMedia, NoMediaPlaybackError } from '../mediaplayback.js';
|
|
43
44
|
import { Surface } from '../offscreen.js';
|
|
45
|
+
import { videoBackendOf } from '../videobackend.js';
|
|
44
46
|
import { frameToRGBA, isVideoFrames } from '../videoframes.js';
|
|
45
47
|
import { fitRect, objectFitOf } from './fit.js';
|
|
46
48
|
import { intrinsicSize } from './layout.js';
|
|
@@ -592,10 +594,18 @@ export class VideoNode extends Node {
|
|
|
592
594
|
}
|
|
593
595
|
|
|
594
596
|
_upload(surface, sink, frame) {
|
|
595
|
-
// natively where the backend converts in its own code
|
|
596
|
-
// bridge's writeVideoSurface
|
|
597
|
-
// colour-matched to what a layer shows)
|
|
598
|
-
|
|
597
|
+
// natively where the backend converts in its own code: the Cocoa
|
|
598
|
+
// bridge's writeVideoSurface (a copy for BGRA, VideoToolbox for YCbCr,
|
|
599
|
+
// colour-matched to what a layer shows), the Wayland context's shader,
|
|
600
|
+
// an Xv port on X11 (src/videobackend.js)
|
|
601
|
+
if (
|
|
602
|
+
videoBackendOf(this.app)?.writeVideoFrame?.(surface, sink, frame, this)
|
|
603
|
+
) {
|
|
604
|
+
// a frame converted in JavaScript earlier — the first, on X11, before
|
|
605
|
+
// the window was mapped — left a buffer the native path does not need
|
|
606
|
+
this._rgba = null;
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
599
609
|
const w = sink.width;
|
|
600
610
|
const h = sink.height;
|
|
601
611
|
if (this._rgba?.width !== w || this._rgba?.height !== h) {
|
|
@@ -632,6 +642,7 @@ export class VideoNode extends Node {
|
|
|
632
642
|
this._surface = null;
|
|
633
643
|
this._rgba = null;
|
|
634
644
|
this._drawn = -1;
|
|
645
|
+
if (surface) videoBackendOf(this.app)?.releaseVideoFrame?.(surface);
|
|
635
646
|
try {
|
|
636
647
|
surface?.destroy();
|
|
637
648
|
} catch {
|
package/src/pacing.js
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
// Off by default: `'display'` is the built-in, and paints every frame the
|
|
50
50
|
// clock gives, after React's own batching — a UI answers its input as
|
|
51
51
|
// quickly as it can unless it says otherwise. `'adaptive'` is what a window
|
|
52
|
-
// that streams asks for.
|
|
52
|
+
// that streams asks for. design/architecture/frame-pacing.md is the account.
|
|
53
53
|
|
|
54
54
|
export const DEFAULT_FRAME_RATE = 'display';
|
|
55
55
|
|
package/src/refresh/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// react-x11/refresh — the runtime half of state-preserving hot reload
|
|
2
2
|
// (Fast Refresh). The loader half is ./loader.js; `react-x11/refresh/register`
|
|
3
|
-
// wires both up for `node --import`. See docs/ecosystem/dev-tooling.md
|
|
3
|
+
// wires both up for `node --import`. See docs/ecosystem/dev-tooling.md, and
|
|
4
|
+
// design/ecosystem/dev-tooling.md §"How it reloads" for the loader's rules.
|
|
4
5
|
//
|
|
5
6
|
// Import order is what this module exists to own:
|
|
6
7
|
//
|
package/src/types/video.d.ts
CHANGED
|
@@ -69,8 +69,11 @@ export class VideoFrames {
|
|
|
69
69
|
readonly colorSpace: VideoColorSpace;
|
|
70
70
|
readonly range: VideoRange;
|
|
71
71
|
/** This display's formats, cheapest first: what a decoder that can choose
|
|
72
|
-
* should emit (`ffmpeg -pix_fmt nv12`). NV12 leads
|
|
73
|
-
*
|
|
72
|
+
* should emit (`ffmpeg -pix_fmt nv12`). NV12 leads wherever something
|
|
73
|
+
* native takes it as it is — a layer on macOS, a shader on Wayland, an Xv
|
|
74
|
+
* port on X11 — and BGRA where a frame is converted in JavaScript. On X11
|
|
75
|
+
* it is BGRA, and not kept, until the port has answered, a few round
|
|
76
|
+
* trips after the first sink is made. */
|
|
74
77
|
readonly preferredFormats: readonly VideoFormat[];
|
|
75
78
|
/** Frames pushed so far. */
|
|
76
79
|
readonly version: number;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Who writes a `<video>`'s frames natively on an app, if anyone — the seam
|
|
2
|
+
// `VideoFrames.preferredFormats` and the drawn presentation ask through
|
|
3
|
+
// (design/architecture/video.md §6.3).
|
|
4
|
+
//
|
|
5
|
+
// The app itself where its backend has the verbs: the Cocoa app over the
|
|
6
|
+
// bridge's video surfaces, the Wayland app over its own GL context. On X11
|
|
7
|
+
// the app is ntk's, which knows nothing of sinks, so the answer is the Xv
|
|
8
|
+
// writer react-x11 keeps for the connection (src/xvideo.js). A feature test,
|
|
9
|
+
// not a question about which backend this is — `typeof app.xv` is an ntk
|
|
10
|
+
// with XVideo discovery. Null where nothing will, and every frame is
|
|
11
|
+
// converted in JavaScript (`frameToRGBA`, src/videoframes.js).
|
|
12
|
+
import { xvVideoFor } from './xvideo.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `{ videoFormats(), writeVideoFrame(surface, sink, frame, node),
|
|
16
|
+
* releaseVideoFrame?(surface), prepare?(sink) }` for `app`, or null.
|
|
17
|
+
*/
|
|
18
|
+
export function videoBackendOf(app) {
|
|
19
|
+
if (!app) return null;
|
|
20
|
+
if (
|
|
21
|
+
typeof app.writeVideoFrame === 'function' ||
|
|
22
|
+
typeof app.videoFormats === 'function'
|
|
23
|
+
) {
|
|
24
|
+
return app;
|
|
25
|
+
}
|
|
26
|
+
if (typeof app.xv === 'function') return xvVideoFor(app);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
package/src/videoframes.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// `VideoFrames`: a sink an application pushes decoded video frames into,
|
|
2
|
-
// and a `<video frames>` shows (
|
|
2
|
+
// and a `<video frames>` shows (design/architecture/video.md §6.3).
|
|
3
3
|
//
|
|
4
4
|
// react-x11 decodes nothing. Where the bytes come from — an `ffmpeg` child
|
|
5
5
|
// writing rawvideo to a pipe, a WASM decoder, an addon over libmpv — is the
|
|
@@ -33,12 +33,16 @@
|
|
|
33
33
|
// a decoder that can choose (`ffmpeg -pix_fmt`); one that cannot still
|
|
34
34
|
// works, with the conversion charged where it happens.
|
|
35
35
|
//
|
|
36
|
-
//
|
|
37
|
-
// (
|
|
38
|
-
//
|
|
36
|
+
// No node and no backend: the one question asked of the display is its
|
|
37
|
+
// formats, through `videoBackendOf` (src/videobackend.js) — the Cocoa app's
|
|
38
|
+
// answer from the bridge, the Wayland app's own, and on X11 the Xv writer's,
|
|
39
|
+
// which a sink starts asking for when it is made.
|
|
40
|
+
|
|
41
|
+
import { videoBackendOf } from './videobackend.js';
|
|
39
42
|
|
|
40
43
|
export const VIDEO_FORMATS = Object.freeze(['NV12', 'I420', 'BGRA']);
|
|
41
44
|
const COLOR_SPACES = ['bt709', 'bt601', 'bt2020'];
|
|
45
|
+
const BGRA_ONLY = Object.freeze(['BGRA']);
|
|
42
46
|
const RANGES = ['video', 'full'];
|
|
43
47
|
// The largest side a frame may have: a surface's limit on every backend, and
|
|
44
48
|
// a number past which a size is a bug rather than a video.
|
|
@@ -209,22 +213,30 @@ export class VideoFrames {
|
|
|
209
213
|
this._frame = null;
|
|
210
214
|
this._listeners = new Set();
|
|
211
215
|
this._preferred = null;
|
|
216
|
+
// On X11 the formats are a port's, and asking for one is round trips:
|
|
217
|
+
// started here, so they are known by the time a decoder that waits for
|
|
218
|
+
// anything at all — a probe of the file, a spawn — reads them.
|
|
219
|
+
videoBackendOf(app)?.prepare?.(this);
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
/**
|
|
215
223
|
* This display's formats, cheapest first: what a decoder that can choose
|
|
216
|
-
* should emit.
|
|
217
|
-
*
|
|
224
|
+
* should emit. YCbCr first wherever something native takes it as it is —
|
|
225
|
+
* a layer on Cocoa, a shader on Wayland, an Xv port on X11 — and BGRA
|
|
226
|
+
* first where a frame is converted in JavaScript on its way to a 2D
|
|
227
|
+
* context. On X11 the answer is BGRA until the port has answered, a few
|
|
228
|
+
* round trips after the first sink is made, and is not kept until then.
|
|
218
229
|
*/
|
|
219
230
|
get preferredFormats() {
|
|
220
|
-
if (this._preferred
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
if (this._preferred !== null) return this._preferred;
|
|
232
|
+
const asked = videoBackendOf(this.app)?.videoFormats?.();
|
|
233
|
+
// null: still being asked — answer for now, and ask again next time
|
|
234
|
+
if (asked === null) return BGRA_ONLY;
|
|
235
|
+
this._preferred = Object.freeze(
|
|
236
|
+
Array.isArray(asked) && asked.length !== 0
|
|
237
|
+
? asked.filter((f) => VIDEO_FORMATS.includes(f))
|
|
238
|
+
: ['BGRA'],
|
|
239
|
+
);
|
|
228
240
|
return this._preferred;
|
|
229
241
|
}
|
|
230
242
|
|
|
@@ -281,8 +293,9 @@ export const isVideoFrames = (v) => v instanceof VideoFrames;
|
|
|
281
293
|
|
|
282
294
|
// --- converting a frame where nothing native will ----------------------------
|
|
283
295
|
//
|
|
284
|
-
// The drawn path
|
|
285
|
-
// Cocoa bridge older than its video surfaces
|
|
296
|
+
// The drawn path where nothing native writes the frame (win32, an X server
|
|
297
|
+
// with no Xv adaptor, a Cocoa bridge older than its video surfaces, and on
|
|
298
|
+
// any backend a frame the native writer declines): the frame converted here to
|
|
286
299
|
// the straight RGBA `putImageData` takes. Not free — two million pixels a
|
|
287
300
|
// frame at 1080p — which is why `preferredFormats` says BGRA first there.
|
|
288
301
|
// The matrix and the range are the frame's; no transfer function is
|