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/docs/gl.md
CHANGED
|
@@ -26,35 +26,27 @@ and the display-list discipline the indirect backend demands.
|
|
|
26
26
|
these are different APIs, not three spellings of one, and which one a
|
|
27
27
|
connection has decides what `onDraw` can do.
|
|
28
28
|
|
|
29
|
-
| | **direct** (X11)
|
|
30
|
-
| -------------- |
|
|
31
|
-
| how it draws | OpenGL ES 2 on the GPU
|
|
32
|
-
| shaders | **yes**, GLSL ES 1.00
|
|
33
|
-
| render targets | **yes** — framebuffer objects
|
|
34
|
-
| geometry | vertex buffers on the GPU
|
|
35
|
-
| lighting | per fragment
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
([windows-gl.md](windows-gl.md)), so it composites with the window's alpha
|
|
51
|
-
where an X11 surface does not. On **Wayland** it is not a surface of its own
|
|
52
|
-
at all: it is a rectangle of the window's backing target, drawn by the
|
|
53
|
-
window's own GLES context between the tree's 2D paint and its children's
|
|
54
|
-
panes (`src/wayland/glarea.js`).
|
|
55
|
-
|
|
56
|
-
On X11 the default is indirect, because it is what react-x11 has always
|
|
57
|
-
used. Turn the other on per app:
|
|
29
|
+
| | **direct** (X11) | **indirect** (X11) | **Cocoa** |
|
|
30
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- |
|
|
31
|
+
| how it draws | OpenGL ES 2 on the GPU (DRI3 + Present on Linux, Apple-DRI on macOS/XQuartz) | GL 1.x commands encoded into the X connection | CGL — OpenGL 4.1 core on Metal — presented as a sublayer |
|
|
32
|
+
| shaders | **yes**, GLSL ES 1.00 | none | **yes** |
|
|
33
|
+
| render targets | **yes** — framebuffer objects | none | **yes** |
|
|
34
|
+
| geometry | vertex buffers on the GPU | immediate mode compiled into display lists | vertex buffers on the GPU |
|
|
35
|
+
| lighting | per fragment | per vertex | per fragment |
|
|
36
|
+
| where it runs | a local connection, plus ntk's optional `x11-dri` addon: a Linux server with DRI3, or macOS/XQuartz with Apple-DRI (ntk 8.4.0) | any server that allows indirect contexts, including over a network | any Mac, on the Cocoa backend — it needs the same optional `x11-dri` addon and nothing else |
|
|
37
|
+
|
|
38
|
+
The Cocoa column hands `onDraw` the same WebGL-shaped `gl` table as X11
|
|
39
|
+
direct. `glPolicy: 'indirect'` is an error there, and says so: there is no X
|
|
40
|
+
server to speak GLX to.
|
|
41
|
+
|
|
42
|
+
**win32** and **Wayland** have no column because they hand `onDraw` the same
|
|
43
|
+
direct API and report `gl.backend === 'direct'` too. On win32 a `<glarea>` is
|
|
44
|
+
a WGL context composited under the window's own visual, so it composites with
|
|
45
|
+
the window's alpha where an X11 surface does not. On Wayland it is drawn into
|
|
46
|
+
the window's own backing target, between the tree's 2D paint and its
|
|
47
|
+
children's panes, rather than being a surface of its own.
|
|
48
|
+
|
|
49
|
+
On X11 the default is indirect. Turn direct on per app:
|
|
58
50
|
|
|
59
51
|
```jsx
|
|
60
52
|
const root = await createRoot({ glPolicy: 'auto' });
|
|
@@ -100,17 +92,36 @@ PascalCase OpenGL 1.x. Nothing translates between them. Branch on
|
|
|
100
92
|
```
|
|
101
93
|
|
|
102
94
|
Code written against one spelling will not run on the other, which is why
|
|
103
|
-
the default X11 policy does not switch under an app that never asked for it
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
API runs unchanged on the Cocoa backend, and one written for indirect GLX
|
|
107
|
-
does not run there at all.
|
|
95
|
+
the default X11 policy does not switch under an app that never asked for it.
|
|
96
|
+
A scene written for the direct API runs unchanged on the Cocoa backend; one
|
|
97
|
+
written for indirect GLX does not run there at all.
|
|
108
98
|
|
|
109
99
|
**On the indirect backend, geometry belongs in a display list.** Every
|
|
110
100
|
immediate-mode vertex is a command on the wire, so a mesh re-sent per frame
|
|
111
|
-
costs kilobytes per frame
|
|
112
|
-
are yours to choose — `GenLists` is
|
|
113
|
-
where round trips are the thing to
|
|
101
|
+
costs kilobytes per frame (about 96 KB for a 1 000-triangle mesh) while a
|
|
102
|
+
compiled list costs one `CallList`. Names are yours to choose — `GenLists` is
|
|
103
|
+
a round trip, and this is the backend where round trips are the thing to
|
|
104
|
+
avoid.
|
|
105
|
+
|
|
106
|
+
### What indirect GLX encodes
|
|
107
|
+
|
|
108
|
+
Indirect GLX is a fixed-function OpenGL 1.x pipeline, and these are the
|
|
109
|
+
commands it can carry:
|
|
110
|
+
|
|
111
|
+
| available | |
|
|
112
|
+
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
113
|
+
| immediate mode | `Begin`/`End`, `Vertex3f`, `Normal3f`, `Color3f/4f`, `TexCoord2f` |
|
|
114
|
+
| matrices | `MatrixMode`, `LoadIdentity`, `LoadMatrixf`, `MultMatrixf`, `Push/PopMatrix`, `Translatef`, `Rotatef`, `Scalef`, `Frustum`, `Ortho` |
|
|
115
|
+
| lighting | `Lightfv`, `LightModelf`, `Materialfv`, `Materialf`, `ColorMaterial`, `ShadeModel` |
|
|
116
|
+
| texturing | `TexImage2D`, `BindTexture`, `TexParameter*`, `TexEnv*`, `TexGen*` |
|
|
117
|
+
| state | `Enable`/`Disable`, `Clear*`, `DepthFunc`, `DepthMask`, `BlendFunc`, `AlphaFunc`, `CullFace`, `FrontFace`, `PolygonMode`, `Viewport`, `Scissor`, `Fog*` |
|
|
118
|
+
| display lists | `CallList`, `ListBase`, `NewList`/`EndList`/`DeleteLists` |
|
|
119
|
+
|
|
120
|
+
There are **no GLSL shaders, no VBOs, no vertex arrays** (`DrawArrays` and
|
|
121
|
+
`VertexPointer` are not encoded), **no framebuffer objects and no
|
|
122
|
+
instancing**, and ARB assembly programs are not supported. These are limits
|
|
123
|
+
of the protocol, not of an implementation: shader materials and
|
|
124
|
+
post-processing need the direct backend.
|
|
114
125
|
|
|
115
126
|
## Input
|
|
116
127
|
|
|
@@ -126,23 +137,17 @@ nothing should: on X11 a listener there takes the event away from the tree
|
|
|
126
137
|
|
|
127
138
|
A `<glarea>`'s children are drawn above it — a legend, a toolbar, a label
|
|
128
139
|
over the scene — laid out in its box and hit before it. They are ordinary
|
|
129
|
-
elements.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
`clearColor` ([elements.md](elements.md#glarea) has the table).
|
|
140
|
+
elements. On the Cocoa backend they composite with the GL frame,
|
|
141
|
+
translucency and all. On X11 each is drawn on an opaque pane over the
|
|
142
|
+
surface, so what a child leaves unpainted shows the surface's `clearColor`
|
|
143
|
+
([elements.md](elements.md#glarea) has the table).
|
|
134
144
|
`useSupports('glOverlay')` asks whether a connection draws them;
|
|
135
145
|
`examples/labs/gl-overlay.jsx` runs one on both backends.
|
|
136
146
|
|
|
137
|
-
The answer is no on XQuartz
|
|
138
|
-
surface there above everything the X server draws
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
over GL.
|
|
142
|
-
|
|
143
|
-
So a HUD is not GL's business: its text is set by the app's own text
|
|
144
|
-
engine, its controls take their own input, and nothing is rasterized into a
|
|
145
|
-
texture.
|
|
147
|
+
The answer is no on XQuartz: the macOS window server composites every GL
|
|
148
|
+
surface there above everything the X server draws. The children are laid out
|
|
149
|
+
and not drawn, and a development build warns once. On macOS the Cocoa backend
|
|
150
|
+
is the one that composites a HUD over GL.
|
|
146
151
|
|
|
147
152
|
## When there is no surface at all
|
|
148
153
|
|
|
@@ -155,16 +160,14 @@ the reader can do.
|
|
|
155
160
|
|
|
156
161
|
## Testing
|
|
157
162
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
163
|
+
Test a GL app on **what it emits**, not on pixels: GL renders where `GetImage`
|
|
164
|
+
cannot read it (on XQuartz a capture reads back white), so a screenshot of a
|
|
165
|
+
window with a `<glarea>` has a hole where the scene is. On the direct
|
|
166
|
+
backend, `gl.readPixels` inside `onDraw` is the only way to see the frame.
|
|
161
167
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
- `test/viewer3d.test.js` does the same for the example, and says in its
|
|
166
|
-
header which claim that harness cannot see and where it is measured
|
|
167
|
-
instead.
|
|
168
|
+
This repo's `test/glarea.test.js` shows the pattern: node-x11's in-process X
|
|
169
|
+
server with its GLX emulator registered, so a frame's GL calls land on a
|
|
170
|
+
`RecordingBackend` — no display, no GPU, no addon.
|
|
168
171
|
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
Design notes: [design/gl.md](../design/gl.md), and the indirect backend's
|
|
173
|
+
design in [design/glx.md](../design/glx.md).
|
package/docs/globalmenu.md
CHANGED
|
@@ -25,10 +25,8 @@ import { MenuBar } from 'react-x11';
|
|
|
25
25
|
/>;
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
platform
|
|
30
|
-
platform shows this one; where it does not, `MenuBar` draws the bar itself,
|
|
31
|
-
exactly as before.
|
|
28
|
+
Where the platform has somewhere to put a menu, `MenuBar` renders nothing and
|
|
29
|
+
the platform shows this one; where it does not, `MenuBar` draws the bar itself.
|
|
32
30
|
|
|
33
31
|
| where the menu goes | when |
|
|
34
32
|
| ------------------------------------------------------ | ------------------------------------------------------------------------------------- |
|
|
@@ -36,20 +34,23 @@ exactly as before.
|
|
|
36
34
|
| **the desktop's panel**, over `com.canonical.dbusmenu` | a panel is running one of the applets above |
|
|
37
35
|
| **the window**, drawn by `MenuBar` | everything else — a stock GNOME session, XQuartz, an ssh session, a bare `startx`, CI |
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
[macos.md §Menus](macos.md#menus-the-global-menu-finally-at-home)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
On macOS the menu bar takes the menu as soon as `MenuBar` mounts and the drawn
|
|
38
|
+
fallback never renders; see
|
|
39
|
+
[macos.md §Menus](macos.md#menus-the-global-menu-finally-at-home).
|
|
40
|
+
|
|
41
|
+
On Linux the panel row is decided live. The menu leaves the window only once a
|
|
42
|
+
**running** owner of `com.canonical.AppMenu.Registrar` has accepted it (a
|
|
43
|
+
registrar that is merely D-Bus-activatable, as Ubuntu ships it, does not
|
|
44
|
+
count), and it moves into the panel when one starts and back into the window
|
|
45
|
+
when one exits, with no re-render on the app's part. KDE's
|
|
46
|
+
`_KDE_NET_WM_APPMENU_*` window properties are written as well. Design notes:
|
|
47
|
+
[design/globalmenu.md](../design/globalmenu.md).
|
|
46
48
|
|
|
47
49
|
## The item vocabulary is the protocol's
|
|
48
50
|
|
|
49
51
|
`menus` is a plain data prop rather than JSX children, and its item shape is
|
|
50
|
-
`com.canonical.dbusmenu`'s
|
|
51
|
-
|
|
52
|
-
an exported one that drift apart.
|
|
52
|
+
`com.canonical.dbusmenu`'s: **the identical array serialises**, so the drawn
|
|
53
|
+
menu and the exported one cannot drift apart.
|
|
53
54
|
|
|
54
55
|
| property | meaning |
|
|
55
56
|
| -------------------------- | ----------------------------------------------------------- |
|
|
@@ -67,13 +68,16 @@ an exported one that drift apart.
|
|
|
67
68
|
| `key` | identity, when the label is not stable |
|
|
68
69
|
|
|
69
70
|
`toggleState` has **three** values, not two: `-1` is _indeterminate_ — a "Bold"
|
|
70
|
-
that is on for part of the selection — and
|
|
71
|
-
unchecked
|
|
71
|
+
that is on for part of the selection — and draws as a dash rather than as
|
|
72
|
+
unchecked.
|
|
73
|
+
|
|
74
|
+
`icon` is the one thing that does not cross the bus: it is a react-x11 drawing
|
|
75
|
+
callback ([components.md](components.md#menubar--contextmenu)), so an item that
|
|
76
|
+
wants an icon in both menus carries `icon` for ours and `iconName` for the
|
|
77
|
+
panel's.
|
|
72
78
|
|
|
73
|
-
`
|
|
74
|
-
|
|
75
|
-
cannot call a function, so an item that wants an icon in both menus carries
|
|
76
|
-
`icon` for ours and `iconName` for the panel's.
|
|
79
|
+
Building `menus` inline on every render is fine: only what changed is sent to
|
|
80
|
+
the panel, and item identity (the path of `key ?? label`) survives re-renders.
|
|
77
81
|
|
|
78
82
|
### Shortcuts are a list, not a string
|
|
79
83
|
|
|
@@ -85,17 +89,14 @@ The outer array is a list of _alternatives_; each inner one is modifier tokens
|
|
|
85
89
|
(`Control`, `Alt`, `Shift`, `Super`) ending in the key. Only the first is
|
|
86
90
|
drawn — a row has one shortcut column.
|
|
87
91
|
|
|
88
|
-
**On macOS, `Control` means
|
|
89
|
-
in the
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
([events.md](events.md#primary-modifier)), and so do the window's own
|
|
97
|
-
bindings: `useAccelerator`, and a drawn `MenuBar` or `ContextMenu`, which
|
|
98
|
-
prints `⌘S` beside the row ([events.md](events.md#the-chord)).
|
|
92
|
+
**On macOS, `Control` means ⌘** — the primary modifier — so `[['Control', 'S']]`
|
|
93
|
+
becomes ⌘S in the menu bar. `Super` maps there too; `Shift` and `Alt` are
|
|
94
|
+
themselves. A chord the `NSMenu` spelling cannot express is not printed beside
|
|
95
|
+
the item, but the binding still fires (the app's window answers it, below).
|
|
96
|
+
The text controls' chords, the edit menu that prints them
|
|
97
|
+
([events.md](events.md#primary-modifier)) and the window's own bindings
|
|
98
|
+
(`useAccelerator`, a drawn `MenuBar` or `ContextMenu`, which prints `⌘S` beside
|
|
99
|
+
the row — [events.md](events.md#the-chord)) follow the same rule.
|
|
99
100
|
|
|
100
101
|
The key is named the way GDK names it, so `plus` rather than `+` and `Prior`
|
|
101
102
|
rather than `PgUp`, because that is what a panel's importer parses. Menus print
|
|
@@ -106,84 +107,35 @@ its own items' chords: Ctrl+S fires the item's `onSelect` without the menu
|
|
|
106
107
|
being opened, gated by that item's own `enabled` and `visible`
|
|
107
108
|
([events.md](events.md#accelerators)).
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
**panel is not going to deliver the key** — it has no idea the window's
|
|
111
|
-
keyboard is where the user is — so the app's own window still has to, and
|
|
110
|
+
When the panel draws the menu, the **panel does not deliver the key**, so
|
|
112
111
|
`MenuBar` keeps its bindings when it stops drawing. `accelerators={false}`
|
|
113
112
|
turns them off for an application with a dispatcher of its own.
|
|
114
113
|
|
|
115
114
|
The macOS menu bar is the one delegate that **does** deliver keys: it runs an
|
|
116
|
-
item on the ⌘ chord
|
|
117
|
-
the
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
## What happens on the wire
|
|
123
|
-
|
|
124
|
-
The D-Bus path, in enough detail to recognise it in `dbus-monitor` and to
|
|
125
|
-
know what to look at when a menu does not appear. None of this applies on
|
|
126
|
-
the Cocoa backend, where there is no wire and no registrar — installing an
|
|
127
|
-
`NSMenu` is the whole of it.
|
|
128
|
-
|
|
129
|
-
1. **Detection.** Does something own `com.canonical.AppMenu.Registrar` right
|
|
130
|
-
now? Only ownership counts — see the trap below.
|
|
131
|
-
2. **Export.** The menu is served at `/com/react_x11/menus/<xid>` on the
|
|
132
|
-
process's existing bus connection ([dbus.md](dbus.md)), so the desktop sees
|
|
133
|
-
one application rather than several.
|
|
134
|
-
3. **Registration.** `RegisterWindow(xid, path)`. The service name is inferred
|
|
135
|
-
from the sender, so only the path is supplied.
|
|
136
|
-
4. **The KDE properties**, in addition: `_KDE_NET_WM_APPMENU_SERVICE_NAME` and
|
|
137
|
-
`_KDE_NET_WM_APPMENU_OBJECT_PATH`, written as `STRING`/format 8. Plasma
|
|
138
|
-
reads these off the window instead of asking a registrar, and runs a
|
|
139
|
-
registrar _as well_ — which of the two a given version prefers is not
|
|
140
|
-
something to bet a menu on.
|
|
141
|
-
5. **Only then** does `MenuBar` stop drawing. `RegisterWindow` returning is the
|
|
142
|
-
evidence; a registrar that refuses the call leaves the menu where the user
|
|
143
|
-
can still reach it.
|
|
144
|
-
|
|
145
|
-
It is followed for the life of the window, not sampled once: `NameOwnerChanged`
|
|
146
|
-
moves the menu into the panel when one starts and back into the window when one
|
|
147
|
-
exits, with no re-render on the app's part.
|
|
148
|
-
|
|
149
|
-
### The trap: activatable is not running
|
|
150
|
-
|
|
151
|
-
Ubuntu ships `com.canonical.AppMenu.Registrar` as a D-Bus **activatable**
|
|
152
|
-
service, so the name resolves even with no panel installed. This is the exact
|
|
153
|
-
opposite of the rule `hasService()` follows for portals, and getting it wrong
|
|
154
|
-
is silent and total — an app with no menu anywhere.
|
|
155
|
-
|
|
156
|
-
A registrar is not the feature; it is a directory the panel reads. Nothing
|
|
157
|
-
starts one except a panel starting up. So react-x11 asks `ListNames` for a live
|
|
158
|
-
owner, and every call it then makes carries D-Bus's `NO_AUTO_START` flag —
|
|
159
|
-
without which the tidy-up `UnregisterWindow` sent after a panel dies would
|
|
160
|
-
_launch a fresh registrar_, which would own the name with nothing drawing the
|
|
161
|
-
menus it collects.
|
|
115
|
+
item on the ⌘ chord installed as its key equivalent, after the window has seen
|
|
116
|
+
the key. So there `MenuBar` reserves those chords — no binding in the window
|
|
117
|
+
answers them, its own included, and the item runs once — and keeps answering
|
|
118
|
+
the chords no `NSMenu` key equivalent can spell
|
|
119
|
+
([events.md](events.md#the-macos-menu-bar)).
|
|
162
120
|
|
|
163
121
|
## Seeing it work, without a desktop that has one
|
|
164
122
|
|
|
165
|
-
|
|
166
|
-
the consumers are panel applets welded into Plasma, into `vala-panel`, or into
|
|
167
|
-
a GNOME extension. So there is one in this repo:
|
|
123
|
+
In a checkout of this repo, a stand-in panel prints every menu handed to it:
|
|
168
124
|
|
|
169
125
|
```bash
|
|
170
126
|
npm run globalmenu:host
|
|
171
127
|
```
|
|
172
128
|
|
|
173
|
-
|
|
174
|
-
to it. In another terminal:
|
|
129
|
+
In another terminal:
|
|
175
130
|
|
|
176
131
|
```bash
|
|
177
132
|
npm run examples:menu
|
|
178
133
|
```
|
|
179
134
|
|
|
180
|
-
The window's own menu bar disappears
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
On a machine that really does have a panel, `npm run globalmenu:host -- --watch`
|
|
186
|
-
attaches to the live registrar rather than competing for the name.
|
|
135
|
+
The window's own menu bar disappears and the menu appears in the host. Type an
|
|
136
|
+
item's id and press Enter to activate it; Ctrl-C the host and the bar returns
|
|
137
|
+
to the window. On a machine with a real panel,
|
|
138
|
+
`npm run globalmenu:host -- --watch` attaches to the live registrar instead.
|
|
187
139
|
|
|
188
140
|
## Turning it off
|
|
189
141
|
|
|
@@ -200,12 +152,11 @@ await createRoot({ desktop: { globalMenu: false } });
|
|
|
200
152
|
```
|
|
201
153
|
|
|
202
154
|
The prop is for one bar. The other two are process-wide: the environment
|
|
203
|
-
variable needs no application change
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
that talk to the session bus.
|
|
155
|
+
variable needs no application change (for a user working around a panel that
|
|
156
|
+
renders something badly), and the `createRoot` option is the one an embedder
|
|
157
|
+
can set without it leaking to child processes — see
|
|
158
|
+
[desktop.md](desktop.md#turning-the-desktop-off), where `desktop: false` turns
|
|
159
|
+
this off along with the other session-bus integrations.
|
|
209
160
|
|
|
210
161
|
## Drawing your own bar
|
|
211
162
|
|
|
@@ -224,55 +175,21 @@ function Bar({ menus }) {
|
|
|
224
175
|
|
|
225
176
|
## Known limits, so they are not rediscovered as bugs
|
|
226
177
|
|
|
227
|
-
- **Stock GNOME has no global menu bar at all**, so
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
`onAboutToShow`, and emits `LayoutUpdated` when the new items actually
|
|
242
|
-
serialise — shells listen for that unconditionally, so a lazily-built submenu
|
|
243
|
-
fills in one round trip later than a synchronous toolkit manages. Answering
|
|
244
|
-
`true` instead would be worse: the shell would immediately fetch a subtree
|
|
245
|
-
that has not been built and cache the old one.
|
|
246
|
-
- **`org.gtk.Menus`/`org.gtk.Actions` is deliberately not exported.** Handling
|
|
247
|
-
both serialisations is right for a panel and wrong for an app — traffic flows
|
|
248
|
-
_towards_ dbusmenu, not away from it. Plasma ships `gmenu-dbusmenu-proxy` to
|
|
249
|
-
convert GTK's menus into dbusmenu, and the GNOME extensions that render
|
|
250
|
-
third-party menus consume dbusmenu. A second exporter would be roughly double
|
|
251
|
-
the work for no consumer the first does not already reach.
|
|
178
|
+
- **Stock GNOME has no global menu bar at all**, so the in-window bar stays.
|
|
179
|
+
The extensions that add one work.
|
|
180
|
+
- **`ContextMenu` is not exported** — a right-click menu belongs where the
|
|
181
|
+
pointer is.
|
|
182
|
+
- **One menu per window.** A second `MenuBar` in a window that already has one
|
|
183
|
+
stays drawn in the window until the first unmounts or turns `globalMenu`
|
|
184
|
+
off, and only then takes the panel. A bar remounted with a new `key`, or
|
|
185
|
+
switched off and on again, waits the same way for the one it replaces to
|
|
186
|
+
finish leaving (a round trip or two).
|
|
187
|
+
- **A lazily-built submenu fills in one round trip late.** `onAboutToShow`
|
|
188
|
+
runs when the panel opens the submenu, and the new items reach the panel
|
|
189
|
+
once they have rendered.
|
|
190
|
+
- **`org.gtk.Menus`/`org.gtk.Actions` is not exported**; Plasma and the GNOME
|
|
191
|
+
extensions consume dbusmenu.
|
|
252
192
|
- **No bus is a first-class configuration.** Everything here degrades to
|
|
253
193
|
"draw the bar" without logging: no `dbus-native` (Node 20), no
|
|
254
194
|
`$DBUS_SESSION_BUS_ADDRESS`, a connection that fails. See
|
|
255
195
|
[dbus.md](dbus.md).
|
|
256
|
-
|
|
257
|
-
## Performance, and the one thing worth knowing
|
|
258
|
-
|
|
259
|
-
A menu is re-serialised whenever the `menus` array changes identity, which for
|
|
260
|
-
an app that builds it inline is every render. That part is cheap. What is not
|
|
261
|
-
cheap is the signal it produces, and the difference is the reason this feature
|
|
262
|
-
has a diff at all:
|
|
263
|
-
|
|
264
|
-
- ids and children identical, only properties differ → `ItemsPropertiesUpdated`,
|
|
265
|
-
**revision unchanged**;
|
|
266
|
-
- structure changed → `revision++` and `LayoutUpdated(revision, parent)`, with
|
|
267
|
-
`parent` the nearest common ancestor of what changed rather than `0`.
|
|
268
|
-
|
|
269
|
-
Plasma re-fetches a whole subtree on `LayoutUpdated` and only patches on
|
|
270
|
-
`ItemsPropertiesUpdated`. A menu whose one check mark flipped must therefore
|
|
271
|
-
not bump the revision — which is exactly what a naive
|
|
272
|
-
`setState` → `LayoutUpdated(++rev, 0)` does on every keystroke, and it turns one
|
|
273
|
-
property write into a re-walk of every menu in the application.
|
|
274
|
-
|
|
275
|
-
Item ids are keyed on the path of `key ?? label` down the tree — the same
|
|
276
|
-
identity `MenuBar` already uses for its React keys — so they survive a
|
|
277
|
-
re-render, and they are never reused, because a shell has properties cached
|
|
278
|
-
against them.
|