react-x11 2.51.1 → 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 +94 -61
- package/docs/README.md +256 -0
- package/docs/accessibility.md +575 -0
- package/docs/appearance.md +317 -0
- package/docs/click-to-component.md +92 -0
- package/docs/clipboard.md +262 -0
- package/docs/components.md +1231 -0
- package/docs/dbus.md +205 -0
- package/docs/debugging.md +371 -0
- package/docs/desktop-calendar.md +247 -0
- package/docs/desktop.md +523 -0
- package/docs/devtools.md +133 -0
- package/docs/drag-and-drop.md +585 -0
- package/docs/ecosystem/animation.md +360 -0
- package/docs/ecosystem/data-fetching.md +186 -0
- package/docs/ecosystem/dev-tooling.md +393 -0
- package/docs/ecosystem/forms.md +277 -0
- package/docs/ecosystem/headless.md +194 -0
- package/docs/ecosystem/i18n.md +86 -0
- package/docs/ecosystem/icons.md +377 -0
- package/docs/ecosystem/layout.md +206 -0
- package/docs/ecosystem/routing.md +135 -0
- package/docs/ecosystem/state.md +344 -0
- package/docs/ecosystem/testing.md +474 -0
- package/docs/ecosystem/theming.md +494 -0
- package/docs/ecosystem.md +439 -0
- package/docs/elements.md +2079 -0
- package/docs/embedding.md +239 -0
- package/docs/events.md +742 -0
- package/docs/extending.md +1700 -0
- package/docs/eyedropper.md +164 -0
- package/docs/filedialog.md +222 -0
- package/docs/frame.md +436 -0
- package/docs/gl.md +173 -0
- package/docs/globalmenu.md +195 -0
- package/docs/macos.md +592 -0
- package/docs/notifications.md +150 -0
- package/docs/packaging.md +468 -0
- package/docs/permissions.md +143 -0
- package/docs/react-features.md +405 -0
- package/docs/remote.md +179 -0
- package/docs/scale.md +225 -0
- package/docs/security.md +132 -0
- package/docs/styling.md +1710 -0
- package/docs/system.md +519 -0
- package/docs/testing.md +494 -0
- package/docs/typescript.md +75 -0
- package/docs/uri-schemes.md +340 -0
- package/docs/wayland-backend.md +332 -0
- package/docs/windows-integrations.md +177 -0
- package/docs/windows.md +380 -0
- package/package.json +11 -3
- 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/bus.js +39 -26
- package/src/cli/create.d.ts +43 -0
- package/src/cli/create.js +305 -0
- package/src/cli/env.js +484 -0
- package/src/cli/index.js +29 -2
- package/src/cli/templates/AGENTS.template.md +195 -0
- package/src/cli/templates/DESIGN.template.md +269 -0
- 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/desktopintegration.js +2 -1
- package/src/desktopsettings.js +193 -24
- package/src/dnd.js +2 -2
- package/src/filedialog.js +3 -3
- package/src/glnodes.js +1 -1
- package/src/grid.js +1 -1
- package/src/keyboardstate.js +28 -0
- package/src/layouts.js +1 -1
- package/src/node.d.ts +10 -10
- 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/portal.js +31 -0
- package/src/refresh/index.js +2 -1
- package/src/screencolor.js +5 -10
- package/src/types/system.d.ts +8 -6
- 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 +86 -0
- package/src/wayland/clipboard.js +14 -7
- package/src/wayland/connection.js +3 -0
- package/src/wayland/context2d.js +129 -10
- package/src/wayland/protocols/index.json +6 -0
- package/src/wayland/protocols/xdg-foreign-unstable-v2.json +1 -0
- package/src/wayland/seat.js +28 -1
- package/src/wayland/swapchain.js +23 -0
- package/src/wayland/video.js +376 -0
- package/src/wayland/xkb.js +137 -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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Desktop integration on Windows: what works, and what does not
|
|
2
|
+
|
|
3
|
+
What an app on the [win32 backend](windows.md) can reach today, what it
|
|
4
|
+
cannot, and which of the gaps have a route. Every row was measured on this
|
|
5
|
+
backend rather than inferred from the API's existence: "works" means a test
|
|
6
|
+
asserts it or it was driven and seen.
|
|
7
|
+
|
|
8
|
+
## Works
|
|
9
|
+
|
|
10
|
+
| what | the API | how |
|
|
11
|
+
| ------------------------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
12
|
+
| a window, composed | `<window>` | DirectComposition surface per window, Direct2D verbs |
|
|
13
|
+
| text and layout | `<text>`, `app.fonts` | DirectWrite: layout, line metrics, hit testing, carets |
|
|
14
|
+
| glyph runs | `ctx.drawGlyphs`, `face.glyphIdFor`/`advanceOf` | `DrawGlyphRun` — what `<Terminal backend="vt">` positions its grid with |
|
|
15
|
+
| variable font axes | `style.variations` | `IDWriteTextLayout4::SetFontAxisValues` |
|
|
16
|
+
| app-supplied fonts | `loadFont`, `openFont` | a DirectWrite font set of the app's own, with the file's axes kept |
|
|
17
|
+
| OpenGL | `<glarea>` | WGL, and `WGL_NV_DX_interop2` to reach the composed surface |
|
|
18
|
+
| keyboard | `onKeyDown`, focus | `ToUnicodeEx` without consuming dead keys; X keysym vocabulary |
|
|
19
|
+
| pointer | `onMouseDown`, wheel, capture | all five buttons, `SetCapture` for drags leaving the window |
|
|
20
|
+
| focus | `onFocus`/`onBlur` | `WM_ACTIVATE` |
|
|
21
|
+
| window states | `useWindowState`, `<window maximized fullscreen alwaysOnTop>` | `ShowWindow`, `SetWindowPos`, a remembered frame for fullscreen |
|
|
22
|
+
| screens and DPI | `useScreens`, `useScale` | per-monitor v2; no live per-window re-scale yet ([windows.md](windows.md#scale-and-dpi)) |
|
|
23
|
+
| light/dark and accent | `useSystemAppearance` | `UISettings` and the theme registry; the frame follows through `DWMWA_USE_IMMERSIVE_DARK_MODE` |
|
|
24
|
+
| native control bezels | `<button>`, `<checkbox>`… | the visual styles engine, on a light desktop; dark ones are drawn (Windows' are not public) |
|
|
25
|
+
| file dialogs | `useFileDialog` | the Common Item Dialog, parented, modal on the UI thread |
|
|
26
|
+
| clipboard | `useClipboard` | text only, and format enumeration |
|
|
27
|
+
| tray icon and menu | `useTray` | `Shell_NotifyIcon` |
|
|
28
|
+
| taskbar progress | `useProgress` | `ITaskbarList3::SetProgressValue` |
|
|
29
|
+
| a badge on the icon | `useBadge`, `setBadge` | the count drawn into an overlay icon — Windows has no badge API |
|
|
30
|
+
| demanding attention | `setUrgent` | `FlashWindowEx` |
|
|
31
|
+
| global hotkeys | `useAccelerator` | `RegisterHotKey` |
|
|
32
|
+
| notifications | `notify`, `useNotifier` | a tray balloon, which the shell shows as a toast |
|
|
33
|
+
| idle time | `useIdle` | `GetLastInputInfo`, polled on the same adaptive wait the X rung uses |
|
|
34
|
+
| keeping the screen awake | `keepAwake`, `useKeepAwake` | `SetThreadExecutionState`, counted |
|
|
35
|
+
| reading a window back | `window.snapshot()` | `PrintWindow` with `PW_CLIENTONLY` and `PW_RENDERFULLCONTENT` |
|
|
36
|
+
| locale | `systemLocale`, `useLocale` | ICU's own resolution, which reads the OS on Windows — no backend code |
|
|
37
|
+
| drag and drop | `useDropTarget`, `useDragSource` | `IDropTarget` and `DoDragDrop` over OLE, both directions |
|
|
38
|
+
| a thumbnail toolbar | `useThumbnailToolbar` | `ITaskbarList3::ThumbBarAddButtons` — **Windows only** |
|
|
39
|
+
| a jump list | `useJumpList` | `ICustomDestinationList` — **Windows only** |
|
|
40
|
+
| recent documents | `useRecentDocument` | `SHAddToRecentDocs` — **Windows only** |
|
|
41
|
+
| input methods | `onCompositionStart`/`Update`/`End`, `<textinput>` | IMM32: the preedit is drawn in the field, the candidate list follows the caret |
|
|
42
|
+
| screen readers | `role`, `aria-*`, `announce()` | a UI Automation provider over a pushed mirror of the tree |
|
|
43
|
+
| window identity | `<window appId>` | the AppUserModelID on the window's property store — taskbar grouping, pinning, the jump list |
|
|
44
|
+
|
|
45
|
+
## Built, with limits worth knowing
|
|
46
|
+
|
|
47
|
+
### Input methods — built on IMM32, with the gaps that layer has
|
|
48
|
+
|
|
49
|
+
CJK input works, through the same composition events a Wayland text-input
|
|
50
|
+
drives, so an application handles one composition and gets both. The preedit
|
|
51
|
+
is drawn **in the field** rather than in a floating box, and the candidate
|
|
52
|
+
list follows the caret.
|
|
53
|
+
|
|
54
|
+
What it does not carry is what IMM32 itself no longer carries. Modern input
|
|
55
|
+
methods — the new Microsoft Japanese and Chinese IMEs included — are TSF text
|
|
56
|
+
services reaching an IMM32 window through a compatibility layer, which does
|
|
57
|
+
not pass on **voice typing** (Win+H inserts nothing), the shell's
|
|
58
|
+
**handwriting** panel, or **shape-writing** on the touch keyboard. A TSF text
|
|
59
|
+
store would close those; it is not built. Reconversion (asking the IME to
|
|
60
|
+
re-open a committed word) is not wired up either, though IMM32 carries it.
|
|
61
|
+
|
|
62
|
+
### Screen readers — built, without text ranges
|
|
63
|
+
|
|
64
|
+
Screen readers are served by a UI Automation provider of this project's own,
|
|
65
|
+
answering from a copy of the tree the app pushes, so a screen reader is never
|
|
66
|
+
paced by the app's busiest moment. Roles, names, descriptions, states,
|
|
67
|
+
bounding rectangles, focus and the Invoke, Toggle, Value and RangeValue
|
|
68
|
+
patterns all cross; `announce()` goes out as a UIA notification. Everything an
|
|
69
|
+
app writes — `role`, `aria-label`, `aria-checked`, `aria-valuenow` — is read
|
|
70
|
+
by the same code the AT-SPI bridge reads, so the two backends cannot disagree
|
|
71
|
+
about what a node is.
|
|
72
|
+
|
|
73
|
+
**What is missing is `ITextProvider`.** A `<textinput>` exposes its value, so
|
|
74
|
+
a screen reader reads the field and hears edits, but caret-by-caret and
|
|
75
|
+
word-by-word navigation _inside_ it is not there.
|
|
76
|
+
|
|
77
|
+
## Not implemented
|
|
78
|
+
|
|
79
|
+
Ordered by what it costs an app today.
|
|
80
|
+
|
|
81
|
+
### `useLauncherMenu` is inert
|
|
82
|
+
|
|
83
|
+
The jump list is built (`useJumpList` above) but it is **not** what
|
|
84
|
+
`useLauncherMenu` means, and is deliberately not wired to it. A launcher-menu
|
|
85
|
+
item carries a callback; a jump-list task starts a _new process_ with
|
|
86
|
+
arguments, because the shell launches the program rather than calling into
|
|
87
|
+
the running one. Mapping one onto the other would quietly change what a click
|
|
88
|
+
does.
|
|
89
|
+
|
|
90
|
+
### Activation — file associations, URL schemes, second launches
|
|
91
|
+
|
|
92
|
+
`useAppOpen` and `useAppActivate` never fire on Windows: there are no file
|
|
93
|
+
associations, no URL schemes and no single-instance handoff yet, so a second
|
|
94
|
+
launch is a second process. [uri-schemes.md](uri-schemes.md) documents what
|
|
95
|
+
the other platforms do.
|
|
96
|
+
|
|
97
|
+
### Eyedropper — the pixel is read, the ladder has no rung
|
|
98
|
+
|
|
99
|
+
The bridge reads a screen pixel (`app.screenColorAt`, `app.pointerPosition`),
|
|
100
|
+
but `useEyedropper()` reports `supported: false` here until a rung draws the
|
|
101
|
+
loupe over it.
|
|
102
|
+
|
|
103
|
+
### Calendar — blocked, not missing
|
|
104
|
+
|
|
105
|
+
`useDesktopCalendarEvents` wants the Windows appointments API, which requires
|
|
106
|
+
package identity. Unpackaged, there is nothing to call, and the hook reports
|
|
107
|
+
`supported: false` rather than guessing.
|
|
108
|
+
|
|
109
|
+
### Permissions — nothing to ask
|
|
110
|
+
|
|
111
|
+
`usePermission` has no Windows meaning for an unpackaged app: the capability
|
|
112
|
+
API is for packaged apps, and everything else sits under one per-device
|
|
113
|
+
switch. The answer is `'unknown'` until something is attempted, and the
|
|
114
|
+
attempt's own error is the answer.
|
|
115
|
+
|
|
116
|
+
### Global menu — not a Windows concept
|
|
117
|
+
|
|
118
|
+
`useGlobalMenu` is a menu bar owned by the desktop. Windows has none; a
|
|
119
|
+
`<MenuBar>` drawn in the window is the whole story here, and that works.
|
|
120
|
+
|
|
121
|
+
### Keyboard grabs
|
|
122
|
+
|
|
123
|
+
`grabKeyboard` accepts and does nothing: there is no Windows mechanism for it.
|
|
124
|
+
A popup that asked for keys gets them only while it is the foreground window.
|
|
125
|
+
|
|
126
|
+
`grabPointer` is a different story — see below.
|
|
127
|
+
|
|
128
|
+
## Built, with limits worth knowing: `<popup grab>`
|
|
129
|
+
|
|
130
|
+
Windows has no pointer grab to give — `SetCapture` sends a window the mouse
|
|
131
|
+
only while a button is already down — so the dismissal is reproduced from two
|
|
132
|
+
signals, and `<Select>`, `<Menu>` and any `<popup grab>` close the way they do
|
|
133
|
+
everywhere else:
|
|
134
|
+
|
|
135
|
+
- a press delivered to **another window of this application** — a click in
|
|
136
|
+
the owner behind the menu, in a second window, or in the menu a submenu came
|
|
137
|
+
from;
|
|
138
|
+
- the application **losing activation** — a press in another application or
|
|
139
|
+
on the desktop. A popup never takes activation itself, so opening one raises
|
|
140
|
+
no blur.
|
|
141
|
+
|
|
142
|
+
**The gap**: a press on the _non-client_ area of one of our own windows — a
|
|
143
|
+
title bar, a resize border — is not reported, so a menu left open while the
|
|
144
|
+
user drags the window behind it stays open. X11's grab covers that and this
|
|
145
|
+
does not.
|
|
146
|
+
|
|
147
|
+
## What Windows has that nothing else does
|
|
148
|
+
|
|
149
|
+
Some Windows surfaces have no cross-platform API to fit into, so they have
|
|
150
|
+
APIs of their own, and **a backend installs a method, and its presence is the
|
|
151
|
+
capability**. They are reported as features of the launcher, beside the badge
|
|
152
|
+
and the progress bar:
|
|
153
|
+
|
|
154
|
+
```jsx
|
|
155
|
+
const launcher = useDesktopCapability('launcher');
|
|
156
|
+
launcher.backend; // 'taskbar' here, 'cocoa' on a Dock
|
|
157
|
+
launcher.features.thumbnailToolbar; // the buttons under the hover preview
|
|
158
|
+
launcher.features.tasks; // the jump list's Tasks category
|
|
159
|
+
launcher.features.recentDocuments; // the shell's Recent lists
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
They live there rather than in `useSupports()` because `useSupports` answers
|
|
163
|
+
for the **display** — transparency, shaders, embedding — and a taskbar button
|
|
164
|
+
is not a display property; and because a boolean cannot say _which_ launcher
|
|
165
|
+
answered. Every other backend answers false for the three, and the hooks do
|
|
166
|
+
nothing where the method is absent. `examples/taskbar.jsx` is the shape;
|
|
167
|
+
there is no `process.platform` in it.
|
|
168
|
+
|
|
169
|
+
- **Thumbnail toolbars** — `useThumbnailToolbar`: up to seven buttons under
|
|
170
|
+
the taskbar thumbnail, play/pause/next for a media app.
|
|
171
|
+
- **Recent documents** — `useRecentDocument`.
|
|
172
|
+
- **A jump list** — `useJumpList` (see [`useLauncherMenu`](#uselaunchermenu-is-inert)).
|
|
173
|
+
- **Taskbar overlay as a status light**: `setBadge` takes a label, and an app
|
|
174
|
+
that wants a dot rather than a number can pass one character.
|
|
175
|
+
|
|
176
|
+
Ideas not built yet are in the
|
|
177
|
+
[design notes](../design/windows-integrations.md#what-windows-has-that-nothing-else-does).
|
package/docs/windows.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# A native Windows backend
|
|
2
|
+
|
|
3
|
+
The `win32` backend draws a react-x11 app into real Win32 windows: one
|
|
4
|
+
DirectComposition surface per window, Direct2D for drawing, DirectWrite for
|
|
5
|
+
text. It is **the default on Windows**, and it needs the `@windowkit/win32`
|
|
6
|
+
native bridge; without the bridge, `'auto'` falls back to X11. The examples
|
|
7
|
+
run on it, except `wm` and `xeyes`, which are X11 window-manager demos.
|
|
8
|
+
|
|
9
|
+
[windows-integrations.md](windows-integrations.md) is the row-by-row status of
|
|
10
|
+
what an app can reach on Windows. Why the backend is built the way it is — the
|
|
11
|
+
threading model, the rendering internals, the plan and its open questions — is
|
|
12
|
+
the design record: [design/windows.md](../design/windows.md).
|
|
13
|
+
|
|
14
|
+
## Choosing a backend
|
|
15
|
+
|
|
16
|
+
`createRoot({ backend })`, or `REACT_X11_BACKEND` without touching code:
|
|
17
|
+
|
|
18
|
+
| asked | on Windows |
|
|
19
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
20
|
+
| `'auto'` (the default) | `'win32'` when `@windowkit/win32` loads. Otherwise X11 through `DISPLAY`, with one console warning outside production, so a WSLg or Cygwin setup keeps working |
|
|
21
|
+
| `'win32'` | the bridge is required: if it cannot load, `createRoot()` rejects with an error that lists what it tried and how to install it |
|
|
22
|
+
| `'x11'` | an X server through `DISPLAY`. Also what `'auto'` picks when you pass `display` or `stream`. Windows has no `fc-match`, so tell ntk where the fonts are with `fontSource` or `REACT_X11_FONT_SOURCE` |
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
const root = await createRoot(); // win32 on Windows, x11 if the bridge is missing
|
|
26
|
+
const root = await createRoot({ backend: 'win32' }); // the bridge or an error
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Asking for `'win32'` on any other platform throws: the backend is
|
|
30
|
+
Windows-only.
|
|
31
|
+
|
|
32
|
+
## Installing the bridge
|
|
33
|
+
|
|
34
|
+
- `@windowkit/win32` is an `optionalDependency` of react-x11 with
|
|
35
|
+
`os: ["win32"]`, so `npm install react-x11` on Windows installs it and
|
|
36
|
+
every other platform skips it. If optional dependencies were omitted,
|
|
37
|
+
install it yourself: `npm install @windowkit/win32`.
|
|
38
|
+
- Prebuilds ship for **x64 and ARM64**. Building from source needs Visual
|
|
39
|
+
Studio with the "Desktop development with C++" workload.
|
|
40
|
+
- It is an N-API addon, so it loads under Bun (`bun.exe`) as well as Node.
|
|
41
|
+
- `REACT_X11_WIN32_PATH=<path>` loads the bridge from a path instead of the
|
|
42
|
+
installed package: a checkout, or the file shipped beside a compiled
|
|
43
|
+
executable (see [Packaging and identity](#packaging-and-identity)).
|
|
44
|
+
- An older bridge degrades feature by feature rather than failing: a missing
|
|
45
|
+
verb answers "unsupported" (for example, `useWindowState()` sees `false`,
|
|
46
|
+
and a `<glarea>`'s children are not drawn).
|
|
47
|
+
- Built and measured on Windows 11. Windows 10 is not a verified target.
|
|
48
|
+
|
|
49
|
+
## Threads and the event loop
|
|
50
|
+
|
|
51
|
+
The bridge runs every window on a UI thread of its own, and your JS stays on
|
|
52
|
+
Node's main thread. Windows' modal loops — a live resize, a native menu, a
|
|
53
|
+
drag (`DoDragDrop` does not return until the drop), a file dialog — run on
|
|
54
|
+
that UI thread, so **JS is never frozen by one**: timers fire, sockets read,
|
|
55
|
+
React commits and frames present while the user drags or a dialog is open.
|
|
56
|
+
This is the main behavioural difference from the Cocoa backend's pump mode.
|
|
57
|
+
|
|
58
|
+
Events reach JS as soon as they happen, without waiting for a timer tick, and
|
|
59
|
+
a handler's `setState` commits on the event that caused it.
|
|
60
|
+
|
|
61
|
+
## Rendering
|
|
62
|
+
|
|
63
|
+
- **Every window is composited by DWM**, so `transparent` always works and
|
|
64
|
+
`useSupports('transparency')` is always true — there is no "if a compositor
|
|
65
|
+
is running".
|
|
66
|
+
- **ClearType on opaque windows, grayscale on transparent ones.** Direct2D
|
|
67
|
+
draws subpixel text only onto a surface whose alpha is ignored, so text in
|
|
68
|
+
a `<window transparent>` or `<popup transparent>` is grayscale-antialiased.
|
|
69
|
+
- **Frames are paced by DWM's compositor clock**, at the refresh rate of the
|
|
70
|
+
monitor under the window; `frameRate` works as on the other backends. An
|
|
71
|
+
idle app polls nothing.
|
|
72
|
+
- Only damaged rectangles are repainted, and scrolling is a blit, as on X11
|
|
73
|
+
and Cocoa.
|
|
74
|
+
- **Reading pixels back**: `window.snapshot()` works once the window has
|
|
75
|
+
been composed. It rejects for a minimised or not-yet-composed window; draw
|
|
76
|
+
into an offscreen surface and read that instead.
|
|
77
|
+
- `<video frames>` draws; `<video src>` does not —
|
|
78
|
+
`useSupports('mediaPlayback')` is false here.
|
|
79
|
+
- Debugging: `REACT_X11_WIN32_DEBUG=1` reports every frame and what it drew
|
|
80
|
+
into, the size each paragraph is shaped at, and drag and drop;
|
|
81
|
+
`REACT_X11_WIN32_FULL_REPAINT=1` paints the whole window every frame, the
|
|
82
|
+
control for a damage bug.
|
|
83
|
+
|
|
84
|
+
## Scale and DPI
|
|
85
|
+
|
|
86
|
+
- Windows are **per-monitor-v2 DPI aware** whatever `node.exe`'s manifest
|
|
87
|
+
says, so nothing is bitmap-stretched and layout runs in device pixels.
|
|
88
|
+
- **Fractional scale is the normal case** (125%, 150%, 175%). Layout rounds
|
|
89
|
+
to whole device pixels, so a one-logical-pixel hairline at 1.25× lands on
|
|
90
|
+
one device pixel or two depending on where it falls
|
|
91
|
+
([scale.md](scale.md)).
|
|
92
|
+
- `useScale()` is the **primary monitor's** scale when the app starts, static
|
|
93
|
+
for the life of the root as on every backend, and every window is laid out
|
|
94
|
+
at it.
|
|
95
|
+
- `useScreens()` lists every monitor; its `available` is the monitor's exact
|
|
96
|
+
work area.
|
|
97
|
+
- **Not yet: live per-window scale.** A window on, or dragged to, a monitor
|
|
98
|
+
at another scale is not re-laid out at that scale: it stays sharp but is
|
|
99
|
+
the wrong physical size there.
|
|
100
|
+
|
|
101
|
+
## Text: DirectWrite behind the engine contract
|
|
102
|
+
|
|
103
|
+
Text is DirectWrite end to end — font matching, fallback, shaping, bidi, line
|
|
104
|
+
breaking, hit testing — so it looks like the platform's text, the way the
|
|
105
|
+
Cocoa backend uses CoreText.
|
|
106
|
+
|
|
107
|
+
- **Generic families**: `sans-serif`, `system-ui` and `ui-sans-serif` →
|
|
108
|
+
Segoe UI; `serif` → Times New Roman; `monospace` and `ui-monospace` →
|
|
109
|
+
Consolas; `cursive` → Segoe Script.
|
|
110
|
+
- **Font stacks** resolve to the first family installed. A face filed under
|
|
111
|
+
another family ("Arial Black", "Segoe UI Light") is found as GDI finds it,
|
|
112
|
+
and Courier/Courier New, Times/Times New Roman and Arial/Helvetica stand in
|
|
113
|
+
for each other.
|
|
114
|
+
- In laid-out text, characters the font lacks fall back to a system font,
|
|
115
|
+
as DirectWrite layouts do. `app.fonts.fallbackFor()` answers `null` (not
|
|
116
|
+
bound yet), so code drawing its own glyph runs gets no fallback face.
|
|
117
|
+
- `loadFont` and `openFont` load the app's own fonts, variable axes kept;
|
|
118
|
+
`style.variations` sets axes.
|
|
119
|
+
- Measurements are rounded up to whole device pixels, and lines break at the
|
|
120
|
+
same opportunities as on Cocoa, but metrics are DirectWrite's: expect a
|
|
121
|
+
pixel or so of difference from the X11 backend's layouts.
|
|
122
|
+
|
|
123
|
+
## Input
|
|
124
|
+
|
|
125
|
+
### Pointer
|
|
126
|
+
|
|
127
|
+
- All five mouse buttons and the wheel. A drag that leaves the window keeps
|
|
128
|
+
receiving motion while a button is held.
|
|
129
|
+
- A precision touchpad sends fractions of a notch; those arrive as wheel
|
|
130
|
+
events with `smooth: true`.
|
|
131
|
+
- Motion and wheel are coalesced to one per frame, as on X11.
|
|
132
|
+
- Not yet: pen- and touch-specific events, touchpad inertia, pinch.
|
|
133
|
+
|
|
134
|
+
### Keyboard
|
|
135
|
+
|
|
136
|
+
- Key events carry the same X keysym vocabulary, `codepoint` and `key` as on
|
|
137
|
+
every backend; the editing, navigation, function and keypad keys have their
|
|
138
|
+
X keysyms.
|
|
139
|
+
- Dead keys compose in the layout, as the layout defines them; the backend
|
|
140
|
+
never consumes the dead-key state behind your back.
|
|
141
|
+
- Tab and Return never insert a control character into a field.
|
|
142
|
+
|
|
143
|
+
### IME
|
|
144
|
+
|
|
145
|
+
Input methods work, through IMM32 — Chinese, Japanese and Korean composition
|
|
146
|
+
reach the same `onCompositionStart`/`Update`/`End` events a Wayland
|
|
147
|
+
text-input drives, so one handler serves both.
|
|
148
|
+
|
|
149
|
+
- The **preedit is drawn in the field**, laid out and styled with the rest of
|
|
150
|
+
the text; Windows' floating composition window is suppressed. The
|
|
151
|
+
**candidate list** is the input method's own and follows the caret.
|
|
152
|
+
- A commit arrives before the next preedit, and the active clause is the
|
|
153
|
+
preedit cursor (`cursorBegin`/`cursorEnd`), as on Wayland.
|
|
154
|
+
- A `sensitive` field is not offered to the input method at all, so its text
|
|
155
|
+
never reaches a word history — as Windows does for its own password boxes.
|
|
156
|
+
- **What IMM32 does not carry**: modern input methods are TSF text services
|
|
157
|
+
reaching IMM32 windows through a compatibility layer, and that layer drops
|
|
158
|
+
**voice typing** (Win+H inserts nothing), the shell's **handwriting**
|
|
159
|
+
panel and **shape-writing** on the touch keyboard. A TSF text store, which
|
|
160
|
+
would close those, is not built. Reconversion is not wired up either.
|
|
161
|
+
- `REACT_X11_TRACE_IME=1` prints a line per input-method event.
|
|
162
|
+
|
|
163
|
+
## Windowing semantics: what maps, what bends, what breaks
|
|
164
|
+
|
|
165
|
+
**Maps**:
|
|
166
|
+
|
|
167
|
+
- `title`, `width`, `height`, `x`, `y` and the natural size. `x`/`y` are
|
|
168
|
+
virtual-screen coordinates, negative left of and above the primary monitor.
|
|
169
|
+
- `minWidth`/`minHeight`/`maxWidth`/`maxHeight` and `resizable` become the
|
|
170
|
+
window's size limits.
|
|
171
|
+
- `fullscreen`, `alwaysOnTop`, maximise and minimise use Windows' own calls;
|
|
172
|
+
full screen is a borderless monitor-sized window that remembers the frame
|
|
173
|
+
it came from.
|
|
174
|
+
- `transparent` — always available ([Rendering](#rendering)).
|
|
175
|
+
- `appId` — the window's AppUserModelID
|
|
176
|
+
([Packaging and identity](#packaging-and-identity)).
|
|
177
|
+
- `<popup>` — no taskbar button, never takes activation, placed against the
|
|
178
|
+
monitor's work area as everywhere.
|
|
179
|
+
- Drag and drop, both directions: `dropAccept`, `onDrag*` and `dragData`
|
|
180
|
+
hold, and JS keeps running through the drag. Windows itself refuses a drop
|
|
181
|
+
from Explorer into an elevated app.
|
|
182
|
+
|
|
183
|
+
**Bends**:
|
|
184
|
+
|
|
185
|
+
- `<popup grab>`: there is no pointer grab on Windows, so a press in another
|
|
186
|
+
of the app's windows, or the app losing activation, closes the popup
|
|
187
|
+
instead. The gap: a press on one of the app's own title bars or resize
|
|
188
|
+
borders does not, so a menu stays open while its window is dragged.
|
|
189
|
+
- `states`: `shaded` and `sticky` have no Windows call and resolve false,
|
|
190
|
+
which `useWindowState()` reports. `focused` can be refused by Windows'
|
|
191
|
+
foreground rules, and then simply does not change.
|
|
192
|
+
- `embeddable` and `<foreign>`: see
|
|
193
|
+
[Embedding](#embedding-frame-foreign-window-embeddable).
|
|
194
|
+
- The global menu: Windows has none; draw a `<MenuBar>`.
|
|
195
|
+
|
|
196
|
+
**Inert** — accepted and ignored, so shared code stays branch-free:
|
|
197
|
+
|
|
198
|
+
- `decorations={false}`, today. A titlebar drawn in the window and moved
|
|
199
|
+
from `onMouseMove` loses snapping and the Snap Layouts flyout.
|
|
200
|
+
- `transientFor`, `windowType`.
|
|
201
|
+
- `grabKeyboard`: a popup gets keys only while it is the foreground window.
|
|
202
|
+
- `onClientMessage` never fires; there is no X.
|
|
203
|
+
|
|
204
|
+
**Breaks**, by design: window managers (`examples/wm.jsx`) — DWM owns every
|
|
205
|
+
frame — and `ssh -X` remoting, for which the X11 backend stays the answer
|
|
206
|
+
([remote.md](remote.md)).
|
|
207
|
+
|
|
208
|
+
## The desktop around the app
|
|
209
|
+
|
|
210
|
+
No D-Bus or portals here: each service has a Windows rung or an honest
|
|
211
|
+
floor. [windows-integrations.md](windows-integrations.md) has the measured
|
|
212
|
+
status of each; in short:
|
|
213
|
+
|
|
214
|
+
- **Work**: `useFileDialog()` (Windows' own dialog, owned by the window that
|
|
215
|
+
asked), `useSystemAppearance()` (light/dark, accent, high contrast, reduced
|
|
216
|
+
motion; the window frame follows dark mode), `useTray()`, `useProgress()`,
|
|
217
|
+
`useBadge()`/`setBadge()` (drawn into the taskbar's overlay icon — pass one
|
|
218
|
+
character for a dot), `setUrgent()`, `useIdle()`, `keepAwake()`,
|
|
219
|
+
`useLocale()`.
|
|
220
|
+
- **Global shortcuts** (`useAccelerator()`): system-wide, no admin. A chord
|
|
221
|
+
another app holds fails; Windows-key chords are reserved.
|
|
222
|
+
- **Notifications** (`notify()`, `useNotifier()`) are tray balloons, which
|
|
223
|
+
the shell shows as toasts and keeps in the Action Center: plain text with a
|
|
224
|
+
severity — no actions, click events, updates or custom icon, as the
|
|
225
|
+
capabilities say.
|
|
226
|
+
- **Clipboard** (`useClipboard()`): text only — writing anything else
|
|
227
|
+
rejects. No `PRIMARY`. `watch()` notices a change within about 400 ms.
|
|
228
|
+
- **Windows only**: `useJumpList()`, `useThumbnailToolbar()`,
|
|
229
|
+
`useRecentDocument()`, reported as features of
|
|
230
|
+
`useDesktopCapability('launcher')`.
|
|
231
|
+
- **Answer "no"**: `useLauncherMenu()` is inert (a jump-list task starts a new
|
|
232
|
+
process — use `useJumpList()`); `useAppOpen()` and `useAppActivate()` never
|
|
233
|
+
fire (no file associations, URL schemes or single instance yet);
|
|
234
|
+
`useEyedropper()` reports `supported: false`; `usePermission()` answers
|
|
235
|
+
`'unknown'`; `useDesktopCalendarEvents()` is `supported: false` unless the
|
|
236
|
+
app is packaged.
|
|
237
|
+
- **Native control bezels** are the system's own while the desktop is light,
|
|
238
|
+
and drawn in dark mode, because Windows' dark common controls are not
|
|
239
|
+
public.
|
|
240
|
+
|
|
241
|
+
## Accessibility: UI Automation
|
|
242
|
+
|
|
243
|
+
Screen readers read the app through UI Automation. What an app writes —
|
|
244
|
+
`role`, `aria-*`, `announce()` — is read by the same code the Linux AT-SPI
|
|
245
|
+
bridge reads, so the two cannot disagree about what a node is.
|
|
246
|
+
|
|
247
|
+
- **Crosses**: roles, names, descriptions, states, bounding rectangles,
|
|
248
|
+
focus and hit testing; the Invoke, Toggle, Value and RangeValue patterns;
|
|
249
|
+
`announce()` as a UIA notification.
|
|
250
|
+
- The bridge answers from a copy of the tree the app pushes after commits, so
|
|
251
|
+
a screen reader never waits on a busy JS thread. Pushes are paced as
|
|
252
|
+
[accessibility.md](accessibility.md#how-often-the-bridge-speaks) describes.
|
|
253
|
+
- **Missing: text ranges.** A `<textinput>` exposes its value, so a screen
|
|
254
|
+
reader reads the field and hears edits, but cannot move caret by caret or
|
|
255
|
+
word by word inside it.
|
|
256
|
+
- `REACT_X11_TRACE_A11Y=1` prints a line per push and action.
|
|
257
|
+
|
|
258
|
+
## 3D: `<glarea>`
|
|
259
|
+
|
|
260
|
+
`<glarea>` draws through OpenGL on the **GPU vendor's driver**, and its frames
|
|
261
|
+
are composited with the rest of the window, the window's alpha included.
|
|
262
|
+
|
|
263
|
+
- The context is an OpenGL ES-profile one: GLSL ES shaders, and the
|
|
264
|
+
WebGL 2-shaped `gl` (`gl.bindBuffer`, …) of the X11 direct backend.
|
|
265
|
+
`@react-x11/components/three` runs on it. No `glPolicy` is needed: this
|
|
266
|
+
backend is always direct.
|
|
267
|
+
- It needs the driver's `WGL_NV_DX_interop2`, which NVIDIA and AMD drivers
|
|
268
|
+
have and some Intel configurations lack.
|
|
269
|
+
- **Without a vendor driver** — a virtual machine without GPU passthrough, a
|
|
270
|
+
CI runner, a fresh install before Windows Update fetched the driver, some
|
|
271
|
+
Remote Desktop sessions — Windows offers only the OpenGL 1.1 software
|
|
272
|
+
rasterizer, which has no shaders. `<glarea>` then reports one `onError`
|
|
273
|
+
naming the reason, and `useSupports('shaders')` is false. The ANGLE
|
|
274
|
+
fallback that would cover these machines is not built.
|
|
275
|
+
- `useSupports('shaders')` is false for one tick and then true: the GL probe
|
|
276
|
+
(about 120 ms) runs the first time something asks, not at startup.
|
|
277
|
+
- A `<glarea>`'s children are drawn on composited panes above the surface,
|
|
278
|
+
translucency included (`useSupports('glOverlay')` is true). The surface
|
|
279
|
+
takes no pointer input; the pointer reaches the tree.
|
|
280
|
+
|
|
281
|
+
Background: [design/windows-gl.md](../design/windows-gl.md).
|
|
282
|
+
|
|
283
|
+
## Embedding: `<Frame>`, `<foreign>`, `<window embeddable>`
|
|
284
|
+
|
|
285
|
+
The API is the same as everywhere; what answers it differs.
|
|
286
|
+
|
|
287
|
+
- **`<Frame src>` works.** The pane runs in its own process and draws into a
|
|
288
|
+
composition surface the host shows; `props`, `fallback` and `restart()`
|
|
289
|
+
behave as on every backend ([frame.md](frame.md)). The host is the pane's
|
|
290
|
+
parent process; a pane started some other way names it with
|
|
291
|
+
`createRoot({ win32: { paneHostPid } })`.
|
|
292
|
+
- **`<foreign>` refuses.** Windows' cross-process child windows attach the two
|
|
293
|
+
programs' input queues, so a hung guest would hang the host.
|
|
294
|
+
`useSupports('embedding')` is false, and a `<foreign>` reports one
|
|
295
|
+
`onError`:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
react-x11: <foreign> needs a backend with cross-process window embedding,
|
|
299
|
+
and this one has none — so nothing can be put in it. Ask
|
|
300
|
+
useSupports('embedding') before rendering one.
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
```jsx
|
|
304
|
+
const embedding = useSupports('embedding');
|
|
305
|
+
return embedding ? <foreign onReady={spawnInto} /> : <text>X11 only</text>;
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
- **`<window embeddable>`** is created and not shown, as everywhere.
|
|
309
|
+
`windowHandleOf(ref)` / `useWindowHandle(ref)` returns a **composition
|
|
310
|
+
surface handle** already valid in the host process — not an HWND, because a
|
|
311
|
+
composition target stops presenting once its window is made a child. Pass
|
|
312
|
+
it to the host out of band, as an XID is passed on X11. The host must bind
|
|
313
|
+
a composition surface to a visual of its own (another react-x11 app, WinUI
|
|
314
|
+
3, a DirectComposition-based host); a plain Win32 or OLE document host
|
|
315
|
+
cannot. The host is the parent process unless
|
|
316
|
+
`createRoot({ win32: { paneHostPid } })` names another.
|
|
317
|
+
- OLE embedding (a pane inside a Word document) is not supported.
|
|
318
|
+
|
|
319
|
+
Background: [design/windows-embedding.md](../design/windows-embedding.md).
|
|
320
|
+
|
|
321
|
+
## Packaging and identity
|
|
322
|
+
|
|
323
|
+
- **`node app.js` works**, with two cosmetic problems. `node.exe` is a
|
|
324
|
+
console program, so launched from a shortcut it opens a console window.
|
|
325
|
+
And without an `appId`, the taskbar groups the app's windows with every
|
|
326
|
+
other `node.exe` window under Node's icon, and pinning pins `node.exe`.
|
|
327
|
+
- **`<window appId>`** (or the class half of `wmClass`) sets the window's
|
|
328
|
+
AppUserModelID, per window, before it is shown. The taskbar groups by it,
|
|
329
|
+
pinning pins it, and the jump list belongs to it. A pinned tile relaunches
|
|
330
|
+
the command line that started the process, named after the window's title,
|
|
331
|
+
with the executable's icon.
|
|
332
|
+
- **A single executable** ([packaging.md](packaging.md) tier 3,
|
|
333
|
+
`node --build-sea`) is a copy of `node.exe` with the app inside. After the
|
|
334
|
+
build: a manifest of the app's own with `rcedit`, the GUI subsystem with
|
|
335
|
+
`editbin /SUBSYSTEM:WINDOWS` so no console opens, then the signature.
|
|
336
|
+
Windows 11 24H2 also has a manifest setting that detaches the console when
|
|
337
|
+
launched from Explorer and keeps it in a terminal. Ship the bridge beside
|
|
338
|
+
the executable and name it with `REACT_X11_WIN32_PATH`.
|
|
339
|
+
- **`bun build --compile`** has `--windows-hide-console` and icon and
|
|
340
|
+
metadata flags, but no way to supply a manifest; `bun.exe`'s own manifest
|
|
341
|
+
has no compatibility section, and what that changes for DPI and DWM is not
|
|
342
|
+
yet verified.
|
|
343
|
+
- **Distribution**: Authenticode signing, which SmartScreen's reputation
|
|
344
|
+
follows. MSIX package identity is what unlocks the calendar, the
|
|
345
|
+
capability-status API and declared protocol activation.
|
|
346
|
+
- [packaging.md](packaging.md) has no Windows tier yet.
|
|
347
|
+
|
|
348
|
+
## Testing
|
|
349
|
+
|
|
350
|
+
- `react-x11/test` runs a tree against an in-process X server or the mock,
|
|
351
|
+
on Windows as anywhere ([testing.md](testing.md)) — it exercises the X11
|
|
352
|
+
backend, not this one.
|
|
353
|
+
- `window.snapshot()` reads a composed window back on a real Windows desktop
|
|
354
|
+
(above).
|
|
355
|
+
- Test at **fractional scales** — 1.25 and 1.5, not only 1 and 2. Logical and
|
|
356
|
+
device pixels part company there, and a test at scale 1 cannot tell which
|
|
357
|
+
of the two a coordinate is in.
|
|
358
|
+
- GitHub-hosted Windows runners have an interactive desktop (1024×768) and no
|
|
359
|
+
GPU driver, so `<glarea>` reports its error there; the rest of the backend
|
|
360
|
+
on Direct3D's software device (WARP) is not yet verified on them.
|
|
361
|
+
|
|
362
|
+
## Not supported yet
|
|
363
|
+
|
|
364
|
+
- Live per-window scale: a window on a monitor at another scale is not
|
|
365
|
+
re-laid out ([Scale and DPI](#scale-and-dpi)).
|
|
366
|
+
- UI Automation text ranges: no caret or word navigation inside a field.
|
|
367
|
+
- TSF: voice typing, handwriting and shape-writing; IME reconversion.
|
|
368
|
+
- `decorations={false}`, `transientFor`, and a drag-region API for drawn
|
|
369
|
+
titlebars (snapping, Snap Layouts).
|
|
370
|
+
- Clipboard formats other than text: images, HTML, file lists.
|
|
371
|
+
- File associations, URL schemes, single instance (`useAppOpen`,
|
|
372
|
+
`useAppActivate`); `useLauncherMenu`.
|
|
373
|
+
- Toasts with actions or click events (notifications are balloons).
|
|
374
|
+
- `useEyedropper()`; the calendar without package identity.
|
|
375
|
+
- `<glarea>` without a vendor OpenGL driver (ANGLE).
|
|
376
|
+
- `<video src>`.
|
|
377
|
+
- Compositor-side animation: transitions run on the frame clock, so a long
|
|
378
|
+
JS block stalls them, unlike on Cocoa.
|
|
379
|
+
- Pen- and touch-specific input, touchpad inertia and pinch.
|
|
380
|
+
- `<foreign>`, OLE embedding, window managers and `ssh -X` — by design.
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
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": {
|
|
7
7
|
"react-x11": "./src/cli/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
|
-
"src"
|
|
10
|
+
"src",
|
|
11
|
+
"!src/cli/create-react-x11",
|
|
12
|
+
"docs/*.md",
|
|
13
|
+
"docs/*/*.md"
|
|
11
14
|
],
|
|
12
15
|
"scripts": {
|
|
13
16
|
"test": "node --import tsx --test --test-timeout=60000",
|
|
@@ -69,6 +72,7 @@
|
|
|
69
72
|
"labs:direct-gl": "tsx examples/labs/direct-gl.jsx",
|
|
70
73
|
"labs:gl-overlay": "tsx examples/labs/gl-overlay.jsx",
|
|
71
74
|
"examples:viewer3d": "tsx examples/viewer3d.jsx",
|
|
75
|
+
"examples:video": "tsx examples/video.jsx",
|
|
72
76
|
"examples:transparent": "tsx examples/transparent.jsx",
|
|
73
77
|
"examples:windows": "tsx examples/windows.jsx",
|
|
74
78
|
"examples:wm": "tsx examples/wm.jsx",
|
|
@@ -107,7 +111,7 @@
|
|
|
107
111
|
"ical.js": "^2.2.1",
|
|
108
112
|
"image-in-browser": "^3.6.0",
|
|
109
113
|
"linebreak": "^1.1.0",
|
|
110
|
-
"ntk": "^8.
|
|
114
|
+
"ntk": "^8.24.0",
|
|
111
115
|
"react-reconciler": "^0.34.0",
|
|
112
116
|
"yoga-layout": "^3.2.1"
|
|
113
117
|
},
|
|
@@ -228,6 +232,10 @@
|
|
|
228
232
|
"./frame/child": {
|
|
229
233
|
"types": "./src/frame/child.d.ts",
|
|
230
234
|
"default": "./src/frame/child.js"
|
|
235
|
+
},
|
|
236
|
+
"./create": {
|
|
237
|
+
"types": "./src/cli/create.d.ts",
|
|
238
|
+
"default": "./src/cli/create.js"
|
|
231
239
|
}
|
|
232
240
|
},
|
|
233
241
|
"types": "./src/index.d.ts"
|
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
|