Root cause of the persistent split-view 花屏: xterm's WebGL addon shares
ONE TextureAtlas across terminal instances with equal config (font / size
/ theme / DPR) — acquireTextureAtlas does `if (configEquals) { ownedBy.push;
return atlas }`. Two split panes then share an atlas, so the
clearTextureAtlas calls netcatty makes to recover from glyph corruption
(on resize / DPR / font change / tab show, from #1049 and #1066) clobber
the *other* pane's rendering. That's why the earlier redraw/clear-based
recovery attempts didn't help and only bounced the garble between panes.
Disable the sharing: remove the "reuse a matching atlas" loop so every
terminal creates its own atlas. The published bundle is minified, so this
is done with a small idempotent postinstall script (a patch-package patch
would be a ~550KB unreadable blob of the whole minified line). It
string-replaces the exact loop in the CJS + ESM builds, runs after
patch-package, and warns without failing if @xterm/addon-webgl changes.
Verified: split-view WebGL no longer garbles; script is idempotent
(patched=2 → already=2) and the production build is unaffected.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
"pack:linux": "npm run build && cross-env NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --publish=never",
|
"pack:linux": "npm run build && cross-env NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --publish=never",
|
||||||
"pack:linux-x64": "npm run build && cross-env npm_config_arch=x64 NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --x64 --publish=never",
|
"pack:linux-x64": "npm run build && cross-env npm_config_arch=x64 NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --x64 --publish=never",
|
||||||
"pack:linux-arm64": "npm run build && cross-env npm_config_arch=arm64 NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --arm64 --publish=never",
|
"pack:linux-arm64": "npm run build && cross-env npm_config_arch=arm64 NODE_OPTIONS=--disable-warning=DEP0190 electron-builder --config electron-builder.config.cjs --linux --arm64 --publish=never",
|
||||||
"postinstall": "electron-builder install-app-deps && patch-package",
|
"postinstall": "electron-builder install-app-deps && patch-package && node scripts/patch-xterm-webgl-atlas.cjs",
|
||||||
"rebuild": "electron-builder install-app-deps",
|
"rebuild": "electron-builder install-app-deps",
|
||||||
"tool:cli": "node electron/cli/netcatty-tool-cli.cjs",
|
"tool:cli": "node electron/cli/netcatty-tool-cli.cjs",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
|||||||
74
scripts/patch-xterm-webgl-atlas.cjs
Normal file
74
scripts/patch-xterm-webgl-atlas.cjs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* Disable @xterm/addon-webgl's cross-terminal texture-atlas sharing.
|
||||||
|
*
|
||||||
|
* xterm's WebGL addon shares ONE TextureAtlas across terminal instances whose
|
||||||
|
* config (font / size / theme / device-pixel-ratio) is equal — see
|
||||||
|
* `acquireTextureAtlas`, which does `if (configEquals) { ownedBy.push; return
|
||||||
|
* atlas }`. In a split workspace two panes then share an atlas, so clearing or
|
||||||
|
* rebuilding it for one pane (which netcatty does on resize / DPR change / font
|
||||||
|
* change / tab show to recover from glyph corruption) corrupts the OTHER pane's
|
||||||
|
* rendering — the persistent "花屏 / garbled" report in issue #1063, most
|
||||||
|
* visible in split view where both panes stay on screen.
|
||||||
|
*
|
||||||
|
* Fix: give every terminal its own atlas by removing the "reuse a matching
|
||||||
|
* atlas" loop, so each terminal falls through to creating its own. The published
|
||||||
|
* package is minified, so we string-replace the exact loop in both the CJS and
|
||||||
|
* ESM builds. This runs from `postinstall` (after patch-package).
|
||||||
|
*
|
||||||
|
* Idempotent. If the upstream code changes (e.g. an @xterm/addon-webgl upgrade)
|
||||||
|
* the loop won't be found; we warn loudly but do not fail the install, and the
|
||||||
|
* strings below must then be refreshed for the new version.
|
||||||
|
*/
|
||||||
|
"use strict";
|
||||||
|
const fs = require("node:fs");
|
||||||
|
const path = require("node:path");
|
||||||
|
|
||||||
|
const MARKER = "/*netcatty:#1063 atlas-isolation*/";
|
||||||
|
|
||||||
|
// Exact (minified) "reuse a shared atlas" loop, per @xterm/addon-webgl@0.19.0.
|
||||||
|
const TARGETS = [
|
||||||
|
{
|
||||||
|
file: "node_modules/@xterm/addon-webgl/lib/addon-webgl.mjs",
|
||||||
|
loop: "for(let h=0;h<le.length;h++){let f=le[h];if(Mi(f.config,u))return f.ownedBy.push(i),f.atlas}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "node_modules/@xterm/addon-webgl/lib/addon-webgl.js",
|
||||||
|
loop: "for(let t=0;t<r.length;t++){const i=r[t];if((0,n.configEquals)(i.config,d))return i.ownedBy.push(e),i.atlas}",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
let patched = 0;
|
||||||
|
let already = 0;
|
||||||
|
let missing = 0;
|
||||||
|
|
||||||
|
for (const { file, loop } of TARGETS) {
|
||||||
|
const abs = path.resolve(process.cwd(), file);
|
||||||
|
let src;
|
||||||
|
try {
|
||||||
|
src = fs.readFileSync(abs, "utf8");
|
||||||
|
} catch {
|
||||||
|
console.warn(`[patch-xterm-webgl-atlas] skip (not found): ${file}`);
|
||||||
|
missing++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (src.includes(MARKER)) {
|
||||||
|
already++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!src.includes(loop)) {
|
||||||
|
console.warn(
|
||||||
|
`[patch-xterm-webgl-atlas] WARNING: atlas-sharing loop not found in ${file}. ` +
|
||||||
|
"@xterm/addon-webgl likely changed — split-view WebGL may garble again (#1063). " +
|
||||||
|
"Refresh the minified target strings in scripts/patch-xterm-webgl-atlas.cjs.",
|
||||||
|
);
|
||||||
|
missing++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fs.writeFileSync(abs, src.replace(loop, MARKER));
|
||||||
|
patched++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`[patch-xterm-webgl-atlas] atlas isolation: patched=${patched} already=${already} missing=${missing}`,
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user