react-x11 2.16.0 → 2.17.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 +38 -23
- package/package.json +3 -1
- package/src/Reconciler.js +82 -23
- package/src/a11y.js +18 -1
- package/src/acceleratorhooks.js +40 -6
- package/src/anchor.js +20 -2
- package/src/appcontext.js +8 -0
- package/src/appearance.js +36 -0
- package/src/{cocoa → backend}/context2d.js +27 -7
- package/src/capabilities.js +99 -1
- package/src/cocoa/app.js +204 -6
- package/src/cocoa/fonts.js +1 -1
- package/src/cocoa/overlay.js +2 -2
- package/src/cocoa/panewindow.js +2 -2
- package/src/cocoa/presenter.js +2 -2
- package/src/cocoa/surface.js +3 -3
- package/src/cocoa/window.js +2 -2
- package/src/events.js +21 -0
- package/src/foreignnodes.js +8 -3
- package/src/frame/index.js +30 -4
- package/src/glnodes.js +12 -1
- package/src/idle.js +59 -1
- package/src/index.d.ts +51 -1
- package/src/index.js +30 -3
- package/src/keysymchars.js +47 -0
- package/src/keysyms.d.ts +19 -1
- package/src/keysyms.js +107 -8
- package/src/launcher.js +17 -8
- package/src/launcherhooks.js +24 -10
- package/src/node.d.ts +1 -1
- package/src/nodes/cascade.js +9 -0
- package/src/nodes/node.js +6 -1
- package/src/nodes/window/hints.js +21 -2
- package/src/nodes/window/window.js +2 -2
- package/src/notifications.js +39 -14
- package/src/screens.js +159 -24
- package/src/taskbarhooks.js +164 -0
- package/src/transfer.js +20 -1
- package/src/trayhooks.js +1 -1
- package/src/types/capabilities.d.ts +32 -3
- package/src/types/elements.d.ts +23 -1
- package/src/types/events.d.ts +21 -0
- package/src/types/filedialog.d.ts +3 -1
- package/src/types/launcher.d.ts +20 -6
- package/src/types/taskbar.d.ts +79 -0
- package/src/wayland/context2d.js +1 -1
- package/src/wayland/xkb.js +170 -59
- package/src/win32/a11y.js +604 -0
- package/src/win32/app.js +768 -0
- package/src/win32/bezels.js +158 -0
- package/src/win32/dnd.js +283 -0
- package/src/win32/fonts.js +497 -0
- package/src/win32/glarea.js +548 -0
- package/src/win32/ime.js +267 -0
- package/src/win32/keymap.js +116 -0
- package/src/win32/native.js +54 -0
- package/src/win32/panehost.js +106 -0
- package/src/win32/panewindow.js +343 -0
- package/src/win32/shell.js +426 -0
- package/src/win32/surface.js +192 -0
- package/src/win32/window.js +659 -0
- package/src/windowid.js +128 -20
package/src/windowid.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { useCallback, useMemo } from 'react';
|
|
9
9
|
|
|
10
10
|
import { useAppOrNull } from './appcontext.js';
|
|
11
|
+
import { canEmbed } from './embedding.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* The XID of the X11 window a ref points at, or `null` if there is not one
|
|
@@ -62,6 +63,71 @@ export function windowOf(target) {
|
|
|
62
63
|
return null;
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The handle **another process** embeds to show this window, or `null` if
|
|
68
|
+
* this backend cannot hand one out.
|
|
69
|
+
*
|
|
70
|
+
* `<window embeddable>` is how a window says "created, not shown; somebody
|
|
71
|
+
* else will place me" — the guest half of embedding, on every backend. This
|
|
72
|
+
* is the number that goes with it, and it is deliberately not `windowIdOf`:
|
|
73
|
+
* on X11 the two are the same, and everywhere else they are not.
|
|
74
|
+
*
|
|
75
|
+
* - **X11** — the window's XID. The same number in every process on the
|
|
76
|
+
* display, which is why one function answered both questions there and
|
|
77
|
+
* why nobody noticed they were two questions.
|
|
78
|
+
* - **Windows** — a **composition surface handle**, already valid in the
|
|
79
|
+
* host process. A window cannot be embedded here at all: a composition
|
|
80
|
+
* target stops presenting the moment its window becomes a child, measured
|
|
81
|
+
* in docs/windows-embedding.md. So what crosses is the buffer, and the
|
|
82
|
+
* host binds it to a visual of its own instead of reparenting. The host is
|
|
83
|
+
* the parent process by default — `createRoot({ win32: { paneHostPid } })`
|
|
84
|
+
* when it is not.
|
|
85
|
+
* - **Anything else** — `null`. Not an error: it is the capability, and the
|
|
86
|
+
* honest answer for a backend with no way to be a guest.
|
|
87
|
+
*
|
|
88
|
+
* The handle is a number to be passed out of band, exactly as an XID is —
|
|
89
|
+
* argv, an environment variable, a message on a pipe. What the host does
|
|
90
|
+
* with it is the host's business and differs per platform; what an app
|
|
91
|
+
* writes to *get* it does not.
|
|
92
|
+
*
|
|
93
|
+
* ```jsx
|
|
94
|
+
* const guest = useRef(null);
|
|
95
|
+
* useEffect(() => {
|
|
96
|
+
* const handle = windowHandleOf(guest);
|
|
97
|
+
* if (handle !== null) process.send?.({ type: 'embed-me', handle });
|
|
98
|
+
* }, []);
|
|
99
|
+
* return <window ref={guest} embeddable />;
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function windowHandleOf(target) {
|
|
103
|
+
const wnd = windowOf(target);
|
|
104
|
+
if (!wnd) return null;
|
|
105
|
+
// A backend that publishes something other than its window says so with a
|
|
106
|
+
// method, because only it knows what that something is.
|
|
107
|
+
if (typeof wnd.embedHandle === 'function') return wnd.embedHandle();
|
|
108
|
+
// …and where windows themselves are embeddable, the window id is it. The
|
|
109
|
+
// same question `<foreign>` asks before it builds a socket, so the two
|
|
110
|
+
// halves of embedding cannot disagree about whether this one works.
|
|
111
|
+
if (canEmbed(wnd.app ?? null)) return windowIdOf(wnd);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* `windowHandleOf` bound to a ref: a **getter**, stable across renders — the
|
|
117
|
+
* shape `useWindowId` has, and for the same reason. A window is not realized
|
|
118
|
+
* on the render that declares it, so a value read then would be null on the
|
|
119
|
+
* render that matters.
|
|
120
|
+
*
|
|
121
|
+
* ```js
|
|
122
|
+
* const handle = useWindowHandle(guestRef);
|
|
123
|
+
* // …later, in an effect, once the tree is mounted:
|
|
124
|
+
* const forTheHost = handle();
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
export function useWindowHandle(ref) {
|
|
128
|
+
return useCallback(() => windowHandleOf(ref), [ref]);
|
|
129
|
+
}
|
|
130
|
+
|
|
65
131
|
/**
|
|
66
132
|
* `windowIdOf` bound to a ref: returns a **getter**, stable across renders,
|
|
67
133
|
* the same shape `useAnchor` has. It is a getter rather than the id itself
|
|
@@ -105,8 +171,54 @@ export function topLevelWindows(app) {
|
|
|
105
171
|
);
|
|
106
172
|
}
|
|
107
173
|
|
|
174
|
+
/**
|
|
175
|
+
* The root-level `<popup>`s this connection is currently rendering — the
|
|
176
|
+
* half `topLevelWindows()` leaves out, in the order they were added.
|
|
177
|
+
*
|
|
178
|
+
* Only interesting when there are no top-level windows at all: a menu-bar
|
|
179
|
+
* app whose whole UI is a popover a tray click opens has no `<window>` for
|
|
180
|
+
* anything to belong to, and the popup *is* the top of that tree
|
|
181
|
+
* (`useTopLevelWindow`). With a window in the tree these stay out of it, for
|
|
182
|
+
* the reason they are out of `topLevelWindows()` — override-redirect is
|
|
183
|
+
* never what a dialog should be transient for.
|
|
184
|
+
*/
|
|
185
|
+
function rootLevelPopups(app) {
|
|
186
|
+
if (!app) return [];
|
|
187
|
+
return (app._rootChildren ?? []).filter(
|
|
188
|
+
(node) => node?.isWindow && node.isPopup && node.window?.id,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
108
192
|
let warnedAboutAmbiguity = false;
|
|
109
193
|
|
|
194
|
+
/**
|
|
195
|
+
* One of several candidates, on the inference `useTopLevelWindow` documents:
|
|
196
|
+
* one is exact, uniquely focused wins, and anything else is the most
|
|
197
|
+
* recently opened plus a development warning that says so.
|
|
198
|
+
*/
|
|
199
|
+
function pickOwner(candidates, what) {
|
|
200
|
+
if (candidates.length <= 1) return candidates[0] ?? null;
|
|
201
|
+
|
|
202
|
+
const focused = candidates.filter((w) => w.events?.windowFocused);
|
|
203
|
+
if (focused.length === 1) return focused[0];
|
|
204
|
+
|
|
205
|
+
// Nothing separates them. `windowFocused` also defaults to true on an
|
|
206
|
+
// ntk too old to report focus changes, so "all of them" is the same
|
|
207
|
+
// answer as "none of them" and both land here.
|
|
208
|
+
if (process.env.NODE_ENV !== 'production' && !warnedAboutAmbiguity) {
|
|
209
|
+
warnedAboutAmbiguity = true;
|
|
210
|
+
console.warn(
|
|
211
|
+
`react-x11: this tree has ${candidates.length} ${what}, none of them ` +
|
|
212
|
+
'uniquely focused, so the owner window is a guess (the most ' +
|
|
213
|
+
'recently opened). Pass the window explicitly to be exact:\n' +
|
|
214
|
+
' const win = useRef(null);\n' +
|
|
215
|
+
' const { openFile } = useFileDialog({ parentWindow: win });\n' +
|
|
216
|
+
' return <window ref={win}>…</window>;',
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return candidates[candidates.length - 1];
|
|
220
|
+
}
|
|
221
|
+
|
|
110
222
|
/**
|
|
111
223
|
* The window a component belongs to, resolved when it is read.
|
|
112
224
|
*
|
|
@@ -134,6 +246,13 @@ let warnedAboutAmbiguity = false;
|
|
|
134
246
|
* - several with nothing to separate them: the most recently opened, and a
|
|
135
247
|
* development warning naming `parentWindow` as the way to be exact. A
|
|
136
248
|
* guess that says it is guessing beats a guess that does not.
|
|
249
|
+
* - **no top-level window at all: the root-level `<popup>` that has the
|
|
250
|
+
* keyboard.** A menu-bar app is a tray item and a popover, and nothing
|
|
251
|
+
* else — there is no `<window>` for a shortcut, a file dialog or the
|
|
252
|
+
* global menu to belong to, and answering `null` made every one of them
|
|
253
|
+
* quietly do nothing (issue #616). A `grabKeyboard` popup is where the
|
|
254
|
+
* keys are by construction, so it is preferred over one that is merely
|
|
255
|
+
* up; among equals the same focus/most-recent inference applies.
|
|
137
256
|
*
|
|
138
257
|
* Returns a **ref-like object** rather than a number: the window is not
|
|
139
258
|
* realized on the first render, so a value read then would be `null` on the
|
|
@@ -146,27 +265,16 @@ export function useTopLevelWindow() {
|
|
|
146
265
|
() => ({
|
|
147
266
|
get current() {
|
|
148
267
|
const windows = topLevelWindows(app);
|
|
149
|
-
if (windows.length
|
|
150
|
-
|
|
151
|
-
const focused = windows.filter((w) => w.events?.windowFocused);
|
|
152
|
-
if (focused.length === 1) return focused[0];
|
|
268
|
+
if (windows.length) return pickOwner(windows, 'top-level windows');
|
|
153
269
|
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
'guess (the most recently opened). Pass the window explicitly ' +
|
|
163
|
-
'to be exact:\n' +
|
|
164
|
-
' const win = useRef(null);\n' +
|
|
165
|
-
' const { openFile } = useFileDialog({ parentWindow: win });\n' +
|
|
166
|
-
' return <window ref={win}>…</window>;',
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
return windows[windows.length - 1];
|
|
270
|
+
// A popup-only tree — the tray popover. Not reached while the app
|
|
271
|
+
// has a window, so nothing that already worked changes shape.
|
|
272
|
+
const popups = rootLevelPopups(app);
|
|
273
|
+
const keyboard = popups.filter((node) => node.props?.grabKeyboard);
|
|
274
|
+
return pickOwner(
|
|
275
|
+
keyboard.length ? keyboard : popups,
|
|
276
|
+
'root-level popups and no window at all',
|
|
277
|
+
);
|
|
170
278
|
},
|
|
171
279
|
}),
|
|
172
280
|
[app],
|