buninu-linux 0.13.56 → 0.15.58
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/LICENSES/linux-firmware/LICENCE.nvidia +131 -0
- package/LICENSES/linux-firmware/LICENSE.amdgpu +51 -0
- package/LICENSES/linux-firmware/LICENSE.amdisp +31 -0
- package/LICENSES/linux-firmware/LICENSE.i915 +39 -0
- package/LICENSES/linux-firmware/LICENSE.radeon +51 -0
- package/LICENSES/linux-firmware/LICENSE.xe +39 -0
- package/LICENSES/linux-firmware/WHENCE +10280 -0
- package/NOTICE.md +64 -46
- package/README.md +410 -96
- package/README.zh.md +2034 -0
- package/build-image.sh +10 -1
- package/build-uki.sh +3 -1
- package/fetch-alpine.sh +15 -1
- package/graphics.md +31 -0
- package/index.js +28 -11
- package/initramfs/bin/buninu-browser +4 -1
- package/initramfs/bin/buninu-land +1 -1
- package/initramfs/bin/bunterm +3 -3
- package/initramfs/bin/chroot +13 -0
- package/initramfs/bin/chvt +11 -0
- package/initramfs/bin/dhclient +8 -0
- package/initramfs/bin/mount +4 -0
- package/initramfs/bin/ping +8 -0
- package/initramfs/bin/umount +6 -0
- package/initramfs/bin/xdg-open +33 -0
- package/initramfs/buninu/CHANGELOG.md +19 -0
- package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +4 -0
- package/initramfs/buninu/apps/bunmsh/package.json +1 -1
- package/initramfs/buninu/apps/bunmsh/src/main.js +1 -1
- package/initramfs/buninu/apps/jsgotty/README.en.md +40 -0
- package/initramfs/buninu/apps/jsgotty/README.md +40 -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/index.html +1 -0
- package/initramfs/buninu/apps/jsgotty/static/js/gotty.js +82 -17
- package/initramfs/buninu/apps/jsgotty/static/js/kitty-keyboard.js +193 -0
- package/initramfs/buninu/apps/jsgotty/tests/android-alt.test.js +115 -0
- package/initramfs/buninu/apps/jsgotty/tests/arrow-modifiers.test.js +80 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard-browser.test.js +53 -0
- package/initramfs/buninu/apps/jsgotty/tests/kitty-keyboard.test.js +102 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/README.md +20 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-collector.cjs +55 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/keyboard-debug.js +74 -0
- package/initramfs/buninu/apps/jsgotty/tests/manual/kitty-keyboard.js +80 -0
- package/initramfs/buninu/package.json +1 -1
- package/initramfs/init.js +27 -10
- package/initramfs/lib/bunterm/clipboard.js +14 -0
- package/initramfs/lib/bunterm/commands.js +37 -0
- package/initramfs/lib/bunterm/fps.js +38 -0
- package/initramfs/lib/bunterm/images.js +9 -5
- package/initramfs/lib/bunterm/input.js +58 -15
- package/initramfs/lib/bunterm/keyboard.js +62 -0
- package/initramfs/lib/bunterm/keycodes.js +104 -0
- package/initramfs/lib/bunterm/kitty-keyboard.cjs +195 -0
- package/initramfs/lib/bunterm/raw-protocol.js +33 -0
- package/initramfs/lib/bunterm/renderer.js +90 -21
- package/initramfs/lib/bunterm/selection.js +60 -0
- package/initramfs/lib/bunterm/terminal.js +147 -12
- package/initramfs/lib/bunterm/vt-menu.js +181 -0
- package/initramfs/lib/bunterm/vt.js +27 -1
- package/initramfs/lib/canvas.js +99 -0
- package/initramfs/lib/chvt.js +31 -0
- package/initramfs/lib/dhclient.js +135 -0
- package/initramfs/lib/dhcp-start.js +85 -0
- package/initramfs/lib/dhcp-transport.js +44 -0
- package/initramfs/lib/dhcp.js +124 -0
- package/initramfs/lib/dlopen.js +58 -11
- package/initramfs/lib/fbdev.js +21 -3
- package/initramfs/lib/gpu.js +77 -0
- package/initramfs/lib/ping.js +110 -0
- package/initramfs/lib/raw-image.js +47 -0
- package/initramfs/lib/xdg-open.js +42 -0
- package/initramfs/usr/share/doc/buninu-linux/bunterm.md +86 -12
- package/initramfs/usr/share/doc/buninu-linux/chvt.md +34 -0
- package/initramfs/usr/share/doc/buninu-linux/dhclient.md +82 -0
- package/initramfs/usr/share/doc/buninu-linux/ping.md +91 -0
- package/initramfs/usr/share/doc/buninu-linux/xdg-open.md +43 -0
- package/package.json +2 -2
- package/scripts/arch.js +7 -5
- package/scripts/arch.sh +5 -5
- package/scripts/build.js +16 -3
- package/scripts/fetch-gpu.js +105 -0
- package/scripts/fetch.js +11 -1
- package/scripts/gpu-firmware.json +22 -0
- package/scripts/modules.js +5 -1
- package/scripts/npm-packages.md +17 -17
- package/test/bunterm-fps.test.js +17 -0
- package/test/bunterm-keyboard.test.js +116 -0
- package/test/bunterm-next-vt.test.js +9 -0
- package/test/bunterm-raw-protocol.test.js +19 -0
- package/test/bunterm-renderer.test.js +158 -0
- package/test/bunterm-selection.test.js +38 -0
- package/test/bunterm-vt-menu.test.js +178 -0
- package/test/chroot-bind.test.js +40 -2
- package/test/chvt.test.js +46 -0
- package/test/dhcp-start.test.js +67 -0
- package/test/dhcp.test.js +99 -0
- package/test/fbdev-sync.test.js +33 -0
- package/test/fetch-npm.test.js +1 -1
- package/test/gpu.test.js +155 -0
- package/test/image-size.test.js +17 -0
- package/test/libc.test.js +41 -0
- package/test/manual-bunterm-image-paths.js +31 -0
- package/test/mount-libc-lifetime.test.js +50 -0
- package/test/no-gpu.test.js +43 -0
- package/test/ping.test.js +80 -0
- package/test/readme-cli.test.js +29 -0
- package/test/stage-npm.test.js +11 -4
- package/test/xdg-open.test.js +55 -0
package/build-image.sh
CHANGED
|
@@ -25,7 +25,16 @@ done
|
|
|
25
25
|
temporary_esp="$(mktemp --tmpdir=. .esp.XXXXXX.img)"
|
|
26
26
|
trap 'rm -f "$temporary_esp"' EXIT
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# Match scripts/build.js: payload + 1/16 + 4 MiB, rounded to 64 MiB.
|
|
29
|
+
payload_bytes=$(stat -c %s "vda/EFI/BOOT/$efi_boot_name")
|
|
30
|
+
if ((payload_bytes > 4294967295)); then
|
|
31
|
+
echo "error: EFI payload exceeds the FAT32 single-file limit" >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
unit=$((64 * 1024 * 1024))
|
|
35
|
+
disk_bytes=$(((payload_bytes + (payload_bytes + 15) / 16 + 4 * 1024 * 1024 + unit - 1) / unit * unit))
|
|
36
|
+
((disk_bytes >= 128 * 1024 * 1024)) || disk_bytes=$((128 * 1024 * 1024))
|
|
37
|
+
disk_sectors=$((disk_bytes / 512))
|
|
29
38
|
esp_start=2048
|
|
30
39
|
esp_end=$((disk_sectors - 34))
|
|
31
40
|
esp_sectors=$((esp_end - esp_start + 1))
|
package/build-uki.sh
CHANGED
|
@@ -5,7 +5,9 @@ cd "$(dirname "$0")"
|
|
|
5
5
|
|
|
6
6
|
. ./scripts/arch.sh
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
gpu_js=$(command -v bun || command -v node)
|
|
9
|
+
|
|
10
|
+
if [ ! -s "$kernel_image" ] || [ ! -s "$kernel_dir/$efi_stub" ] || [ ! -s "$modules_root/modules.dep" ] || ! "$gpu_js" scripts/fetch-gpu.js --check || \
|
|
9
11
|
{ [ "${REAL_MACHINE:-}" = 1 ] && \
|
|
10
12
|
{ [ ! -s "$modules_root/kernel/drivers/hid/usbhid/usbhid.ko" ] || \
|
|
11
13
|
[ ! -s "$modules_root/kernel/drivers/acpi/battery.ko" ]; }; }; then
|
package/fetch-alpine.sh
CHANGED
|
@@ -33,6 +33,9 @@ depfile="$(mktemp)"
|
|
|
33
33
|
selected="$(mktemp)"
|
|
34
34
|
trap 'rm -f "$depfile" "$selected" "$selected.names"' EXIT
|
|
35
35
|
extract "$linux_package" "lib/modules/$kernel_release/modules.dep" > "$depfile"
|
|
36
|
+
# Recreate the generated module tree so removed selections cannot linger.
|
|
37
|
+
rm -rf "$modules_root"
|
|
38
|
+
mkdir -p "$modules_root"
|
|
36
39
|
extract "$linux_package" "lib/modules/$kernel_release/modules.builtin" > "$modules_root/modules.builtin"
|
|
37
40
|
|
|
38
41
|
select_module() {
|
|
@@ -63,8 +66,15 @@ select_optional_module() {
|
|
|
63
66
|
# Network: virtio-net for QEMU (init.js loads these at boot). virtio_pci is
|
|
64
67
|
# the PCI transport for every virtio device, disk included; linux-virt builds
|
|
65
68
|
# it in, linux-lts has it as a module that nothing in modules.dep pulls in.
|
|
66
|
-
for module in failover net_failover virtio_net; do select_module "$module"; done
|
|
69
|
+
for module in failover net_failover virtio_net af_packet; do select_module "$module"; done
|
|
67
70
|
select_optional_module virtio_pci
|
|
71
|
+
# GPU modules are packaged only; init and cfg.all leave them unloaded.
|
|
72
|
+
if [ "${BUNINU_NO_GPU:-}" != 1 ]; then
|
|
73
|
+
for module in virtio-gpu vmwgfx; do select_optional_module "$module"; done
|
|
74
|
+
if [ "$arch" = x86_64 ]; then
|
|
75
|
+
for module in i915 xe amdgpu radeon nouveau; do select_optional_module "$module"; done
|
|
76
|
+
fi
|
|
77
|
+
fi
|
|
68
78
|
# Storage: what /bin/mount loads on demand. ext4 brings jbd2, mbcache and
|
|
69
79
|
# crc16; FAT, exFAT and NTFS ask the kernel for the NLS tables at mount
|
|
70
80
|
# time, which has no /sbin/modprobe to answer, so mount preloads them.
|
|
@@ -145,4 +155,8 @@ extract "$linux_package" "lib/modules/$kernel_release/modules.alias" \
|
|
|
145
155
|
|
|
146
156
|
chmod 0755 "$native_dir/lib/$musl_loader"
|
|
147
157
|
|
|
158
|
+
# Share the verified firmware extraction and cache marker with the JS build.
|
|
159
|
+
js=$(command -v bun || command -v node)
|
|
160
|
+
"$js" scripts/fetch-gpu.js
|
|
161
|
+
|
|
148
162
|
echo "Fetched Alpine $arch linux-$linux_flavor kernel, $(wc -l < "$selected") modules, musl, and the $efi_stub UKI stub."
|
package/graphics.md
CHANGED
|
@@ -158,6 +158,28 @@ screen and xterm.js's `CoreMouseService` → PTY. Terminal
|
|
|
158
158
|
replies (cursor position, device attributes, kitty acks) go from the
|
|
159
159
|
emulator straight to the PTY.
|
|
160
160
|
|
|
161
|
+
## Raw image fast path
|
|
162
|
+
|
|
163
|
+
Native bunterm can negotiate BGRA (`f=1111970369`, a private Buninu format) or
|
|
164
|
+
standard RGBA (`f=32`) with Casty. `images.js` retains the received raw bytes;
|
|
165
|
+
`raw-image.js` creates a Skia image only when a fallback operation needs one.
|
|
166
|
+
For an opaque, integer-aligned 1:1 placement matching the framebuffer channel
|
|
167
|
+
order, `canvas.js` copies these bytes into the existing raster back buffer.
|
|
168
|
+
This avoids `MakeImage` followed by `readPixels`, but still performs the raster
|
|
169
|
+
copy and the final framebuffer flush. Opacity is checked from the actual alpha
|
|
170
|
+
bytes once per image, not assumed from its format or filename.
|
|
171
|
+
|
|
172
|
+
Rows completely covered by such an image skip text hashing and painting.
|
|
173
|
+
Partially covered rows keep normal rendering, and uncovering a row forces its
|
|
174
|
+
current text to repaint. Pointer and menu overlays are still drawn afterwards.
|
|
175
|
+
Scaling, transparency, other channel orders and 16-bit framebuffers retain
|
|
176
|
+
CanvasKit rendering. Encoded PNG/JPEG handling is unchanged.
|
|
177
|
+
|
|
178
|
+
原始像素只有在通道排列一致、1:1 整數座標且經檢查確定不透明時,才直接複製到
|
|
179
|
+
繪圖緩衝區,省去先建立 Skia Image 再讀回像素的步驟。完全被圖片覆蓋的文字列
|
|
180
|
+
略過雜湊與重畫;圖片移開後會重畫當下的文字。縮放、透明、格式不符時仍使用
|
|
181
|
+
CanvasKit,最後寫入 framebuffer 的複製也仍然存在。
|
|
182
|
+
|
|
161
183
|
## Sizes
|
|
162
184
|
|
|
163
185
|
| Component | Raw | In the gzip initramfs |
|
|
@@ -222,6 +244,15 @@ at once and draws on VT 2; and keys sent with the monitor's `sendkey` reach
|
|
|
222
244
|
it: `showimg /buninu/icon.png` places the kitty image, and the prompt's
|
|
223
245
|
colour emoji and the pointer render, all checked with `screendump`.
|
|
224
246
|
|
|
247
|
+
## Optional client GPU acceleration
|
|
248
|
+
|
|
249
|
+
Client GPU rendering can be enabled separately with the PID 1 REPL's `cfg.gpu`,
|
|
250
|
+
before opening framebuffer clients. It loads matched GPU drivers and exposes
|
|
251
|
+
render nodes, while this CanvasKit/bunterm stack remains software-rendered.
|
|
252
|
+
Boot and `cfg.all` do not load those GPU drivers. See the README's
|
|
253
|
+
[optional client GPU acceleration](README.md#optional-client-gpu-acceleration-cfggpu)
|
|
254
|
+
for firmware, device permissions and framebuffer handoff constraints.
|
|
255
|
+
|
|
225
256
|
## Not done yet
|
|
226
257
|
|
|
227
258
|
- Selection and scrollback viewing in `bunterm` (the pointer itself works,
|
package/index.js
CHANGED
|
@@ -79,7 +79,7 @@ function usage() {
|
|
|
79
79
|
|
|
80
80
|
Usage:
|
|
81
81
|
${pkg.name} [-f] [-b] [-r] [--arch ARCH] [--docker] [--export] [--linux-lts]
|
|
82
|
-
[--real] [-- qemu arguments]
|
|
82
|
+
[--real] [--no-gpu] [-- qemu arguments]
|
|
83
83
|
${pkg.name} --check [FILE...]
|
|
84
84
|
|
|
85
85
|
Stages (always run in this order, whichever you pick):
|
|
@@ -107,6 +107,8 @@ Stages (always run in this order, whichever you pick):
|
|
|
107
107
|
primary console, and include USB xHCI/HID keyboard modules
|
|
108
108
|
(x86_64 only)
|
|
109
109
|
|
|
110
|
+
--no-gpu omit the added GPU modules/firmware; retain the original framebuffer path
|
|
111
|
+
|
|
110
112
|
--check [FILE...]
|
|
111
113
|
print content checksums of the build outputs that ignore
|
|
112
114
|
mtimes, timestamps and GUIDs, and exit (check_build.js; the
|
|
@@ -116,6 +118,8 @@ Stages (always run in this order, whichever you pick):
|
|
|
116
118
|
-h, --help show this
|
|
117
119
|
-V, --version show name, version, runtime and platform
|
|
118
120
|
--readme render README.md in the terminal and exit
|
|
121
|
+
--readme-zh, --docs
|
|
122
|
+
render README.zh.md in the terminal and exit
|
|
119
123
|
|
|
120
124
|
Short flags combine: -fbr is the whole pipeline, -br is what an edit to
|
|
121
125
|
initramfs/init.js needs, -r just boots what is already there. Anything after
|
|
@@ -134,11 +138,12 @@ reported before anything starts.
|
|
|
134
138
|
`;
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
function parse(argv) {
|
|
141
|
+
async function parse(argv) {
|
|
138
142
|
const selected = new Set();
|
|
139
143
|
const passthrough = [];
|
|
140
144
|
let linuxLts = false;
|
|
141
145
|
let real = false;
|
|
146
|
+
let noGpu = false;
|
|
142
147
|
let exportImage = false;
|
|
143
148
|
let docker = false;
|
|
144
149
|
// BUNINU_ARCH is what scripts/arch.sh reads; --arch overrides it.
|
|
@@ -165,12 +170,17 @@ function parse(argv) {
|
|
|
165
170
|
printVersion();
|
|
166
171
|
process.exit(0);
|
|
167
172
|
} else if (argument === "--readme") {
|
|
168
|
-
printReadme();
|
|
173
|
+
await printReadme();
|
|
174
|
+
process.exit(0);
|
|
175
|
+
} else if (argument === "--readme-zh" || argument === "--docs") {
|
|
176
|
+
await printReadme("README.zh.md");
|
|
169
177
|
process.exit(0);
|
|
170
178
|
} else if (argument === "--check") {
|
|
171
179
|
runCheck(argv.slice(index + 1));
|
|
172
180
|
} else if (argument === "--linux-lts") {
|
|
173
181
|
linuxLts = true;
|
|
182
|
+
} else if (argument === "--no-gpu") {
|
|
183
|
+
noGpu = true;
|
|
174
184
|
} else if (argument === "--real") {
|
|
175
185
|
real = true;
|
|
176
186
|
} else if (argument === "--export") {
|
|
@@ -197,8 +207,8 @@ function parse(argv) {
|
|
|
197
207
|
if (passthrough.length > 0 && !selected.has(stages[2])) {
|
|
198
208
|
fail("arguments after -- only make sense with -r/--run");
|
|
199
209
|
}
|
|
200
|
-
if ((linuxLts || real) && !selected.has(stages[0]) && !selected.has(stages[1])) {
|
|
201
|
-
fail("--linux-lts/--real only make sense with -f/--fetch or -b/--build");
|
|
210
|
+
if ((linuxLts || real || noGpu) && !selected.has(stages[0]) && !selected.has(stages[1])) {
|
|
211
|
+
fail("--linux-lts/--real/--no-gpu only make sense with -f/--fetch or -b/--build");
|
|
202
212
|
}
|
|
203
213
|
if (exportImage && !selected.has(stages[1])) {
|
|
204
214
|
fail("--export requires -b/--build (use -fb --export for a clean build)");
|
|
@@ -206,7 +216,7 @@ function parse(argv) {
|
|
|
206
216
|
if (real && arch === "aarch64") {
|
|
207
217
|
fail("--real builds an x86_64 PC image; it cannot be combined with --arch aarch64");
|
|
208
218
|
}
|
|
209
|
-
return { selected, passthrough, linuxLts, real, exportImage, docker, arch };
|
|
219
|
+
return { selected, passthrough, linuxLts, real, noGpu, exportImage, docker, arch };
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
// Same shape as Buninu's --version. The runtime line tells you whether this
|
|
@@ -222,8 +232,8 @@ function printVersion() {
|
|
|
222
232
|
|
|
223
233
|
// Same as Buninu's --readme: Bun renders the Markdown to ANSI with clickable
|
|
224
234
|
// links. Under node there is no renderer, so the file is printed as-is.
|
|
225
|
-
function printReadme() {
|
|
226
|
-
const path = resolve(rootDir,
|
|
235
|
+
function printReadme(filename = "README.md") {
|
|
236
|
+
const path = resolve(rootDir, filename);
|
|
227
237
|
let markdown;
|
|
228
238
|
try {
|
|
229
239
|
markdown = readFileSync(path, "utf8");
|
|
@@ -234,7 +244,12 @@ function printReadme() {
|
|
|
234
244
|
typeof Bun !== "undefined" && typeof Bun.markdown?.ansi === "function"
|
|
235
245
|
? Bun.markdown.ansi(markdown, { hyperlinks: true })
|
|
236
246
|
: markdown;
|
|
237
|
-
|
|
247
|
+
// Wait for the whole document to flush before parse() exits. Large README
|
|
248
|
+
// output can otherwise be truncated when stdout is a pipe.
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
process.stdout.write(rendered.endsWith("\n") ? rendered : rendered + "\n",
|
|
251
|
+
(error) => error ? reject(error) : resolve());
|
|
252
|
+
});
|
|
238
253
|
}
|
|
239
254
|
|
|
240
255
|
// --check hands everything after it to check_build.js, under the same
|
|
@@ -316,7 +331,7 @@ function dockerImage() {
|
|
|
316
331
|
// into the bind-mounted checkout stays owned by that user; fakeroot provides
|
|
317
332
|
// the root ownership the initramfs records.
|
|
318
333
|
function dockerRun(script, env) {
|
|
319
|
-
const passEnv = ["BUNINU_ARCH", "LINUX_FLAVOR", "REAL_MACHINE", "ALPINE_MIRROR", "BUNINU_JS_UKI", "BUNINU_JS_CPIO"]
|
|
334
|
+
const passEnv = ["BUNINU_ARCH", "LINUX_FLAVOR", "REAL_MACHINE", "BUNINU_NO_GPU", "ALPINE_MIRROR", "BUNINU_JS_UKI", "BUNINU_JS_CPIO"]
|
|
320
335
|
.filter((name) => env[name] !== undefined)
|
|
321
336
|
.flatMap((name) => ["--env", `${name}=${env[name]}`]);
|
|
322
337
|
const user = typeof process.getuid === "function"
|
|
@@ -344,7 +359,7 @@ function runScript(stage, script, args = [], env = process.env) {
|
|
|
344
359
|
}
|
|
345
360
|
}
|
|
346
361
|
|
|
347
|
-
const { selected, passthrough, linuxLts, real, exportImage, docker, arch } = parse(process.argv.slice(2));
|
|
362
|
+
const { selected, passthrough, linuxLts, real, noGpu, exportImage, docker, arch } = await parse(process.argv.slice(2));
|
|
348
363
|
const ordered = stages.filter((stage) => selected.has(stage));
|
|
349
364
|
const useDocker = docker || process.platform === "darwin";
|
|
350
365
|
const buildArch = arch ?? "x86_64";
|
|
@@ -357,6 +372,7 @@ const stageEnv = {
|
|
|
357
372
|
...(arch && { BUNINU_ARCH: arch }),
|
|
358
373
|
...((linuxLts || real) && { LINUX_FLAVOR: "lts" }),
|
|
359
374
|
...(real && { REAL_MACHINE: "1" }),
|
|
375
|
+
...(noGpu && { BUNINU_NO_GPU: "1" }),
|
|
360
376
|
};
|
|
361
377
|
|
|
362
378
|
// The arch.js config for the JS stages, from the same settings as stageEnv.
|
|
@@ -364,6 +380,7 @@ const jsConfig = async (env) => (await import("./scripts/arch.js")).config({
|
|
|
364
380
|
arch: env.BUNINU_ARCH,
|
|
365
381
|
flavor: env.LINUX_FLAVOR,
|
|
366
382
|
real: env.REAL_MACHINE === "1",
|
|
383
|
+
noGpu: env.BUNINU_NO_GPU === "1",
|
|
367
384
|
mirror: env.ALPINE_MIRROR,
|
|
368
385
|
});
|
|
369
386
|
|
|
@@ -14,9 +14,12 @@ const { chooseFrontend } = await import(pathToFileURL(entry).href);
|
|
|
14
14
|
const args = process.argv.slice(2);
|
|
15
15
|
const choice = chooseFrontend(args);
|
|
16
16
|
const casty = choice.frontend === "casty";
|
|
17
|
+
const forwarded = casty ? choice.args : args;
|
|
18
|
+
// Preserve Casty's separator when Bun consumes the first one after the script.
|
|
19
|
+
const runtimeArgs = forwarded[0] === "--" ? ["--", ...forwarded] : forwarded;
|
|
17
20
|
const child = Bun.spawn({
|
|
18
21
|
cmd: [process.execPath, casty ? join(modules, "@drxiaozhi/casty/index.js") : entry,
|
|
19
|
-
...
|
|
22
|
+
...runtimeArgs],
|
|
20
23
|
env: casty ? { ...process.env, CASTY_BROWSER: join(browser, "buninu-browser.js") } : process.env,
|
|
21
24
|
stdio: ["inherit", "inherit", "inherit"],
|
|
22
25
|
});
|
|
@@ -14,7 +14,7 @@ const { chooseFrontend } = await import(pathToFileURL(entry).href);
|
|
|
14
14
|
const args = process.argv.slice(2);
|
|
15
15
|
const choice = chooseFrontend(args);
|
|
16
16
|
const casty = choice.frontend === "casty";
|
|
17
|
-
const forwarded = casty ? choice.args : args;
|
|
17
|
+
const forwarded = casty ? ["--no-shortcuts", ...choice.args] : args;
|
|
18
18
|
// Bun consumes a leading separator after the script path; preserve Casty's
|
|
19
19
|
// own separator when --casty is followed directly by -- engine flags.
|
|
20
20
|
const runtimeArgs = forwarded[0] === "--" ? ["--", ...forwarded] : forwarded;
|
package/initramfs/bin/bunterm
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { showDocument } from "../lib/document.js";
|
|
5
5
|
import { runTerminal } from "../lib/bunterm/terminal.js";
|
|
6
6
|
import { activeConsole, openConsole } from "../lib/fbdev.js";
|
|
7
|
-
import { virtualConsoleNumber } from "../lib/bunterm/vt.js";
|
|
7
|
+
import { virtualConsoleNumber, nextVirtualTerminal } from "../lib/bunterm/vt.js";
|
|
8
8
|
import { closeSync, openSync, readFileSync } from "node:fs";
|
|
9
9
|
|
|
10
10
|
const CANONICAL_BUNTERM = "/bin/bunterm";
|
|
@@ -79,7 +79,7 @@ const main = async () => {
|
|
|
79
79
|
} finally {
|
|
80
80
|
probe.close();
|
|
81
81
|
}
|
|
82
|
-
if (!options.console) options.console = `/dev/tty${
|
|
82
|
+
if (!options.console) options.console = `/dev/tty${nextVirtualTerminal()}`;
|
|
83
83
|
|
|
84
84
|
// Every live bunterm converges on one unambiguous /proc/<pid>/cmdline:
|
|
85
85
|
// /bin/bunterm /bin/bunterm /dev/ttyN [options...]
|
|
@@ -94,7 +94,7 @@ const main = async () => {
|
|
|
94
94
|
Object.entries(process.env).filter((entry) => typeof entry[1] === "string"),
|
|
95
95
|
);
|
|
96
96
|
const targetConsoleNumber = virtualConsoleNumber(options.console);
|
|
97
|
-
if (
|
|
97
|
+
if (targetConsoleNumber !== activeConsoleNumber) {
|
|
98
98
|
const consoleFd = openSync(options.console, "r+");
|
|
99
99
|
try {
|
|
100
100
|
const child = Bun.spawn(
|
package/initramfs/bin/chroot
CHANGED
|
@@ -53,6 +53,19 @@ try { bindings = resolveBindings(options.binds); }
|
|
|
53
53
|
catch (error) { fail(`invalid bind: ${error.message}`); }
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
// dlopen.js resolves each symbol lazily. Bind everything needed after mounts
|
|
57
|
+
// or chroot before the host libc path can disappear from this process's root.
|
|
58
|
+
// Even --keep-mounts needs umount2 when setup fails and must be rolled back.
|
|
59
|
+
try {
|
|
60
|
+
void libc.symbols.chroot;
|
|
61
|
+
if (options.mount || bindings.length) {
|
|
62
|
+
void libc.symbols.mount;
|
|
63
|
+
void libc.symbols.umount2;
|
|
64
|
+
}
|
|
65
|
+
errno();
|
|
66
|
+
void libc.symbols.strerror;
|
|
67
|
+
} catch (error) { fail(error.message); }
|
|
68
|
+
|
|
56
69
|
const report = (message) => { if (options.verbose) console.error(`chroot: ${message}`); };
|
|
57
70
|
|
|
58
71
|
// ---------------------------------------------------------------- mounts --
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import { parseChvt, switchVt } from "../lib/chvt.js";
|
|
3
|
+
import { showDocument } from "../lib/document.js";
|
|
4
|
+
try {
|
|
5
|
+
const options = parseChvt(process.argv.slice(2));
|
|
6
|
+
if (options.help) await showDocument("chvt");
|
|
7
|
+
else await switchVt(options.number);
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.error(`chvt: ${error.message}`);
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import {parseDhclient,runDhclient} from '../lib/dhclient.js';
|
|
3
|
+
import {showDocument} from '../lib/document.js';
|
|
4
|
+
try {
|
|
5
|
+
const options=parseDhclient(Bun.argv.slice(2));
|
|
6
|
+
if(options.help)await showDocument('dhclient');
|
|
7
|
+
else process.exitCode=await runDhclient(options);
|
|
8
|
+
}catch(error){console.error(`dhclient: ${error.message}`);process.exitCode=1;}
|
package/initramfs/bin/mount
CHANGED
|
@@ -325,6 +325,10 @@ const doMount = (source, target, type, flags, data, { verbose, fake }) => {
|
|
|
325
325
|
console.log(`mount(${source}, ${target}, ${type ?? "(none)"}, [${words.join(",")}], "${data}")`);
|
|
326
326
|
}
|
|
327
327
|
if (fake) return;
|
|
328
|
+
// A bind over /lib can hide libc before the read-only remount pass fails.
|
|
329
|
+
void libc.symbols.mount;
|
|
330
|
+
errno();
|
|
331
|
+
void libc.symbols.strerror;
|
|
328
332
|
const dataBytes = data ? cString(data) : null;
|
|
329
333
|
const typeBytes = type ? cString(type) : null;
|
|
330
334
|
const result = libc.symbols.mount(
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import {parsePing,runPing} from '../lib/ping.js';
|
|
3
|
+
import {showDocument} from '../lib/document.js';
|
|
4
|
+
try {
|
|
5
|
+
const options=parsePing(Bun.argv.slice(2));
|
|
6
|
+
if(options.help)await showDocument('ping');
|
|
7
|
+
else process.exitCode=await runPing(options);
|
|
8
|
+
} catch(error) {console.error(`ping: ${error.message}`);process.exitCode=2;}
|
package/initramfs/bin/umount
CHANGED
|
@@ -58,6 +58,12 @@ const unmount = (target) => {
|
|
|
58
58
|
return false;
|
|
59
59
|
};
|
|
60
60
|
|
|
61
|
+
// A previous target may contain libc; later failures still need strerror.
|
|
62
|
+
if (targets.length) {
|
|
63
|
+
try { void libc.symbols.umount2; errno(); void libc.symbols.strerror; }
|
|
64
|
+
catch (error) { console.error(`umount: ${error.message}`); process.exit(1); }
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
let status = 0;
|
|
62
68
|
for (const target of targets) {
|
|
63
69
|
const path = target.length > 1 ? target.replace(/\/+$/, "") : target;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bun
|
|
2
|
+
import { browserTarget, parseOpenArgs, openError, openExitCode, releaseVersion } from "../lib/xdg-open.js";
|
|
3
|
+
import { showDocument } from "../lib/document.js";
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const options = parseOpenArgs(process.argv.slice(2));
|
|
7
|
+
if (options.help) {
|
|
8
|
+
await showDocument("xdg-open");
|
|
9
|
+
} else if (options.version) {
|
|
10
|
+
// Both image builders already generate this from buninu-linux's version.
|
|
11
|
+
const release = new URL("../usr/lib/os-release", import.meta.url);
|
|
12
|
+
let version;
|
|
13
|
+
if (await Bun.file(release).exists()) version = releaseVersion(await Bun.file(release).text());
|
|
14
|
+
else version = (await Bun.file(new URL("../../package.json", import.meta.url)).json()).version;
|
|
15
|
+
console.log(`xdg-open (Buninu Linux) ${version}`);
|
|
16
|
+
} else {
|
|
17
|
+
const url = browserTarget(options.target);
|
|
18
|
+
for (const tool of ["bunterm", "buninu-browser"])
|
|
19
|
+
if (!Bun.which(tool)) throw openError(`Required tool not found: ${tool}`, 3);
|
|
20
|
+
let child;
|
|
21
|
+
try {
|
|
22
|
+
child = Bun.spawn(["bunterm", "-e", "buninu-browser", url], {
|
|
23
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
24
|
+
});
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw openError(error.message, error.code === "ENOENT" ? 3 : 4);
|
|
27
|
+
}
|
|
28
|
+
process.exitCode = await child.exited === 0 ? 0 : 4;
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`xdg-open: ${error.message}`);
|
|
32
|
+
process.exitCode = openExitCode(error);
|
|
33
|
+
}
|
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0 - 2026-10-21
|
|
4
|
+
|
|
5
|
+
- Sync jsgotty to 1.2.0:
|
|
6
|
+
* Add opt-in Kitty keyboard support for disambiguated shortcuts, including
|
|
7
|
+
Super, and press/repeat/release events. Support flags 1, 2 and 8, mode
|
|
8
|
+
queries, and independent mode stacks for normal and alternate screens;
|
|
9
|
+
legacy keyboard encoding remains the default.
|
|
10
|
+
* Recover Super state on Android WebViews that deliver Meta key events but
|
|
11
|
+
omit the modifier state. Handle Android Alt shortcuts without accent
|
|
12
|
+
dead-key conversion, including recognizable keyup-only events.
|
|
13
|
+
* Preserve Alt on arrow, Insert, PageUp and PageDown shortcuts, and encode
|
|
14
|
+
Ctrl+/ as the conventional US control character.
|
|
15
|
+
* Add a manual keyboard tester with readable, colored shortcut names and
|
|
16
|
+
raw sequences; restore keyboard and raw modes when it exits.
|
|
17
|
+
* Allow local Kitty graphics consumers to request binary image data,
|
|
18
|
+
avoiding a base64 conversion; browser clients retain base64 by default.
|
|
19
|
+
- Sync bunmsh to 0.4.1: use the existing linker-aware shell launcher for the
|
|
20
|
+
awk fallback's subprocess commands instead of bypassing linker handling.
|
|
21
|
+
|
|
3
22
|
## 0.4.21 - 2026-10-15
|
|
4
23
|
|
|
5
24
|
- Sync jsgotty to 1.1.16: Turn on pixel report
|
|
@@ -302,7 +302,7 @@ async function interactive(state) {
|
|
|
302
302
|
const timer = setTimeout(() => {
|
|
303
303
|
if (pendingCursorQuery?.resolve === resolve) pendingCursorQuery = null;
|
|
304
304
|
resolve(null);
|
|
305
|
-
},
|
|
305
|
+
}, 400);
|
|
306
306
|
pendingCursorQuery = {
|
|
307
307
|
resolve(position) {
|
|
308
308
|
clearTimeout(timer);
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
[中文 README](README.md)
|
|
4
4
|
|
|
5
5
|
## News
|
|
6
|
+
|
|
7
|
+
### 2026/10/21
|
|
8
|
+
|
|
9
|
+
- Added [Kitty keyboard](#kitty-keyboard) support for distinct modifier combinations, including Super, and press/repeat/release events.
|
|
10
|
+
|
|
6
11
|
### 2026/08/16
|
|
7
12
|
- Fixed the disconnected-screen `Reconnect` button not being clickable in WebGL mode.
|
|
8
13
|
- The `Reconnect` UI now stays above the WebGL canvas and receives mouse and touch events correctly; Kitty image handling no longer intercepts right-click / long-press for entering a reconnect token or PID.
|
|
@@ -238,6 +243,41 @@ The `jsgotty` server has two connection modes:
|
|
|
238
243
|
- On disconnect, the overlay provides a `Reconnect` button for immediate manual retry.
|
|
239
244
|
- Right-click / long-press on that button lets you enter a custom reconnect token or PID.
|
|
240
245
|
|
|
246
|
+
## Kitty keyboard
|
|
247
|
+
|
|
248
|
+
The web terminal supports the shortcut-related parts of the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/), separately from Kitty graphics. Legacy keyboard encoding remains the default. A terminal application can opt in:
|
|
249
|
+
|
|
250
|
+
```sh
|
|
251
|
+
printf '\033[>11u' # 1: disambiguation + 2: repeat/release + 8: all keys
|
|
252
|
+
printf '\033[<u' # restore the previous mode when the application exits
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The receiving application must decode these sequences and restore the mode in its exit/finally handler; do not leave an ordinary shell in this mode. Negotiation supports `CSI ? u`, `CSI = flags ; mode u`, push and pop. Normal and alternate screens have independent stacks, limited to 32 entries. Reconnection, full reset and soft reset clear the state. Use flag `1` for unambiguous shortcuts, or `11` for modifier keys and press/repeat/release reporting.
|
|
256
|
+
|
|
257
|
+
DOM Meta maps to Kitty **Super (bit 8)**, not the separate Meta bit (32). Super+X is `CSI 120;9u`; Ctrl+Shift+Super+X is `CSI 120;14u`. Release events add `:3` when enabled.
|
|
258
|
+
|
|
259
|
+
Supported flags are **1, 2 and 8**; alternate keys (4) and associated text (16) are not implemented or reflected in the negotiated flags. IME composition/text commits and paste retain their existing paths; physical keys and releases cannot be inferred from committed text. Letters, digits, punctuation, navigation, Tab/Enter, function keys, keypad keys and standard Kitty media keys retain distinct modifiers. OS/browser-reserved shortcuts may never reach the page. Keys without standard Kitty codes, such as brightness and microphone mute, are not assigned private encodings.
|
|
260
|
+
|
|
261
|
+
This implements **jsgotty → PTY** encoding. Controlling Hyprland/Omarchy still requires Casty or another receiver to enable/decode Kitty keyboard and forward Super and release events. It does not complete the remote desktop input chain or change the mouse protocol.
|
|
262
|
+
|
|
263
|
+
Reload the jsgotty page after updating. Run the manual tester inside its terminal:
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
bun tests/manual/kitty-keyboard.js
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
It prints negotiated flags, raw sequences, modifiers and event types, exits after 60 seconds or unmodified Escape/q/Ctrl-C, and restores the previous keyboard/raw modes. An optional real bundled-xterm test uses an already installed browser:
|
|
270
|
+
|
|
271
|
+
```sh
|
|
272
|
+
CHROMIUM_HEADLESS=/path/to/chromium-headless-shell node --test tests/kitty-keyboard-browser.test.js
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Android Keyboards
|
|
276
|
+
|
|
277
|
+
For Android WebViews that deliver Meta down/up but lose `metaKey`, jsgotty tracks both Meta keys inside its own input handler. Blur or page hiding clears held state and sends releases for reported keys when event reporting is enabled.
|
|
278
|
+
|
|
279
|
+
In the Android terminal, Alt with letters, digits, punctuation, or space sends terminal Alt sequences based on physical key positions (US layout, including Shift), bypassing accent dead-key handling. Alt-E/I/N/U therefore no longer compose characters such as é, ê, ñ, and ü; input methods and paste remain available. When WebView omits `code`, recognized legacy `keyCode` values provide a fallback. Dead keys delivered only as `keyup` send the shortcut on release, without duplicating a previously handled `keydown`. Active composition, Ctrl, Meta, AltGr, and unidentified keys retain their existing handling. This applies only inside the jsgotty terminal and does not modify Android system keyboard settings.
|
|
280
|
+
|
|
241
281
|
## Tested platforms
|
|
242
282
|
- Windows x64: Windows 11
|
|
243
283
|
- Linux x64: CachyOS
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
[English README](README.en.md)
|
|
4
4
|
|
|
5
5
|
## News
|
|
6
|
+
|
|
7
|
+
### 2026/10/21
|
|
8
|
+
|
|
9
|
+
- 新增 [Kitty keyboard](#kitty-keyboard) 支援,可區分 Super 等修飾鍵組合,並回報按下、重複與放開事件。
|
|
10
|
+
|
|
6
11
|
### 2026/08/16
|
|
7
12
|
- 修正 WebGL 模式下斷線畫面的 `Reconnect` 按鈕無法點擊。
|
|
8
13
|
- `Reconnect` 介面現在會正確位於 WebGL canvas 上方並接收滑鼠與觸控事件;右鍵/長按輸入 reconnect token 或 PID 也不會被 Kitty 圖片事件攔截。
|
|
@@ -238,6 +243,41 @@ bun gotty.js --client -k <token|pid> [target]
|
|
|
238
243
|
- 斷線時會提供 `Reconnect` 按鈕,可手動立即重試。
|
|
239
244
|
- 該按鈕右鍵/長按可輸入自訂 reconnect token 或 PID。
|
|
240
245
|
|
|
246
|
+
## Kitty keyboard
|
|
247
|
+
|
|
248
|
+
網頁終端現在支援 [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) 的快捷鍵相關功能,與 Kitty 圖片協定分開。預設仍是原本的鍵盤編碼;由終端內的程式選擇開啟:
|
|
249
|
+
|
|
250
|
+
```sh
|
|
251
|
+
printf '\033[>11u' # 1:消除歧義 + 2:重複/放開事件 + 8:回報所有按鍵
|
|
252
|
+
printf '\033[<u' # 程式退出時還原上一層模式
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
程式必須能解析新編碼,並在 finally/退出處理中還原模式,不建議直接在一般 shell 長期開啟。支援 `CSI ? u` 查詢、`CSI = flags ; mode u` 設定/加上/移除旗標、`CSI > flags u` push 與 `CSI < count u` pop。主畫面與 alternate screen 各有獨立堆疊(上限 32 層);重連、完整重設或 soft reset 會清除狀態。只需要無歧義快捷鍵時可用旗標 `1`;需要獨立修飾鍵及所有按鍵的 down/repeat/up 時使用 `11`。
|
|
256
|
+
|
|
257
|
+
網頁的 Meta 對應 Kitty 的 **Super(bit 8)**,不是協定中的另一個 Meta(bit 32)。例如 Super+X 是 `CSI 120;9u`,Ctrl+Shift+Super+X 是 `CSI 120;14u`;啟用事件回報後,放開會帶 `:3`。
|
|
258
|
+
|
|
259
|
+
目前支援旗標 **1、2、8**;尚未實作替代鍵位(4)與附帶文字(16),查詢只回報實際啟用的旗標。IME 組字/文字提交與貼上保留既有路徑,不保證能回報實體按鍵或放開事件。字母、數字、標點、方向鍵、Tab、Enter、功能鍵、數字鍵盤與有標準 Kitty 編碼的媒體鍵可區分修飾組合;系統/瀏覽器先攔截的快捷鍵不會到達網頁,亮度、麥克風靜音等沒有標準 Kitty 鍵碼的鍵也不會擅自編成私有序列。
|
|
260
|
+
|
|
261
|
+
這完成的是 **jsgotty → PTY** 的鍵盤編碼。要操作 Hyprland/Omarchy,Casty 或其他接收程式仍須啟用、解析 Kitty keyboard 並轉送 Super 與放開事件;此項改動不代表整條遠端桌面輸入鏈已完成,也不改變滑鼠協定。
|
|
262
|
+
|
|
263
|
+
在 jsgotty 終端內手動確認(60 秒後自動退出,或按未修飾的 Esc/q/Ctrl+C):
|
|
264
|
+
|
|
265
|
+
```sh
|
|
266
|
+
bun tests/manual/kitty-keyboard.js
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
測試會印出協商結果、原始序列、修飾鍵及 press/repeat/release;結束時還原鍵盤模式與 raw mode。更新後請重新載入 jsgotty 網頁。可用既有 Chromium 執行真正 xterm 前端測試,不會自動安裝瀏覽器:
|
|
270
|
+
|
|
271
|
+
```sh
|
|
272
|
+
CHROMIUM_HEADLESS=/path/to/chromium-headless-shell node --test tests/kitty-keyboard-browser.test.js
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Android 鍵盤
|
|
276
|
+
|
|
277
|
+
Android WebView 若有 `Meta` down/up 卻漏掉 `metaKey`,jsgotty 會在自己的輸入處理中追蹤左右 Meta 並還原 Super。失焦或頁面隱藏時清除按住狀態;已送出的按鍵在事件回報模式下會補送放開。
|
|
278
|
+
|
|
279
|
+
Android 終端的 Alt+字母、數字、標點及空白會依實體按鍵位置(美式配置,支援 Shift)送出終端 Alt 序列,不進入重音死鍵流程。因此 Alt-E/I/N/U 等組合不再用來輸入 é、ê、ñ、ü;仍可使用輸入法或貼上。若 WebView 省略 `code`,會回退使用可辨識的 `keyCode`;若死鍵只送出 `keyup`,則在放開時補送一次,避免與已處理的 `keydown` 重複。正在組字、Ctrl、Meta、AltGr,以及無法辨識按鍵的事件維持原本處理。此規則只作用於 jsgotty 終端,不修改 Android 的系統鍵盤配置。
|
|
280
|
+
|
|
241
281
|
## Tested platforms 已測試作業系統
|
|
242
282
|
- Windows x64: Windows 11
|
|
243
283
|
- Linux x64: CachyOS
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jsgotty",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "jsgotty",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.2.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"node-addon-api": "^7.1.1",
|
|
12
12
|
"ws": "^8.18.3"
|