react-x11 2.51.1 → 2.52.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 +87 -54
- package/docs/README.md +410 -0
- package/docs/accessibility.md +628 -0
- package/docs/appearance.md +416 -0
- package/docs/architecture/accessibility.md +470 -0
- package/docs/architecture/animation.md +707 -0
- package/docs/architecture/container-queries.md +525 -0
- package/docs/architecture/custom-layout.md +334 -0
- package/docs/architecture/drag-and-drop.md +941 -0
- package/docs/architecture/element-layer-contents.md +622 -0
- package/docs/architecture/frame-pacing.md +255 -0
- package/docs/architecture/grid-layout.md +358 -0
- package/docs/architecture/protocol-efficiency.md +357 -0
- package/docs/architecture/video.md +1149 -0
- package/docs/architecture/windowed-regions.md +854 -0
- package/docs/click-to-component.md +125 -0
- package/docs/clipboard.md +281 -0
- package/docs/components.md +1749 -0
- package/docs/dbus.md +277 -0
- package/docs/debugging.md +464 -0
- package/docs/desktop-calendar.md +284 -0
- package/docs/desktop.md +648 -0
- package/docs/devtools.md +250 -0
- package/docs/drag-and-drop.md +663 -0
- package/docs/ecosystem/animation.md +394 -0
- package/docs/ecosystem/data-fetching.md +201 -0
- package/docs/ecosystem/dev-tooling.md +436 -0
- package/docs/ecosystem/forms.md +313 -0
- package/docs/ecosystem/headless.md +219 -0
- package/docs/ecosystem/i18n.md +89 -0
- package/docs/ecosystem/icons.md +410 -0
- package/docs/ecosystem/layout.md +216 -0
- package/docs/ecosystem/routing.md +146 -0
- package/docs/ecosystem/state.md +361 -0
- package/docs/ecosystem/testing.md +500 -0
- package/docs/ecosystem/theming.md +540 -0
- package/docs/ecosystem.md +473 -0
- package/docs/elements.md +2762 -0
- package/docs/embedding.md +288 -0
- package/docs/events.md +1025 -0
- package/docs/extending.md +2242 -0
- package/docs/eyedropper.md +223 -0
- package/docs/filedialog.md +258 -0
- package/docs/frame.md +580 -0
- package/docs/gl.md +170 -0
- package/docs/globalmenu.md +278 -0
- package/docs/glx.md +137 -0
- package/docs/macos.md +3295 -0
- package/docs/notifications.md +164 -0
- package/docs/packaging.md +611 -0
- package/docs/permissions.md +148 -0
- package/docs/react-features.md +430 -0
- package/docs/remote.md +223 -0
- package/docs/scale.md +339 -0
- package/docs/security.md +132 -0
- package/docs/styling.md +1928 -0
- package/docs/system.md +599 -0
- package/docs/testing.md +514 -0
- package/docs/typescript.md +117 -0
- package/docs/uri-schemes.md +380 -0
- package/docs/wayland-backend.md +591 -0
- package/docs/wayland.md +545 -0
- package/docs/windows-embedding.md +271 -0
- package/docs/windows-gl.md +221 -0
- package/docs/windows-integrations.md +233 -0
- package/docs/windows.md +1404 -0
- package/package.json +9 -2
- 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/desktopintegration.js +2 -1
- package/src/desktopsettings.js +193 -24
- package/src/filedialog.js +3 -3
- package/src/keyboardstate.js +28 -0
- package/src/node.d.ts +10 -10
- package/src/portal.js +31 -0
- package/src/screencolor.js +5 -10
- package/src/types/system.d.ts +8 -6
- package/src/wayland/app.js +59 -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/xkb.js +137 -0
package/src/wayland/context2d.js
CHANGED
|
@@ -67,6 +67,8 @@ const MODE_RRECT = 3;
|
|
|
67
67
|
const MODE_GRADIENT = 4;
|
|
68
68
|
/** A texture read through a projection: `vParams` is (s/w, t/w, 1/w). */
|
|
69
69
|
const MODE_PROJECTIVE = 5;
|
|
70
|
+
/** A pattern: an image laid down from the fragment's device position. */
|
|
71
|
+
const MODE_PATTERN = 6;
|
|
70
72
|
|
|
71
73
|
/**
|
|
72
74
|
* The stencil buffer, shared between two uses: bit 7 is the clip (set where
|
|
@@ -132,6 +134,12 @@ uniform highp vec2 uViewport;
|
|
|
132
134
|
uniform int uGradKind;
|
|
133
135
|
uniform vec4 uGradA;
|
|
134
136
|
uniform vec4 uGradB;
|
|
137
|
+
// a pattern: device position to its pixels (the inverse transform it is
|
|
138
|
+
// filled under, a b c d and then e f), its size, and which axes repeat (1 x,
|
|
139
|
+
// 2 y) and whether its rows run bottom up (4: a render target)
|
|
140
|
+
uniform highp vec4 uPatInv;
|
|
141
|
+
uniform highp vec4 uPatOff;
|
|
142
|
+
uniform int uPatFlags;
|
|
135
143
|
|
|
136
144
|
float sdRoundBox(vec2 p, vec2 b, float r) {
|
|
137
145
|
vec2 q = abs(p) - b + r;
|
|
@@ -167,6 +175,28 @@ void main() {
|
|
|
167
175
|
// Interpolated across the quad in the plane of the screen, s/w, t/w and
|
|
168
176
|
// 1/w are each exact, and so is their quotient: a texture in perspective.
|
|
169
177
|
gl_FragColor = vColor * sampleTex(vParams.xy / vParams.z);
|
|
178
|
+
} else if (uMode == 6) {
|
|
179
|
+
// A pattern, from the fragment's device position as a gradient is, so
|
|
180
|
+
// any shape can be filled with one: back through the transform it was
|
|
181
|
+
// filled under to its own pixels, wrapped along the axes it repeats on
|
|
182
|
+
// and clear off the ones it does not, and read at the centre of the
|
|
183
|
+
// texel it lands in, so a tile drawn at its own size lands pixel for
|
|
184
|
+
// pixel — a grid's dots do not smear across a seam.
|
|
185
|
+
highp vec2 p = vec2(gl_FragCoord.x, uViewport.y - gl_FragCoord.y);
|
|
186
|
+
highp vec2 q = vec2(uPatInv.x * p.x + uPatInv.z * p.y + uPatOff.x,
|
|
187
|
+
uPatInv.y * p.x + uPatInv.w * p.y + uPatOff.y);
|
|
188
|
+
highp vec2 size = uPatOff.zw;
|
|
189
|
+
highp vec2 t = q / size;
|
|
190
|
+
float flags = float(uPatFlags);
|
|
191
|
+
if (mod(flags, 2.0) >= 1.0) t.x = fract(t.x);
|
|
192
|
+
if (mod(floor(flags / 2.0), 2.0) >= 1.0) t.y = fract(t.y);
|
|
193
|
+
if (t.x < 0.0 || t.x >= 1.0 || t.y < 0.0 || t.y >= 1.0) {
|
|
194
|
+
gl_FragColor = vec4(0.0);
|
|
195
|
+
} else {
|
|
196
|
+
t = (floor(t * size) + 0.5) / size;
|
|
197
|
+
if (flags >= 4.0) t.y = 1.0 - t.y;
|
|
198
|
+
gl_FragColor = vColor * sampleTex(t);
|
|
199
|
+
}
|
|
170
200
|
} else {
|
|
171
201
|
// Gradients are evaluated per fragment from its device position, so any
|
|
172
202
|
// shape — a cover quad over a path included — can be filled with one.
|
|
@@ -415,6 +445,43 @@ export function parseFont(font) {
|
|
|
415
445
|
*/
|
|
416
446
|
const fontIds = new WeakMap();
|
|
417
447
|
let nextFontId = 1;
|
|
448
|
+
const REPETITIONS = new Set(['repeat', 'repeat-x', 'repeat-y', 'no-repeat']);
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* A pattern: an image or a surface, and the axes it repeats along. Its
|
|
452
|
+
* texture is looked up as it is filled rather than made here, so a surface
|
|
453
|
+
* drawn on again after the pattern was made fills with what it holds then,
|
|
454
|
+
* as a canvas pattern of a canvas does.
|
|
455
|
+
*/
|
|
456
|
+
class Pattern {
|
|
457
|
+
constructor(source, repetition) {
|
|
458
|
+
this.source = source;
|
|
459
|
+
this.repetition = repetition;
|
|
460
|
+
/** its own transform, under the one it is filled with (`setTransform`) */
|
|
461
|
+
this.m = null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
setTransform(m) {
|
|
465
|
+
this.m = m
|
|
466
|
+
? [m.a ?? 1, m.b ?? 0, m.c ?? 0, m.d ?? 1, m.e ?? 0, m.f ?? 0]
|
|
467
|
+
: null;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function invert(m) {
|
|
472
|
+
const det = m[0] * m[3] - m[1] * m[2];
|
|
473
|
+
if (!det) return null;
|
|
474
|
+
const a = m[3] / det;
|
|
475
|
+
const b = -m[1] / det;
|
|
476
|
+
const c = -m[2] / det;
|
|
477
|
+
const d = m[0] / det;
|
|
478
|
+
return [a, b, c, d, -(a * m[4] + c * m[5]), -(b * m[4] + d * m[5])];
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/** A paint that is not a colour: drawn from the fragment's position. */
|
|
482
|
+
const isPaint = (style) =>
|
|
483
|
+
style instanceof Gradient || style instanceof Pattern;
|
|
484
|
+
|
|
418
485
|
function fontKey(font) {
|
|
419
486
|
let id = fontIds.get(font);
|
|
420
487
|
if (!id) fontIds.set(font, (id = `f${nextFontId++}`));
|
|
@@ -571,6 +638,9 @@ export class WaylandContext2D {
|
|
|
571
638
|
uGradKind: u('uGradKind'),
|
|
572
639
|
uGradA: u('uGradA'),
|
|
573
640
|
uGradB: u('uGradB'),
|
|
641
|
+
uPatInv: u('uPatInv'),
|
|
642
|
+
uPatOff: u('uPatOff'),
|
|
643
|
+
uPatFlags: u('uPatFlags'),
|
|
574
644
|
};
|
|
575
645
|
shared.buffer = gl.createBuffer();
|
|
576
646
|
gl.bindBuffer(gl.ARRAY_BUFFER, shared.buffer);
|
|
@@ -847,9 +917,23 @@ export class WaylandContext2D {
|
|
|
847
917
|
return new Gradient('radial', [x0, y0, r0, x1, y1, r1]);
|
|
848
918
|
}
|
|
849
919
|
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
920
|
+
/**
|
|
921
|
+
* A pattern of `image` — an ImageData, an ntk `Image`, a surface or a
|
|
922
|
+
* render target. Null for one with nothing in it, as canvas answers for an
|
|
923
|
+
* image it cannot use; `repetition` is canvas's, null and `''` meaning
|
|
924
|
+
* `'repeat'`, and anything else a `SyntaxError`.
|
|
925
|
+
*/
|
|
926
|
+
createPattern(image, repetition) {
|
|
927
|
+
const rep =
|
|
928
|
+
repetition == null || repetition === '' ? 'repeat' : String(repetition);
|
|
929
|
+
if (!REPETITIONS.has(rep)) {
|
|
930
|
+
throw new SyntaxError(
|
|
931
|
+
`createPattern: ${JSON.stringify(rep)} is not a repetition — ` +
|
|
932
|
+
"expected 'repeat', 'repeat-x', 'repeat-y' or 'no-repeat'.",
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
if (!((image?.width | 0) > 0 && (image?.height | 0) > 0)) return null;
|
|
936
|
+
return new Pattern(image, rep);
|
|
853
937
|
}
|
|
854
938
|
|
|
855
939
|
// ---- paths ------------------------------------------------------------
|
|
@@ -1088,7 +1172,7 @@ export class WaylandContext2D {
|
|
|
1088
1172
|
|
|
1089
1173
|
_rect(x, y, w, h, radius, style, strokeWidth) {
|
|
1090
1174
|
if (!(w > 0 && h > 0)) return;
|
|
1091
|
-
if (style
|
|
1175
|
+
if (isPaint(style)) {
|
|
1092
1176
|
if (radius <= 0 && strokeWidth === 0)
|
|
1093
1177
|
return this._gradientQuad(x, y, w, h, style);
|
|
1094
1178
|
// rounded/stroked gradient: stencil the shape, cover with the gradient
|
|
@@ -1226,8 +1310,40 @@ export class WaylandContext2D {
|
|
|
1226
1310
|
}
|
|
1227
1311
|
}
|
|
1228
1312
|
|
|
1313
|
+
/** Bind a gradient or a pattern for the next quads; false where there is
|
|
1314
|
+
* nothing to draw with — a pattern of an image that has gone. */
|
|
1315
|
+
_usePaint(paint) {
|
|
1316
|
+
if (paint instanceof Pattern) return this._usePattern(paint);
|
|
1317
|
+
this._useGradient(paint);
|
|
1318
|
+
return true;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/** Bind a pattern's texture, and the way from a device pixel to one of
|
|
1322
|
+
* its own, as the current batch's uniforms. */
|
|
1323
|
+
_usePattern(pat) {
|
|
1324
|
+
const src = this._textureFor(pat.source);
|
|
1325
|
+
if (!src) return false;
|
|
1326
|
+
const inv = invert(pat.m ? mul(this._m, pat.m) : this._m);
|
|
1327
|
+
if (!inv) return false;
|
|
1328
|
+
const gl = this.gl;
|
|
1329
|
+
this._flush();
|
|
1330
|
+
this._mode = MODE_PATTERN;
|
|
1331
|
+
this._texture = src.tex;
|
|
1332
|
+
this._swizzle = src.swizzle;
|
|
1333
|
+
const rep = pat.repetition;
|
|
1334
|
+
const flags =
|
|
1335
|
+
(rep === 'repeat' || rep === 'repeat-x' ? 1 : 0) +
|
|
1336
|
+
(rep === 'repeat' || rep === 'repeat-y' ? 2 : 0) +
|
|
1337
|
+
(src.flipY ? 4 : 0);
|
|
1338
|
+
gl.useProgram(this._program);
|
|
1339
|
+
gl.uniform4f(this._loc.uPatInv, inv[0], inv[1], inv[2], inv[3]);
|
|
1340
|
+
gl.uniform4f(this._loc.uPatOff, inv[4], inv[5], src.w, src.h);
|
|
1341
|
+
gl.uniform1i(this._loc.uPatFlags, flags);
|
|
1342
|
+
return true;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1229
1345
|
_gradientQuad(x, y, w, h, grad) {
|
|
1230
|
-
this.
|
|
1346
|
+
if (!this._usePaint(grad)) return;
|
|
1231
1347
|
const m = this._m;
|
|
1232
1348
|
const alpha = this.globalAlpha;
|
|
1233
1349
|
this._quad(
|
|
@@ -1277,7 +1393,7 @@ export class WaylandContext2D {
|
|
|
1277
1393
|
// only once the device has been claimed.
|
|
1278
1394
|
this._claim();
|
|
1279
1395
|
let color = null;
|
|
1280
|
-
if (!(style
|
|
1396
|
+
if (!isPaint(style)) {
|
|
1281
1397
|
color = this._color(style);
|
|
1282
1398
|
if (color[3] === 0) return;
|
|
1283
1399
|
if (this._fillCoverage(polys, rule, bounds, color)) return;
|
|
@@ -1297,15 +1413,18 @@ export class WaylandContext2D {
|
|
|
1297
1413
|
const { x, y, w, h } = bounds;
|
|
1298
1414
|
const saved = this._m;
|
|
1299
1415
|
this._m = IDENTITY;
|
|
1300
|
-
if (style
|
|
1301
|
-
this._m = saved; // gradient endpoints
|
|
1302
|
-
this.
|
|
1416
|
+
if (isPaint(style)) {
|
|
1417
|
+
this._m = saved; // a gradient's endpoints, a pattern's origin: user space
|
|
1418
|
+
const painted = this._usePaint(style);
|
|
1303
1419
|
this._m = IDENTITY;
|
|
1304
1420
|
const a = this.globalAlpha;
|
|
1421
|
+
// a pattern with nothing to draw with still covers, clear, so the
|
|
1422
|
+
// counts the winding pass left are zeroed as the cover always does
|
|
1423
|
+
if (!painted) this._setMode(MODE_SOLID, null, SWIZZLE_RGBA);
|
|
1305
1424
|
this._quad(
|
|
1306
1425
|
[x, y, x + w, y, x + w, y + h, x, y + h],
|
|
1307
1426
|
UV_UNIT,
|
|
1308
|
-
[a, a, a, a],
|
|
1427
|
+
painted ? [a, a, a, a] : CLEAR,
|
|
1309
1428
|
ZERO4,
|
|
1310
1429
|
);
|
|
1311
1430
|
} else {
|
|
@@ -127,5 +127,11 @@
|
|
|
127
127
|
"keyboard-shortcuts-inhibit-unstable-v1": [
|
|
128
128
|
"zwp_keyboard_shortcuts_inhibit_manager_v1",
|
|
129
129
|
"zwp_keyboard_shortcuts_inhibitor_v1"
|
|
130
|
+
],
|
|
131
|
+
"xdg-foreign-unstable-v2": [
|
|
132
|
+
"zxdg_exporter_v2",
|
|
133
|
+
"zxdg_importer_v2",
|
|
134
|
+
"zxdg_exported_v2",
|
|
135
|
+
"zxdg_imported_v2"
|
|
130
136
|
]
|
|
131
137
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"zxdg_exporter_v2","version":1,"requests":[{"name":"destroy","type":"destructor","args":[]},{"name":"export_toplevel","args":[{"name":"id","type":"new_id","interface":"zxdg_exported_v2"},{"name":"surface","type":"object","interface":"wl_surface"}]}],"events":[],"enums":{"error":{"entries":[{"name":"invalid_surface","value":0}]}}},{"name":"zxdg_importer_v2","version":1,"requests":[{"name":"destroy","type":"destructor","args":[]},{"name":"import_toplevel","args":[{"name":"id","type":"new_id","interface":"zxdg_imported_v2"},{"name":"handle","type":"string"}]}],"events":[],"enums":{}},{"name":"zxdg_exported_v2","version":1,"requests":[{"name":"destroy","type":"destructor","args":[]}],"events":[{"name":"handle","args":[{"name":"handle","type":"string"}]}],"enums":{}},{"name":"zxdg_imported_v2","version":1,"requests":[{"name":"destroy","type":"destructor","args":[]},{"name":"set_parent_of","args":[{"name":"surface","type":"object","interface":"wl_surface"}]}],"events":[{"name":"destroyed","args":[]}],"enums":{"error":{"entries":[{"name":"invalid_surface","value":0}]}}}]
|
package/src/wayland/seat.js
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
import { EventEmitter } from 'node:events';
|
|
28
28
|
import fs from 'node:fs';
|
|
29
|
-
import { XkbKeymap } from './xkb.js';
|
|
29
|
+
import { XkbKeymap, REAL_MODS, layoutsFromKeymap } from './xkb.js';
|
|
30
30
|
|
|
31
31
|
/** `wl_seat.capability` bits. */
|
|
32
32
|
export const SEAT_CAP = { POINTER: 1, KEYBOARD: 2, TOUCH: 4 };
|
|
@@ -185,7 +185,10 @@ export class WaylandSeat extends EventEmitter {
|
|
|
185
185
|
this.xkb = null;
|
|
186
186
|
/** X-style modifier mask: depressed | latched | locked */
|
|
187
187
|
this.mods = 0;
|
|
188
|
+
/** the locked half alone — Caps Lock and Num Lock (keyboardState) */
|
|
189
|
+
this.locked = 0;
|
|
188
190
|
this.group = 0;
|
|
191
|
+
this._layouts = null;
|
|
189
192
|
/** pointer buttons currently held, as an X state mask */
|
|
190
193
|
this.buttonMask = 0;
|
|
191
194
|
this.repeat = { rate: 25, delay: 600 };
|
|
@@ -437,6 +440,7 @@ export class WaylandSeat extends EventEmitter {
|
|
|
437
440
|
: null;
|
|
438
441
|
this.keymap = { format, size, text };
|
|
439
442
|
this.xkb = text ? XkbKeymap.parse(text) : null;
|
|
443
|
+
this._layouts = null;
|
|
440
444
|
this.emit('keymap', this.keymap, this.xkb);
|
|
441
445
|
} catch (err) {
|
|
442
446
|
this.emit(
|
|
@@ -473,6 +477,7 @@ export class WaylandSeat extends EventEmitter {
|
|
|
473
477
|
k.on('modifiers', (serial, depressed, latched, locked, group) => {
|
|
474
478
|
this.lastSerial = serial;
|
|
475
479
|
this.mods = (depressed | latched | locked) & 0xff;
|
|
480
|
+
this.locked = locked;
|
|
476
481
|
this.group = group;
|
|
477
482
|
this.emit('modifiers', {
|
|
478
483
|
mods: this.mods,
|
|
@@ -501,6 +506,28 @@ export class WaylandSeat extends EventEmitter {
|
|
|
501
506
|
});
|
|
502
507
|
}
|
|
503
508
|
|
|
509
|
+
/**
|
|
510
|
+
* The locks and the layout, in `useKeyboardState()`'s shape
|
|
511
|
+
* (src/keyboardstate.js). Caps Lock is Lock, which no keymap can move;
|
|
512
|
+
* Num Lock is wherever this keymap binds the virtual `NumLock`, with the
|
|
513
|
+
* X convention's Mod2 where it does not say.
|
|
514
|
+
*
|
|
515
|
+
* Only as fresh as the last `wl_keyboard.modifiers`, and a compositor sends
|
|
516
|
+
* those to the client with keyboard focus alone: a lock toggled while
|
|
517
|
+
* another app has the keyboard arrives when focus comes back. That is the
|
|
518
|
+
* protocol, not a shortcut — X11's StateNotify reaches every client.
|
|
519
|
+
*/
|
|
520
|
+
keyboardState() {
|
|
521
|
+
const numLock = this.xkb?.maskOf('NumLock') || REAL_MODS.Mod2;
|
|
522
|
+
this._layouts ??= Object.freeze(layoutsFromKeymap(this.keymap?.text));
|
|
523
|
+
return {
|
|
524
|
+
capsLock: Boolean(this.locked & REAL_MODS.Lock),
|
|
525
|
+
numLock: Boolean(this.locked & numLock),
|
|
526
|
+
group: this.group,
|
|
527
|
+
layouts: this._layouts,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
504
531
|
/** A key event in the shape events.js reads, decoded against the keymap. */
|
|
505
532
|
_keyEvent(type, keycode, time, serial, repeat = false) {
|
|
506
533
|
const state = XkbKeymap.stateOf(this.mods, this.group);
|
package/src/wayland/swapchain.js
CHANGED
|
@@ -191,6 +191,17 @@ export class WaylandSwapchain {
|
|
|
191
191
|
|
|
192
192
|
/** Give the chain buffers of this size, keeping its GBM surface. */
|
|
193
193
|
_resize(w, h) {
|
|
194
|
+
// The size they already are keeps them: x11-dri makes nothing again and
|
|
195
|
+
// moves no generation, so the wl_buffers stay too. Dropped, the next
|
|
196
|
+
// swap answered a buffer the compositor already had — `isNew: false`, no
|
|
197
|
+
// descriptor — that this chain no longer knew, and imported it with
|
|
198
|
+
// none. A small surface asks for this on every change of size: `slackFor`
|
|
199
|
+
// rounds both sides up to the same 128. What each buffer holds is of the
|
|
200
|
+
// old size, so all of it is owed.
|
|
201
|
+
if (w === this.allocWidth && h === this.allocHeight) {
|
|
202
|
+
for (const entry of this.buffers.values()) entry.damage = 'all';
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
194
205
|
// In place: the handle stays valid, the context keeps its GL objects and
|
|
195
206
|
// stays current. Buffers from the old generation are gone, so their
|
|
196
207
|
// `wl_buffer`s go with them — the compositor may still be showing one, so
|
|
@@ -299,6 +310,18 @@ export class WaylandSwapchain {
|
|
|
299
310
|
return true;
|
|
300
311
|
}
|
|
301
312
|
this._swapsSinceNew = 0;
|
|
313
|
+
if (!out.isNew) {
|
|
314
|
+
// GBM exported this one already, so it carries no descriptor, and the
|
|
315
|
+
// chain has no wl_buffer for it: the two have lost track of each
|
|
316
|
+
// other, which no compositor did.
|
|
317
|
+
this._fail(
|
|
318
|
+
new Error(
|
|
319
|
+
`GPU buffer ${id} came round again with no wl_buffer for it ` +
|
|
320
|
+
'(react-x11 bug: the swapchain dropped a buffer GBM still holds)',
|
|
321
|
+
),
|
|
322
|
+
);
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
302
325
|
return await this._import(id, out, generation, damage);
|
|
303
326
|
}
|
|
304
327
|
|
package/src/wayland/xkb.js
CHANGED
|
@@ -331,6 +331,14 @@ export class XkbKeymap {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/**
|
|
335
|
+
* A modifier by name, as the real mask the compositor reports it under —
|
|
336
|
+
* `'NumLock'` is whichever ModN this keymap binds it to, `'Lock'` is Lock.
|
|
337
|
+
*/
|
|
338
|
+
maskOf(name) {
|
|
339
|
+
return this._modMask(name);
|
|
340
|
+
}
|
|
341
|
+
|
|
334
342
|
/** A modifier name (real or virtual) as a real mask. */
|
|
335
343
|
_modMask(name) {
|
|
336
344
|
if (name in REAL_MODS) return REAL_MODS[name];
|
|
@@ -461,6 +469,135 @@ export class XkbKeymap {
|
|
|
461
469
|
}
|
|
462
470
|
}
|
|
463
471
|
|
|
472
|
+
/**
|
|
473
|
+
* Files the rules add to `xkb_symbols` that are not a layout: the model's
|
|
474
|
+
* extras (`inet(evdev)`) and every option's (`group(alt_shift_toggle)`,
|
|
475
|
+
* `compose(rctrl)`). Only consulted where a keymap has one group and so no
|
|
476
|
+
* group suffix to anchor on.
|
|
477
|
+
*/
|
|
478
|
+
const NOT_LAYOUTS = new Set([
|
|
479
|
+
'altwin',
|
|
480
|
+
'capslock',
|
|
481
|
+
'compose',
|
|
482
|
+
'ctrl',
|
|
483
|
+
'eurosign',
|
|
484
|
+
'fkeys',
|
|
485
|
+
'grab',
|
|
486
|
+
'group',
|
|
487
|
+
'hyper',
|
|
488
|
+
'inet',
|
|
489
|
+
'keypad',
|
|
490
|
+
'kpdl',
|
|
491
|
+
'level2',
|
|
492
|
+
'level3',
|
|
493
|
+
'level5',
|
|
494
|
+
'nbsp',
|
|
495
|
+
'parens',
|
|
496
|
+
'rupeesign',
|
|
497
|
+
'scrolllock',
|
|
498
|
+
'shift',
|
|
499
|
+
'srvr_ctrl',
|
|
500
|
+
'srvrkeys',
|
|
501
|
+
'terminate',
|
|
502
|
+
'typo',
|
|
503
|
+
]);
|
|
504
|
+
|
|
505
|
+
/** `macintosh_vndr/us(alt)` → `us`: the file, without directory or variant. */
|
|
506
|
+
const fileOf = (token) => token.replace(/\(.*$/, '').replace(/^.*\//, '');
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* The layout codes a keymap was compiled from — `['us', 'ru']`, what X11
|
|
510
|
+
* reads out of `_XKB_RULES_NAMES` and what a Wayland client has nothing
|
|
511
|
+
* like. Pure, and exported for the test.
|
|
512
|
+
*
|
|
513
|
+
* The one place a compositor's keymap still says is the **name** of its
|
|
514
|
+
* `xkb_symbols` section. libxkbcommon names it after the include string the
|
|
515
|
+
* rules built — `pc+us+ru:2+inet(evdev)+group(alt_shift_toggle)` — with both
|
|
516
|
+
* `+` and `:` written as `_`:
|
|
517
|
+
*
|
|
518
|
+
* xkb_symbols "pc_us_ru_2_inet(evdev)_group(alt_shift_toggle)" {
|
|
519
|
+
* name[1]="English (US)";
|
|
520
|
+
* name[2]="Russian";
|
|
521
|
+
*
|
|
522
|
+
* The rules put the model's file first, then the layouts in order — each one
|
|
523
|
+
* after the first followed by its group number — then the model's extras and
|
|
524
|
+
* the options. So group N is the file right before the bare `N`, group 1 is
|
|
525
|
+
* the file right before group 2, and with a single group it is the file
|
|
526
|
+
* before the first extra. `name[N]` is how many groups there are, and an
|
|
527
|
+
* answer that cannot account for every one of them is no answer: the list
|
|
528
|
+
* comes back empty, which is what "not known" means in src/keyboardstate.js.
|
|
529
|
+
*
|
|
530
|
+
* The `name[N]` strings are not the answer themselves: they are
|
|
531
|
+
* descriptions, `"Russian"`, where an indicator wants `ru` — the same reason
|
|
532
|
+
* the X11 path reads the rules rather than XKB's group names.
|
|
533
|
+
*/
|
|
534
|
+
export function layoutsFromKeymap(text) {
|
|
535
|
+
if (typeof text !== 'string') return [];
|
|
536
|
+
const at = text.search(/xkb_symbols\s+"/);
|
|
537
|
+
if (at < 0) return [];
|
|
538
|
+
const section = text.slice(at);
|
|
539
|
+
const name = /^xkb_symbols\s+"([^"]*)"/.exec(section)?.[1];
|
|
540
|
+
let groups = 0;
|
|
541
|
+
for (const m of section.matchAll(/\bname\[(?:group)?(\d+)\]\s*=/gi)) {
|
|
542
|
+
groups = Math.max(groups, Number(m[1]));
|
|
543
|
+
}
|
|
544
|
+
if (!name || !groups || groups > 4) return [];
|
|
545
|
+
|
|
546
|
+
const tokens = splitIncludes(name);
|
|
547
|
+
const layouts = [];
|
|
548
|
+
if (groups === 1) {
|
|
549
|
+
let i = tokens.findIndex((t, n) => n > 0 && NOT_LAYOUTS.has(fileOf(t)));
|
|
550
|
+
if (i < 0) i = tokens.length;
|
|
551
|
+
if (i < 2) return [];
|
|
552
|
+
layouts.push(tokens[i - 1]);
|
|
553
|
+
} else {
|
|
554
|
+
let prev = -1;
|
|
555
|
+
for (let g = 2; g <= groups; g++) {
|
|
556
|
+
const i = tokens.indexOf(String(g), prev + 1);
|
|
557
|
+
if (i < 2) return [];
|
|
558
|
+
if (g === 2) layouts.push(tokens[i - 2]);
|
|
559
|
+
layouts.push(tokens[i - 1]);
|
|
560
|
+
prev = i;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
const codes = layouts.map(fileOf);
|
|
564
|
+
if (codes.some((c) => !/^[a-z][a-z0-9-]*$/i.test(c) || NOT_LAYOUTS.has(c))) {
|
|
565
|
+
return [];
|
|
566
|
+
}
|
|
567
|
+
// `empty` is what a keymap with no layout to name is built from; reporting
|
|
568
|
+
// it would put "EMPTY" in an indicator (the X11 path drops it too)
|
|
569
|
+
return codes.every((c) => c === 'empty') ? [] : codes;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* `pc_us(dvorak)_ru(phonetic_winkeys)_2` → `['pc', 'us(dvorak)',
|
|
574
|
+
* 'ru(phonetic_winkeys)', '2']`. Split where a `_`, `+` or `:` stands outside
|
|
575
|
+
* parentheses — the raw include string spells them that way, and a
|
|
576
|
+
* variant's own underscores are inside them. Two file names have an
|
|
577
|
+
* underscore of their own, the `*_vndr/` directories and `srvr_ctrl`, and are
|
|
578
|
+
* kept whole.
|
|
579
|
+
*/
|
|
580
|
+
function splitIncludes(name) {
|
|
581
|
+
const guarded = name
|
|
582
|
+
.replace(/([a-z0-9]+)_vndr\//gi, '$1\u0001vndr/')
|
|
583
|
+
.replace(/srvr_ctrl/g, 'srvr\u0001ctrl');
|
|
584
|
+
const out = [];
|
|
585
|
+
let depth = 0;
|
|
586
|
+
let cur = '';
|
|
587
|
+
for (const ch of guarded) {
|
|
588
|
+
if (ch === '(') depth++;
|
|
589
|
+
else if (ch === ')') depth = Math.max(0, depth - 1);
|
|
590
|
+
if (depth === 0 && (ch === '_' || ch === '+' || ch === ':')) {
|
|
591
|
+
if (cur) out.push(cur);
|
|
592
|
+
cur = '';
|
|
593
|
+
} else {
|
|
594
|
+
cur += ch;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (cur) out.push(cur);
|
|
598
|
+
return out.map((t) => t.replaceAll('\u0001', '_'));
|
|
599
|
+
}
|
|
600
|
+
|
|
464
601
|
/**
|
|
465
602
|
* `XkbKSIsKeypad`: the one run of keysyms the keypad produces, `KP_Space`
|
|
466
603
|
* through `KP_Equal`. Every keypad key is in it on both of its levels — the
|