buninu-linux 0.15.58 → 0.16.59
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/NOTICE.md +46 -46
- package/README.md +22 -15
- package/README.zh.md +22 -15
- package/initramfs/buninu/CHANGELOG.md +47 -0
- package/initramfs/buninu/apps/jsgotty/LICENSES/MIT-jsmdcui.txt +22 -0
- package/initramfs/buninu/apps/jsgotty/NOTICE +5 -0
- package/initramfs/buninu/apps/jsgotty/README.en.md +21 -0
- package/initramfs/buninu/apps/jsgotty/README.md +21 -0
- package/initramfs/buninu/apps/jsgotty/client-modes.mjs +117 -0
- package/initramfs/buninu/apps/jsgotty/client.mjs +100 -10
- package/initramfs/buninu/apps/jsgotty/clipboard-powershell.js +18 -0
- package/initramfs/buninu/apps/jsgotty/clipboard.js +148 -0
- package/initramfs/buninu/apps/jsgotty/gotty.js +13 -0
- package/initramfs/buninu/apps/jsgotty/package-lock.json +2 -2
- package/initramfs/buninu/apps/jsgotty/package.json +1 -1
- package/initramfs/buninu/apps/jsgotty/static/css/index.css +14 -1
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +107 -8
- package/initramfs/buninu/apps/jsgotty/static/js/kitty.js +115 -7
- package/initramfs/buninu/apps/jsgotty/terminal-modes.js +332 -0
- package/initramfs/buninu/apps/jsgotty/tests/client-exit.test.mjs +45 -0
- package/initramfs/buninu/apps/jsgotty/tests/client-modes.test.mjs +105 -0
- package/initramfs/buninu/apps/jsgotty/tests/clipboard-browser.test.js +81 -0
- package/initramfs/buninu/apps/jsgotty/tests/clipboard.test.js +75 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-collector.cjs +55 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/zoom-debug.js +39 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.js +33 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual-touch-diagnostics.mjs +22 -0
- package/initramfs/buninu/apps/jsgotty/tests/nested-cli-pty.test.mjs +116 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes-browser.test.js +144 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.md +44 -0
- package/initramfs/buninu/apps/jsgotty/tests/reconnect-modes.test.js +166 -0
- package/initramfs/buninu/apps/jsgotty/tests/touch-drag-browser.test.js +106 -0
- package/initramfs/buninu/apps/jsgotty/tests/wheel-browser.test.js +60 -0
- package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +44 -0
- package/initramfs/buninu/apps/jsmdcui/package.json +1 -1
- package/initramfs/buninu/apps/jsmdcui/src/highlight/go-regexp.js +83 -0
- package/initramfs/buninu/apps/jsmdcui/src/highlight/highlighter.js +49 -62
- package/initramfs/buninu/apps/jsmdcui/src/highlight/parser.js +8 -5
- package/initramfs/buninu/apps/jsmdcui/tests/go-regexp.test.js +98 -0
- package/initramfs/buninu/apps/jsmdcui/tests/heading-ids.test.js +21 -16
- package/initramfs/buninu/apps/jsmdcui/tests/highlight-regions.test.js +73 -0
- package/initramfs/buninu/apps/jsmdcui/tests/id-collision.test.js +32 -9
- package/initramfs/buninu/apps/jsmdcui/tests/markdown-behavior.mjs +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/NORMAL-HTML.md +46 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/README.md +85 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/REPORT.md +328 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/compare.mjs +75 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/go.mod +5 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html-report.json +619 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/normal-html.html +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/oracle.go +49 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/report.json +2174 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/PowerShell.txt +39 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ada.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/apacheconf.txt +49 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/arduino.txt +38 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asciidoc.txt +33 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/asm.txt +77 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ats.txt +48 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/awk.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/b.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/bat.txt +39 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/c.txt +34 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/caddyfile.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cake.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/clojure.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cmake.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/coffeescript.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/colortest.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/conky.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cpp.txt +46 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crontab.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/crystal.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csharp.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/css.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/csx.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cuda.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/cython.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/d.txt +52 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dart.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/default.txt +8 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dockerfile.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/dot.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elixir.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/elm.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erb.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/erlang.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fish.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/forth.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fortran.txt +40 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/freebsd-kernel.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/fsharp.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gdscript.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gemini.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-ebuild.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gentoo-etc-portage.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-commit.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-config.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/git-rebase-todo.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gleam.txt +44 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/glsl.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gnuplot.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/go.txt +33 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/godoc.txt +9 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/golo.txt +50 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/gomod.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/graphql.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groff.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/groovy.txt +42 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haml.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hare.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/haskell.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/hc.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html4.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/html5.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ini.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/inputrc.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/java.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/javascript.txt +41 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jinja2.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/json.txt +21 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/jsonnet.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/julia.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/justfile.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/keymap.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kickstart.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kotlin.txt +30 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/kvlang.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ledger.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lfe.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lilypond.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lisp.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/log.txt +45 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/lua.txt +48 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mail.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/makefile.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/man.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/markdown.txt +21 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mc.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/meson.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/micro.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/mpdconf.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/msbuild.txt +7 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nanorc.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nftables.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nginx.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nim.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nix.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/nu.txt +92 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/objc.txt +36 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ocaml.txt +25 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/octave.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/odin.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pascal.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/patch.txt +13 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/peg.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/perl.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/php.txt +44 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pkg-config.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/po.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pony.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/pov.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-action.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-config.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/privoxy-filter.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/proto.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/prql.txt +42 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/puppet.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python2.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/python3.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/r.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/raku.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/reST.txt +18 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/renpy.txt +11 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rpmspec.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/ruby.txt +34 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/rust.txt +27 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sage.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scad.txt +23 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/scala.txt +16 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sed.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sh.txt +20 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sls.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/smalltalk.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/solidity.txt +26 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/sql.txt +35 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/stata.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/svelte.txt +10 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/swift.txt +53 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/systemd.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tcl.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/terraform.txt +28 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/tex.txt +15 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/toml.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/twig.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/typescript.txt +32 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/v.txt +47 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vala.txt +22 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/verilog.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vhdl.txt +37 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vi.txt +17 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/vue.txt +29 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xml.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/xresources.txt +14 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yaml.txt +19 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/yum.txt +12 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zig.txt +24 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zscript.txt +31 -0
- package/initramfs/buninu/apps/jsmdcui/tests/syntax-parity/samples/zsh.txt +21 -0
- package/initramfs/buninu/package.json +1 -1
- package/initramfs/lib/bunterm/images.js +5 -1
- package/initramfs/lib/bunterm/raw-protocol.js +82 -4
- package/initramfs/lib/bunterm/renderer.js +35 -16
- package/initramfs/lib/bunterm/terminal.js +11 -1
- package/initramfs/lib/fbdev.js +11 -5
- package/initramfs/lib/mapped-frame.js +28 -0
- package/initramfs/lib/raw-image.js +29 -5
- package/package.json +1 -1
- package/scripts/arch.js +5 -5
- package/scripts/arch.sh +5 -5
- package/scripts/npm-packages.md +11 -10
- package/test/bunterm-image-bytes.test.js +15 -0
- package/test/bunterm-mapped-lifetime.test.js +147 -0
- package/test/bunterm-raw-protocol.test.js +20 -0
- package/test/bunterm-renderer.test.js +9 -0
- package/test/fbdev-sync.test.js +9 -0
- package/test/fetch-npm.test.js +2 -2
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import {ownMapping, retainMapping} from "../mapped-frame.js";
|
|
1
3
|
// Buninu private extension; not a registered Kitty pixel format.
|
|
2
4
|
export const BGRA_FORMAT = 0x42475241;
|
|
3
5
|
export function preferredPixelFormat(display, littleEndian = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1) {
|
|
@@ -14,6 +16,45 @@ export function preferredPixelFormat(display, littleEndian = new Uint8Array(new
|
|
|
14
16
|
// Only the native renderer advertises these formats; browser jsgotty is unchanged.
|
|
15
17
|
export function bgraParser(Base,display) {
|
|
16
18
|
return class extends Base {
|
|
19
|
+
constructor(...args) {
|
|
20
|
+
super(...args);
|
|
21
|
+
// The parser cache and renderer have independent lifetimes. Replacing a
|
|
22
|
+
// cache entry must not detach a frame still being drawn or handled.
|
|
23
|
+
this.images = new MappedImages(this.images);
|
|
24
|
+
this.mappingReads = [];
|
|
25
|
+
this.disposed = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dispose() {
|
|
29
|
+
if (this.disposed) return;
|
|
30
|
+
this.disposed = true;
|
|
31
|
+
this.images.clear();
|
|
32
|
+
this.transfers.clear();
|
|
33
|
+
this.pending = Buffer.alloc(0);
|
|
34
|
+
for (const release of this.mappingReads) release?.();
|
|
35
|
+
this.mappingReads = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_readImageFile(control,payloadText,deleteAfterRead) {
|
|
39
|
+
// Only complete raw temporary frames are immutable producer handoffs.
|
|
40
|
+
// Ordinary files, compressed/partial transfers keep the copying reader.
|
|
41
|
+
const format=Number(control.f);
|
|
42
|
+
if(!deleteAfterRead || ![32,BGRA_FORMAT].includes(format) || control.O || control.S || control.o || typeof Bun.mmap!=="function")
|
|
43
|
+
return super._readImageFile(control,payloadText,deleteAfterRead);
|
|
44
|
+
const path=fs.realpathSync(Buffer.from(payloadText,"base64").toString("utf8"));
|
|
45
|
+
const stat=fs.statSync(path),w=Number(control.s),h=Number(control.v);
|
|
46
|
+
if(!this._isSafeTempGraphicsPath(path)||!stat.isFile()||!Number.isInteger(w)||!Number.isInteger(h)||w<1||h<1||w*h>16777216||stat.size!==w*h*4)
|
|
47
|
+
return super._readImageFile(control,payloadText,deleteAfterRead);
|
|
48
|
+
let bytes;
|
|
49
|
+
try { bytes=Bun.mmap(path,{shared:false}); }
|
|
50
|
+
catch { return super._readImageFile(control,payloadText,deleteAfterRead); }
|
|
51
|
+
// Keep the mapping through parsing, even for queries/errors that do not
|
|
52
|
+
// enter the image cache. Never truncate/reuse producer files.
|
|
53
|
+
this.mappingReads.push(ownMapping(bytes));
|
|
54
|
+
try { fs.unlinkSync(path); } catch {}
|
|
55
|
+
return Buffer.from(bytes.buffer,bytes.byteOffset,bytes.byteLength);
|
|
56
|
+
}
|
|
57
|
+
|
|
17
58
|
_resolveImageData(control,payload) {
|
|
18
59
|
const bytes=super._resolveImageData(control,payload);
|
|
19
60
|
if(Number(control.f)===BGRA_FORMAT){
|
|
@@ -24,10 +65,47 @@ export function bgraParser(Base,display) {
|
|
|
24
65
|
return bytes;
|
|
25
66
|
}
|
|
26
67
|
_parsePacket(packet,cursor) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
68
|
+
if (this.disposed) return null;
|
|
69
|
+
const previousReads = this.mappingReads;
|
|
70
|
+
this.mappingReads = [];
|
|
71
|
+
try {
|
|
72
|
+
const result=super._parsePacket(packet,cursor);
|
|
73
|
+
const release = retainMapping(result?.image?.data);
|
|
74
|
+
// The consumer uses `using` while awaiting image handling/ACKs. This
|
|
75
|
+
// protects the result even if the parser cache is replaced or closed.
|
|
76
|
+
if (release) result[Symbol.dispose] = release;
|
|
77
|
+
if(result?.kind==='query'&&Number(result.control.f)===BGRA_FORMAT&&result.responseMessage==='OK')
|
|
78
|
+
result.responseMessage='OK Buninu1 '+preferredPixelFormat(display);
|
|
79
|
+
return result;
|
|
80
|
+
} finally {
|
|
81
|
+
for (const release of this.mappingReads) release?.();
|
|
82
|
+
this.mappingReads = previousReads;
|
|
83
|
+
}
|
|
31
84
|
}
|
|
32
85
|
};
|
|
33
86
|
}
|
|
87
|
+
|
|
88
|
+
class MappedImages extends Map {
|
|
89
|
+
constructor(entries) {
|
|
90
|
+
super();
|
|
91
|
+
this.releases = new Map();
|
|
92
|
+
for (const [id, image] of entries) this.set(id, image);
|
|
93
|
+
}
|
|
94
|
+
set(id, image) {
|
|
95
|
+
const release = retainMapping(image?.data);
|
|
96
|
+
this.delete(id);
|
|
97
|
+
super.set(id, image);
|
|
98
|
+
if (release) this.releases.set(id, release);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
delete(id) {
|
|
102
|
+
this.releases.get(id)?.();
|
|
103
|
+
this.releases.delete(id);
|
|
104
|
+
return super.delete(id);
|
|
105
|
+
}
|
|
106
|
+
clear() {
|
|
107
|
+
for (const release of this.releases.values()) release();
|
|
108
|
+
this.releases.clear();
|
|
109
|
+
super.clear();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -348,15 +348,24 @@ export class Renderer {
|
|
|
348
348
|
}
|
|
349
349
|
}
|
|
350
350
|
|
|
351
|
-
drawRow(line, row) {
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
351
|
+
drawRow(line, row, hidden = []) {
|
|
352
|
+
const draw = (first, end) => {
|
|
353
|
+
if(end<=first)return;
|
|
354
|
+
this.canvas.drawRect(this.cellRect(first,row,end-first,1),this.paintFor(this.background));
|
|
355
|
+
if(!line)return;
|
|
356
|
+
for(let column=first;column<end;column++){
|
|
357
|
+
const cell=line.getCell(column);if(!cell)break;
|
|
358
|
+
this.drawCell(cell,column,row);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
// Retain two boundary cells for wide glyphs and italic overhang. Interior
|
|
362
|
+
// pixels will be replaced by the opaque image; omit their background too.
|
|
363
|
+
let column=0;
|
|
364
|
+
for(const [first,end] of hidden.map(([a,b])=>[a+2,b-2]).sort((a,b)=>a[0]-b[0])){
|
|
365
|
+
draw(column,Math.min(this.columns,first));
|
|
366
|
+
column=Math.max(column,end);
|
|
359
367
|
}
|
|
368
|
+
draw(column,this.columns);
|
|
360
369
|
}
|
|
361
370
|
|
|
362
371
|
drawCursor(line, x, y) {
|
|
@@ -425,28 +434,38 @@ export class Renderer {
|
|
|
425
434
|
const buffer = term.buffer.active;
|
|
426
435
|
const top = buffer.viewportY;
|
|
427
436
|
const dirty = [];
|
|
428
|
-
// Only proven opaque, exact raw copies may hide text work. Partial
|
|
429
|
-
// scaling, transparency and unsupported
|
|
430
|
-
const coveredRows = new Set();
|
|
437
|
+
// Only proven opaque, exact raw copies may hide text work. Partial-height
|
|
438
|
+
// rows, scaling, transparency and unsupported layouts remain unchanged.
|
|
439
|
+
const coveredRows = new Set(), coveredSpans = new Map();
|
|
440
|
+
const previousSpans = this.rawCoveredSpans ?? new Map();
|
|
431
441
|
const previouslyCovered = this.rawCoveredRows ?? new Set();
|
|
432
442
|
for (const {image, rect, source} of images) {
|
|
433
443
|
const bounds = source || [0, 0, image.width(), image.height()];
|
|
434
|
-
if (
|
|
435
|
-
|
|
444
|
+
if (!this.screen.canDrawRawImage?.(image, rect, bounds)) continue;
|
|
445
|
+
const left=Math.max(0,Math.ceil((rect[0]-this.offsetX)/this.cellWidth));
|
|
446
|
+
const right=Math.min(this.columns,Math.floor((rect[2]-this.offsetX)/this.cellWidth));
|
|
436
447
|
const first = Math.max(0, Math.ceil((rect[1] - this.offsetY) / this.cellHeight));
|
|
437
448
|
const end = Math.min(this.rows, Math.floor((rect[3] - this.offsetY) / this.cellHeight));
|
|
438
|
-
for (let row = first; row < end; row++)
|
|
449
|
+
for (let row = first; row < end; row++) {
|
|
450
|
+
if(left===0 && right===this.columns)coveredRows.add(row);
|
|
451
|
+
else if(right-left>4){
|
|
452
|
+
const spans=coveredSpans.get(row)??[];spans.push([left,right]);coveredSpans.set(row,spans);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
439
455
|
}
|
|
440
456
|
for (let row = 0; row < this.rows; row++) {
|
|
441
457
|
const line = buffer.getLine(top + row);
|
|
442
458
|
const hash = coveredRows.has(row) ? this.rowHashes[row] : line ? this.rowHash(line, this.columns) : 0;
|
|
443
459
|
if (this.dirtyAll || hash !== this.rowHashes[row] || this.forcedRows.has(row)
|
|
444
|
-
|| coveredRows.has(row) !== previouslyCovered.has(row)
|
|
460
|
+
|| coveredRows.has(row) !== previouslyCovered.has(row)
|
|
461
|
+
|| (coveredSpans.get(row)?.length??0)!==(previousSpans.get(row)?.length??0)
|
|
462
|
+
|| coveredSpans.get(row)?.some(([a,b],i)=>a!==previousSpans.get(row)?.[i]?.[0]||b!==previousSpans.get(row)?.[i]?.[1])) {
|
|
445
463
|
dirty.push(row);
|
|
446
464
|
this.rowHashes[row] = hash;
|
|
447
465
|
}
|
|
448
466
|
}
|
|
449
467
|
this.rawCoveredRows = coveredRows;
|
|
468
|
+
this.rawCoveredSpans = coveredSpans;
|
|
450
469
|
this.forcedRows.clear();
|
|
451
470
|
const localPointer = Boolean(this.screen.saveRegion && this.screen.restoreRegion);
|
|
452
471
|
const pointerChanged = this.lastPointer?.x !== pointer?.x || this.lastPointer?.y !== pointer?.y;
|
|
@@ -477,7 +496,7 @@ export class Renderer {
|
|
|
477
496
|
this.dirtyAll = false;
|
|
478
497
|
dirty.sort((a, b) => a - b);
|
|
479
498
|
for (const row of dirty) {
|
|
480
|
-
if (!coveredRows.has(row)) this.drawRow(buffer.getLine(top + row), row);
|
|
499
|
+
if (!coveredRows.has(row)) this.drawRow(buffer.getLine(top + row), row, coveredSpans.get(row));
|
|
481
500
|
}
|
|
482
501
|
if (cursor?.visible && !coveredRows.has(cursor.y) && dirty.includes(cursor.y)) {
|
|
483
502
|
this.drawCursor(buffer.getLine(top + cursor.y), cursor.x, cursor.y);
|
|
@@ -93,6 +93,7 @@ export const createSession = ({
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
let frameTimer = null;
|
|
96
|
+
let lastPaintTime = -Infinity;
|
|
96
97
|
let active = true;
|
|
97
98
|
let blinkOn = true;
|
|
98
99
|
const cursorHidden = () => Boolean(term._core?.coreService?.isCursorHidden);
|
|
@@ -108,6 +109,7 @@ export const createSession = ({
|
|
|
108
109
|
if (frameTimer) clearTimeout(frameTimer);
|
|
109
110
|
frameTimer = null;
|
|
110
111
|
if (!active) return false;
|
|
112
|
+
lastPaintTime = performance.now();
|
|
111
113
|
const buffer = term.buffer.active;
|
|
112
114
|
// cursorY counts from the buffer's base, so a scrolled-back viewport
|
|
113
115
|
// moves the cursor down the screen and eventually off it.
|
|
@@ -129,7 +131,9 @@ export const createSession = ({
|
|
|
129
131
|
};
|
|
130
132
|
const requestFrame = () => {
|
|
131
133
|
if (!active) return;
|
|
132
|
-
|
|
134
|
+
// Limit paint starts, rather than adding 16 ms after every arriving frame.
|
|
135
|
+
// Slow rendering already consumes the interval; keep one pending paint.
|
|
136
|
+
frameTimer ??= setTimeout(paint, Math.max(0, 16 - (performance.now() - lastPaintTime)));
|
|
133
137
|
};
|
|
134
138
|
const changed = () => { renderer.invalidate(); requestFrame(); };
|
|
135
139
|
const selection = createSelection(term, renderer, changed);
|
|
@@ -198,8 +202,10 @@ export const createSession = ({
|
|
|
198
202
|
let queue = Promise.resolve();
|
|
199
203
|
const pump = (chunk) => {
|
|
200
204
|
queue = queue.then(async () => {
|
|
205
|
+
if (closed) return;
|
|
201
206
|
const { events } = kitty.consume(chunk);
|
|
202
207
|
for (const event of events) {
|
|
208
|
+
if (closed) return;
|
|
203
209
|
if (event.kind === "plain") {
|
|
204
210
|
// A selection is a snapshot of the displayed cells. Drop it before
|
|
205
211
|
// output scrolls/replaces those cells, rather than copying stale rows.
|
|
@@ -210,8 +216,11 @@ export const createSession = ({
|
|
|
210
216
|
const buffer = term.buffer.active;
|
|
211
217
|
const graphic = kitty.parsePacket(event.packet, { row: buffer.cursorY + 1, col: buffer.cursorX + 1 });
|
|
212
218
|
if (!graphic) continue;
|
|
219
|
+
using frame = graphic[Symbol.dispose] ? graphic : null;
|
|
213
220
|
const motion = await images.handle(graphic);
|
|
221
|
+
if (closed) return;
|
|
214
222
|
if (motion) await writeTerminal(term, motion);
|
|
223
|
+
if (closed) return;
|
|
215
224
|
const ack = kitty.acknowledge(graphic);
|
|
216
225
|
if (ack) backend.write(Buffer.from(ack, "utf8"));
|
|
217
226
|
}
|
|
@@ -386,6 +395,7 @@ export const createSession = ({
|
|
|
386
395
|
keyboardProtocol = null;
|
|
387
396
|
pointerDevice?.close();
|
|
388
397
|
pointerDevice = null;
|
|
398
|
+
kitty.dispose();
|
|
389
399
|
images.delete();
|
|
390
400
|
renderer.delete();
|
|
391
401
|
term.dispose();
|
package/initramfs/lib/fbdev.js
CHANGED
|
@@ -283,11 +283,17 @@ export const syncFramebuffer = (display, native = {
|
|
|
283
283
|
|
|
284
284
|
export const flush = (display) => {
|
|
285
285
|
const visibleBytes = display.width * display.bytesPerPixel;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
286
|
+
if (display.xOffset === 0 && display.stride === visibleBytes) {
|
|
287
|
+
// Contiguous visible rows need one bulk copy, not one JS call per row.
|
|
288
|
+
display.memory.set(display.pixels.subarray(0, display.height * display.stride),
|
|
289
|
+
display.yOffset * display.stride);
|
|
290
|
+
} else {
|
|
291
|
+
for (let y = 0; y < display.height; y++) {
|
|
292
|
+
const row = display.pixels.subarray(y * display.stride, y * display.stride + visibleBytes);
|
|
293
|
+
const position = (y + display.yOffset) * display.stride
|
|
294
|
+
+ display.xOffset * display.bytesPerPixel;
|
|
295
|
+
display.memory.set(row, position);
|
|
296
|
+
}
|
|
291
297
|
}
|
|
292
298
|
syncFramebuffer(display);
|
|
293
299
|
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Only buffers created by our mmap reader may be detached. Ordinary buffers,
|
|
2
|
+
// pooled Buffer slabs and callers' pixel arrays retain their normal lifetime.
|
|
3
|
+
const mappings = new WeakMap();
|
|
4
|
+
|
|
5
|
+
export function ownMapping(bytes) {
|
|
6
|
+
const buffer = bytes.buffer;
|
|
7
|
+
if (mappings.has(buffer)) throw Error('mapping already owned');
|
|
8
|
+
mappings.set(buffer, {refs: 0});
|
|
9
|
+
return retainMapping(bytes);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function retainMapping(bytes) {
|
|
13
|
+
const buffer = bytes?.buffer;
|
|
14
|
+
const state = buffer && mappings.get(buffer);
|
|
15
|
+
if (!state) return null;
|
|
16
|
+
state.refs++;
|
|
17
|
+
let released = false;
|
|
18
|
+
return () => {
|
|
19
|
+
if (released) return;
|
|
20
|
+
released = true;
|
|
21
|
+
if (--state.refs) return;
|
|
22
|
+
mappings.delete(buffer);
|
|
23
|
+
// Transfer to zero bytes runs Bun's mmap deallocator immediately, rather
|
|
24
|
+
// than batching many full frames into a later GC pause. Older runtimes or
|
|
25
|
+
// non-detachable buffers safely fall back to ordinary GC reclamation.
|
|
26
|
+
try { buffer.transfer?.(0); } catch {}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import {retainMapping} from "./mapped-frame.js";
|
|
1
2
|
// Retain immutable raw pixels until an operation actually needs a Skia Image.
|
|
2
3
|
// The native image store owns these handles; encoded images remain ordinary Skia images.
|
|
3
4
|
const rawImages = new WeakMap();
|
|
5
|
+
const littleEndian = new Uint8Array(new Uint32Array([1]).buffer)[0] === 1;
|
|
4
6
|
export const rawImageData = image => rawImages.get(image);
|
|
5
7
|
export const skiaImage = image => rawImages.get(image)?.materialize() ?? image;
|
|
6
8
|
|
|
7
9
|
export function createRawImage(CanvasKit, pixels, width, height, colorType) {
|
|
8
10
|
let decoded = null, closed = false;
|
|
11
|
+
const release = retainMapping(pixels);
|
|
9
12
|
const data = {
|
|
10
13
|
pixels, width, height, colorType,
|
|
11
14
|
opaque: undefined,
|
|
@@ -25,9 +28,13 @@ export function createRawImage(CanvasKit, pixels, width, height, colorType) {
|
|
|
25
28
|
delete() {
|
|
26
29
|
if (closed) return;
|
|
27
30
|
closed = true;
|
|
28
|
-
decoded?.delete();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
try { decoded?.delete(); }
|
|
32
|
+
finally {
|
|
33
|
+
decoded = null;
|
|
34
|
+
data.pixels = pixels = null;
|
|
35
|
+
rawImages.delete(image);
|
|
36
|
+
release?.();
|
|
37
|
+
}
|
|
31
38
|
},
|
|
32
39
|
};
|
|
33
40
|
rawImages.set(image, data);
|
|
@@ -39,8 +46,25 @@ export function rawImageOpaque(data) {
|
|
|
39
46
|
if (data.opaque === undefined) {
|
|
40
47
|
const pixels = data.pixels;
|
|
41
48
|
data.opaque = true;
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
if (littleEndian && pixels.byteOffset % 4 === 0 && pixels.byteLength % 4 === 0) {
|
|
50
|
+
const words = new Uint32Array(pixels.buffer, pixels.byteOffset, pixels.byteLength / 4);
|
|
51
|
+
let i = 0;
|
|
52
|
+
// All eight alpha bytes must be 255 for their AND to stay 255. Group
|
|
53
|
+
// the common opaque case without skipping any pixel or reading a tail.
|
|
54
|
+
for (; i + 7 < words.length; i += 8) {
|
|
55
|
+
if (((words[i] & words[i + 1] & words[i + 2] & words[i + 3]
|
|
56
|
+
& words[i + 4] & words[i + 5] & words[i + 6] & words[i + 7]) >>> 24) !== 255) {
|
|
57
|
+
data.opaque = false;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (; data.opaque && i < words.length; i++) {
|
|
62
|
+
if ((words[i] >>> 24) !== 255) { data.opaque = false; break; }
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
for (let i = 3; i < pixels.length; i += 4) {
|
|
66
|
+
if (pixels[i] !== 255) { data.opaque = false; break; }
|
|
67
|
+
}
|
|
44
68
|
}
|
|
45
69
|
}
|
|
46
70
|
return data.opaque;
|
package/package.json
CHANGED
package/scripts/arch.js
CHANGED
|
@@ -16,8 +16,8 @@ export const rootDir = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
|
16
16
|
|
|
17
17
|
const pins = {
|
|
18
18
|
x86_64: {
|
|
19
|
-
linuxVirtSha256: "
|
|
20
|
-
linuxLtsSha256: "
|
|
19
|
+
linuxVirtSha256: "73ad4774ca26c196103a00e2fb8d28ed02de4c08c11fad96e903da03dcb085f4",
|
|
20
|
+
linuxLtsSha256: "ae0a669a08c90ef971996f81f1bcb6f7ca8211ad62624ad8c5b57412c3f1def8",
|
|
21
21
|
muslSha256: "573712e2f49c15bfc20a2699f204acdfc74c772722b15e7353d768057fae0e71",
|
|
22
22
|
stubSha256: "8e64a5a3afee5f930e6e6716be726dc6d405530ac7f8fa5be6251dae68671ec9",
|
|
23
23
|
libstdcppSha256: "14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6",
|
|
@@ -32,8 +32,8 @@ const pins = {
|
|
|
32
32
|
serialMajor: 4,
|
|
33
33
|
},
|
|
34
34
|
aarch64: {
|
|
35
|
-
linuxVirtSha256: "
|
|
36
|
-
linuxLtsSha256: "
|
|
35
|
+
linuxVirtSha256: "89b68fc32d61a1483ab0d9db78b5a06144ac115fe2ee8102cb4ad7987f46d524",
|
|
36
|
+
linuxLtsSha256: "f6387970517083d5f08abacbf81baf1d70e1baa8fe4c2783469f45bb0751075c",
|
|
37
37
|
muslSha256: "5e9674b7f41152fe2119093b5cb4c13eaaadb19c2d5422b2d7267913e663ee6e",
|
|
38
38
|
stubSha256: "a1823d2d7082db555d528f82c1809f276c818aeb5b40f198f4565f104e055c38",
|
|
39
39
|
libstdcppSha256: "2302e766d4e4926038ec166ecb85837ee884576115236ddb565e3a5fca4a11d7",
|
|
@@ -68,7 +68,7 @@ export const config = ({
|
|
|
68
68
|
// The Alpine repository keeps only the newest build of a package, so a pin
|
|
69
69
|
// stops downloading when Alpine bumps it; the hashes then fail loudly.
|
|
70
70
|
const alpineBase = `${mirror}/v3.24/main/${arch}`;
|
|
71
|
-
const kernelVersion = "6.18.
|
|
71
|
+
const kernelVersion = "6.18.59";
|
|
72
72
|
const kernelRelease = `${kernelVersion}-0-${flavor}`;
|
|
73
73
|
const bunVersion = "1.4.2";
|
|
74
74
|
const downloadsDir = join(rootDir, "downloads", arch);
|
package/scripts/arch.sh
CHANGED
|
@@ -44,7 +44,7 @@ fi
|
|
|
44
44
|
alpine_mirror=${ALPINE_MIRROR:-https://dl-cdn.alpinelinux.org/alpine}
|
|
45
45
|
alpine_base="$alpine_mirror/v3.24/main/$arch"
|
|
46
46
|
|
|
47
|
-
kernel_version=6.18.
|
|
47
|
+
kernel_version=6.18.59
|
|
48
48
|
kernel_release="$kernel_version-0-$linux_flavor"
|
|
49
49
|
linux_package="linux-$linux_flavor-$kernel_version-r0.apk"
|
|
50
50
|
musl_package=musl-1.2.6-r2.apk
|
|
@@ -63,8 +63,8 @@ musl_libc="libc.musl-$arch.so.1"
|
|
|
63
63
|
|
|
64
64
|
case $arch in
|
|
65
65
|
x86_64)
|
|
66
|
-
linux_virt_sha256=
|
|
67
|
-
linux_lts_sha256=
|
|
66
|
+
linux_virt_sha256=73ad4774ca26c196103a00e2fb8d28ed02de4c08c11fad96e903da03dcb085f4
|
|
67
|
+
linux_lts_sha256=ae0a669a08c90ef971996f81f1bcb6f7ca8211ad62624ad8c5b57412c3f1def8
|
|
68
68
|
musl_sha256=573712e2f49c15bfc20a2699f204acdfc74c772722b15e7353d768057fae0e71
|
|
69
69
|
stub_sha256=8e64a5a3afee5f930e6e6716be726dc6d405530ac7f8fa5be6251dae68671ec9
|
|
70
70
|
libstdcpp_sha256=14c987b556f5385a5db18376e788c75f37d85321b8dc1920d926ea7daac1d6f6
|
|
@@ -82,8 +82,8 @@ case $arch in
|
|
|
82
82
|
serial_major=4
|
|
83
83
|
;;
|
|
84
84
|
aarch64)
|
|
85
|
-
linux_virt_sha256=
|
|
86
|
-
linux_lts_sha256=
|
|
85
|
+
linux_virt_sha256=89b68fc32d61a1483ab0d9db78b5a06144ac115fe2ee8102cb4ad7987f46d524
|
|
86
|
+
linux_lts_sha256=f6387970517083d5f08abacbf81baf1d70e1baa8fe4c2783469f45bb0751075c
|
|
87
87
|
musl_sha256=5e9674b7f41152fe2119093b5cb4c13eaaadb19c2d5422b2d7267913e663ee6e
|
|
88
88
|
stub_sha256=a1823d2d7082db555d528f82c1809f276c818aeb5b40f198f4565f104e055c38
|
|
89
89
|
libstdcpp_sha256=2302e766d4e4926038ec166ecb85837ee884576115236ddb565e3a5fca4a11d7
|
package/scripts/npm-packages.md
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# npm packages
|
|
2
2
|
# Latest versions checked on 2026-10-11; SHA-256 pins verified against registry SHA-512 integrity.
|
|
3
|
-
# buninu-browser 0.
|
|
3
|
+
# buninu-browser 0.6.0, buninu-land 0.3.1, and casty 1.5.1 verified on 2026-10-29.
|
|
4
|
+
# jspulse 0.2.3 verified on 2026-10-22.
|
|
4
5
|
# jspinyin 0.2.0 verified on 2026-10-22.
|
|
5
6
|
|
|
6
|
-
"buninu-browser@0.
|
|
7
|
-
- https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.
|
|
8
|
-
- sha256:
|
|
7
|
+
"buninu-browser@0.6.0":
|
|
8
|
+
- https://registry.npmjs.org/buninu-browser/-/buninu-browser-0.6.0.tgz
|
|
9
|
+
- sha256:81929d903239f615f6265e0b8bf52cc36e34c622486c517bc58d70078c28f72c
|
|
9
10
|
|
|
10
|
-
"buninu-land@0.
|
|
11
|
-
- https://registry.npmjs.org/buninu-land/-/buninu-land-0.
|
|
12
|
-
- sha256:
|
|
11
|
+
"buninu-land@0.3.1":
|
|
12
|
+
- https://registry.npmjs.org/buninu-land/-/buninu-land-0.3.1.tgz
|
|
13
|
+
- sha256:6a2df4b52e12bd20972b195903b72a8a1fdefb62a60391c5f090764283731d22
|
|
13
14
|
|
|
14
|
-
"@drxiaozhi/casty@1.
|
|
15
|
-
- https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.
|
|
16
|
-
- sha256:
|
|
15
|
+
"@drxiaozhi/casty@1.5.1":
|
|
16
|
+
- https://registry.npmjs.org/@drxiaozhi/casty/-/casty-1.5.1.tgz
|
|
17
|
+
- sha256:821b9ca5e726afa691c4761d9ec917f862bb1a14592a7e590713c5fc735f71fa
|
|
17
18
|
|
|
18
19
|
"@drxiaozhi/jspulse@0.2.3":
|
|
19
20
|
- https://registry.npmjs.org/@drxiaozhi/jspulse/-/jspulse-0.2.3.tgz
|
|
@@ -53,3 +53,18 @@ test('raw RGBA bytes pass directly to CanvasKit', async () => {
|
|
|
53
53
|
expect(received).toBe(image.data);
|
|
54
54
|
expect(received.equals(pixels)).toBe(true);
|
|
55
55
|
});
|
|
56
|
+
|
|
57
|
+
import {rawImageOpaque} from '../initramfs/lib/raw-image.js';
|
|
58
|
+
test('raw opacity checks every pixel in aligned and unaligned byte views', () => {
|
|
59
|
+
for (const length of [0, 1, 7, 8, 9, 15, 16, 17, 33]) for (const offset of [0, 1, 4]) {
|
|
60
|
+
const storage = new Uint8Array(length * 4 + offset);
|
|
61
|
+
const pixels = storage.subarray(offset);
|
|
62
|
+
for (let i = 3; i < pixels.length; i += 4) pixels[i] = 255;
|
|
63
|
+
expect(rawImageOpaque({pixels})).toBe(true);
|
|
64
|
+
for (let i = 3; i < pixels.length; i += 4) {
|
|
65
|
+
pixels[i] = 254;
|
|
66
|
+
expect(rawImageOpaque({pixels})).toBe(false);
|
|
67
|
+
pixels[i] = 255;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import {test, expect} from 'bun:test';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import {tmpdir} from 'node:os';
|
|
4
|
+
import {join} from 'node:path';
|
|
5
|
+
import {createRequire} from 'node:module';
|
|
6
|
+
import {bgraParser, BGRA_FORMAT} from '../initramfs/lib/bunterm/raw-protocol.js';
|
|
7
|
+
import {ownMapping, retainMapping} from '../initramfs/lib/mapped-frame.js';
|
|
8
|
+
import {createRawImage, rawImageData, skiaImage} from '../initramfs/lib/raw-image.js';
|
|
9
|
+
import {ImageStore} from '../initramfs/lib/bunterm/images.js';
|
|
10
|
+
const {KittyGraphicsParser} = createRequire(import.meta.url)('../initramfs/buninu/apps/jsgotty/gotty.js');
|
|
11
|
+
const CK = {ColorType:{RGBA_8888:1,BGRA_8888:2}};
|
|
12
|
+
function fixture() {
|
|
13
|
+
const dir=fs.mkdtempSync(join(tmpdir(),'tty-graphics-protocol-lifetime-'));
|
|
14
|
+
const parser=new (bgraParser(KittyGraphicsParser))({binaryData:true});
|
|
15
|
+
const term={buffer:{active:{type:'alternate',baseY:0,cursorY:0,cursorX:0,viewportY:0}}};
|
|
16
|
+
const store=new ImageStore({CanvasKit:CK,term,renderer:{cellWidth:1,cellHeight:1,offsetX:0,offsetY:0,rows:10,invalidateRows(){}}});
|
|
17
|
+
const parse=(control,payload='')=>parser.parsePacket(Buffer.from(control+';'+payload),{row:1,col:1});
|
|
18
|
+
let serial=0;
|
|
19
|
+
return {parser,store,parse,frame(id=1,format=BGRA_FORMAT){
|
|
20
|
+
const path=join(dir,'frame'+serial++);fs.writeFileSync(path,Buffer.from([1,2,3,255]));
|
|
21
|
+
return parse(`a=T,t=t,f=${format},s=1,v=1,i=${id},C=1`,Buffer.from(path).toString('base64'));
|
|
22
|
+
},close(){parser.dispose();store.delete();fs.rmSync(dir,{recursive:true,force:true});}};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('replacement protects parser results and renderer until their independent owners release',async()=>{
|
|
26
|
+
const f=fixture();
|
|
27
|
+
try {
|
|
28
|
+
using first=f.frame();const bytes=first.image.data;
|
|
29
|
+
await f.store.handle(first);
|
|
30
|
+
using second=f.frame();
|
|
31
|
+
expect(bytes.byteLength).toBe(4); // parser replaced, renderer/result still own old frame
|
|
32
|
+
await f.store.handle(second);
|
|
33
|
+
expect(bytes.byteLength).toBe(4); // result is still borrowed
|
|
34
|
+
first[Symbol.dispose]();first[Symbol.dispose]();
|
|
35
|
+
expect(bytes.byteLength).toBe(0);
|
|
36
|
+
const next=second.image.data;
|
|
37
|
+
f.parser.dispose();second[Symbol.dispose]();
|
|
38
|
+
expect(next.byteLength).toBe(4); // renderer outlives parser
|
|
39
|
+
f.store.delete();expect(next.byteLength).toBe(0);
|
|
40
|
+
} finally {f.close();}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('multiple placements and lowercase deletion retain pixels for re-placement',async()=>{
|
|
44
|
+
const f=fixture();
|
|
45
|
+
try {
|
|
46
|
+
using first=f.frame(1,32);const bytes=first.image.data;
|
|
47
|
+
await f.store.handle(first);first[Symbol.dispose]();
|
|
48
|
+
using second=f.parse('a=p,i=1,p=two,C=1');await f.store.handle(second);second[Symbol.dispose]();
|
|
49
|
+
expect(f.store.visible()).toHaveLength(2);
|
|
50
|
+
await f.store.handle(f.parse('a=d,d=i,i=1,p=two'));
|
|
51
|
+
expect(f.store.visible()).toHaveLength(1);expect(bytes.byteLength).toBe(4);
|
|
52
|
+
await f.store.handle(f.parse('a=d,d=a'));
|
|
53
|
+
expect(f.store.visible()).toHaveLength(0);expect(bytes.byteLength).toBe(4);
|
|
54
|
+
using again=f.parse('a=p,i=1,C=1');await f.store.handle(again);again[Symbol.dispose]();
|
|
55
|
+
expect(rawImageData(f.store.visible()[0].image).pixels[0]).toBe(1);
|
|
56
|
+
await f.store.handle(f.parse('a=d,d=A'));
|
|
57
|
+
expect(bytes.byteLength).toBe(0);
|
|
58
|
+
} finally {f.close();}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('queued frames survive replacement and parser shutdown before consumer handles them',async()=>{
|
|
62
|
+
const f=fixture();
|
|
63
|
+
try {
|
|
64
|
+
using first=f.frame();using second=f.frame();f.parser.dispose();
|
|
65
|
+
await f.store.handle(first);first[Symbol.dispose]();
|
|
66
|
+
expect(first.image.data.byteLength).toBe(4);
|
|
67
|
+
await f.store.handle(second);expect(first.image.data.byteLength).toBe(0);
|
|
68
|
+
second[Symbol.dispose]();f.store.delete();expect(second.image.data.byteLength).toBe(0);
|
|
69
|
+
expect(f.frame()).toBeNull();
|
|
70
|
+
} finally {f.close();}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('closing while decode awaits cannot repopulate the image store',async()=>{
|
|
74
|
+
const f=fixture();
|
|
75
|
+
try {
|
|
76
|
+
using first=f.frame();const handling=f.store.handle(first);
|
|
77
|
+
f.store.delete();f.parser.dispose();await handling;
|
|
78
|
+
expect(f.store.images.size).toBe(0);expect(f.store.placements.size).toBe(0);
|
|
79
|
+
first[Symbol.dispose]();expect(first.image.data.byteLength).toBe(0);
|
|
80
|
+
} finally {f.close();}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test('using releases a borrowed frame when processing throws; ordinary buffers never detach',()=>{
|
|
84
|
+
const f=fixture();let bytes;
|
|
85
|
+
try {
|
|
86
|
+
expect(()=>{using first=f.frame();bytes=first.image.data;f.parser.dispose();throw Error('test');}).toThrow('test');
|
|
87
|
+
expect(bytes.byteLength).toBe(0);
|
|
88
|
+
const ordinary=Buffer.alloc(4);const image=createRawImage(CK,ordinary,1,1,1);
|
|
89
|
+
image.delete();expect(ordinary.byteLength).toBe(4);
|
|
90
|
+
} finally {f.close();}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('failed/unsupported transfer falls back to GC without breaking cleanup',()=>{
|
|
94
|
+
for(const transfer of [undefined,()=>{throw Error('not detachable');}]){
|
|
95
|
+
const bytes=new Uint8Array(4);Object.defineProperty(bytes.buffer,'transfer',{value:transfer});
|
|
96
|
+
const release=ownMapping(bytes),other=retainMapping(bytes);
|
|
97
|
+
release();expect(bytes.byteLength).toBe(4);
|
|
98
|
+
expect(()=>{other();other();release();}).not.toThrow();
|
|
99
|
+
expect(bytes.byteLength).toBe(4);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('query/error mappings are released even though they never enter the image cache',()=>{
|
|
104
|
+
const f=fixture();const mapped=[];
|
|
105
|
+
const read=f.parser._readImageFile.bind(f.parser);
|
|
106
|
+
f.parser._readImageFile=(...args)=>{const bytes=read(...args);mapped.push(bytes);return bytes;};
|
|
107
|
+
try {
|
|
108
|
+
// A query validates its file then drops its data. Exercise the same packet
|
|
109
|
+
// cleanup on an exception after the read, before the cache can retain it.
|
|
110
|
+
for(const fail of [false,true]){
|
|
111
|
+
const path=join(tmpdir(),'tty-graphics-protocol-query-'+process.pid);fs.writeFileSync(path,Buffer.from([1,2,3,255]));
|
|
112
|
+
const resolve=f.parser._resolveImageData.bind(f.parser);
|
|
113
|
+
if(fail)f.parser._resolveImageData=(...args)=>{resolve(...args);throw Error('validation failed');};
|
|
114
|
+
const result=f.parse(`a=q,t=t,f=${BGRA_FORMAT},s=1,v=1,i=7`,Buffer.from(path).toString('base64'));
|
|
115
|
+
expect(result.responseMessage).toStartWith(fail?'ERROR:':'OK');
|
|
116
|
+
expect(mapped.at(-1).byteLength).toBe(0);
|
|
117
|
+
}
|
|
118
|
+
expect(f.parser.images.size).toBe(0);
|
|
119
|
+
} finally {f.close();}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('materialized Skia images and mapped bytes both release even if Skia cleanup throws',()=>{
|
|
123
|
+
const bytes=new Uint8Array(4),owner=ownMapping(bytes);
|
|
124
|
+
const image=createRawImage({...CK,AlphaType:{Unpremul:1},ColorSpace:{SRGB:1},MakeImage(){return {delete(){throw Error('Skia cleanup');}};}},bytes,1,1,1);
|
|
125
|
+
owner();skiaImage(image);
|
|
126
|
+
expect(()=>image.delete()).toThrow('Skia cleanup');expect(bytes.byteLength).toBe(0);
|
|
127
|
+
expect(()=>image.delete()).not.toThrow();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test('repeated mapped replacements unmap promptly without waiting for a GC',async()=>{
|
|
131
|
+
const f=fixture();let previous;
|
|
132
|
+
try {
|
|
133
|
+
for(let i=0;i<200;i++){
|
|
134
|
+
using frame=f.frame();await f.store.handle(frame);
|
|
135
|
+
if(previous)expect(previous.byteLength).toBe(0);
|
|
136
|
+
previous=frame.image.data;
|
|
137
|
+
expect(previous[3]).toBe(255);
|
|
138
|
+
}
|
|
139
|
+
f.parser.dispose();f.store.delete();expect(previous.byteLength).toBe(0);
|
|
140
|
+
if(process.platform==='linux'){
|
|
141
|
+
const maps=fs.readFileSync('/proc/self/maps','utf8');
|
|
142
|
+
// Files were unlinked at read time; lingering mappings would still have
|
|
143
|
+
// their original name followed by '(deleted)' here.
|
|
144
|
+
expect(maps.split('\n').filter(line=>line.includes('tty-graphics-protocol-lifetime-'))).toHaveLength(0);
|
|
145
|
+
}
|
|
146
|
+
} finally {f.close();}
|
|
147
|
+
});
|
|
@@ -17,3 +17,23 @@ test('native preference follows framebuffer bitfields; browser parser remains un
|
|
|
17
17
|
const bad=fb();bad.fields.red.msbRight=1;
|
|
18
18
|
expect(preferredPixelFormat(bad)).toBe('png');expect(preferredPixelFormat(fb(),false)).toBe('png');
|
|
19
19
|
});
|
|
20
|
+
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import {tmpdir} from 'node:os';
|
|
23
|
+
import {join} from 'node:path';
|
|
24
|
+
test('raw temporary mappings survive unlink; regular and partial files retain copying behavior',()=>{
|
|
25
|
+
const dir=fs.mkdtempSync(join(tmpdir(),'tty-graphics-protocol-mmap-'));
|
|
26
|
+
const parser=new (bgraParser(KittyGraphicsParser,fb()))({binaryData:true});
|
|
27
|
+
const bytes=Buffer.from([1,2,3,255,4,5,6,255]);
|
|
28
|
+
try{
|
|
29
|
+
for(const format of [32,BGRA_FORMAT]){
|
|
30
|
+
const path=join(dir,'frame');fs.writeFileSync(path,bytes);
|
|
31
|
+
const got=parser._readImageFile({f:String(format),s:'2',v:'1'},Buffer.from(path).toString('base64'),true);
|
|
32
|
+
expect(fs.existsSync(path)).toBe(false);expect(got).toEqual(bytes);
|
|
33
|
+
}
|
|
34
|
+
const path=join(dir,'regular');fs.writeFileSync(path,bytes);
|
|
35
|
+
const payload=Buffer.from(path).toString('base64'),control={f:String(BGRA_FORMAT),s:'2',v:'1'};
|
|
36
|
+
expect(parser._readImageFile(control,payload,false)).toEqual(bytes);expect(fs.existsSync(path)).toBe(true);
|
|
37
|
+
expect(parser._readImageFile({...control,O:'4',S:'4'},payload,true)).toEqual(bytes.subarray(4));
|
|
38
|
+
}finally{parser.dispose();fs.rmSync(dir,{recursive:true,force:true});}
|
|
39
|
+
});
|