Skip to content

Releases: jingyingruodi/dsh-headless-display

v1.4.1 — 画面与音频双静默

Choose a tag to compare

@jingyingruodi jingyingruodi released this 21 Oct 06:54

画面与音频双静默

这个版本把「静默」从画面补齐到音频:隔离 display 里的程序可以正常出声,
但声音不会从用户的扬声器/耳机出来;同时保证用户自己正在听的音频完全不受影响。

音频静默(新)

  • 每个隔离 display 建一个 node.hidden=true 的 null sink(dsh-iso-<N>)
  • iso_display_run 起的进程注入 PULSE_SINK,音频全部丢弃;iso_display_stop 卸载 sink
  • 配置:audio: 'null' | 'inherit'(默认 null)

守住用户正在听的音频

  • 本机实测:任何新增音频 sink 都会把用户默认声卡置为静音(不止本插件建的,Proton/Steam 中途建的也算)
  • 250ms keeper:仅在「sink 数量增加」时把默认设备与静音状态拉回启动时的值;
    用户自己手动静音不会被抢回
  • iso_display_start 返回 audio.{userDefaultSink,userDefaultMuted,keptUserAudioUnmuted}
  • iso_display_stop 返回 userAudioRestores

实测证据(2026-10-21,黑盒智能体只给目标与工具定义、无源码)

  • 渲染:Desynced 主菜单 1280x720,253,530 色(via: grim)
  • 游戏音频:application.name="Desynced" 的流落在 dsh-iso-2;
    从 dsh-iso-2.monitor 录音 6.14s,max −24.2 dB —— 确实在出声,但被丢弃
  • 用户音频:默认设备(蓝牙耳机)全程不变、Mute: no、音量不变;
    386 次采样 0 次静音
  • 桌面窗口:整轮 0 条 Desynced/Windows 相关窗口

Desynced in isolated display

继承自 v1.3.x 的静默硬保证

  • 隔离 display 编号按进程归属判定(不再把用户桌面 :1 误判成隔离 display)
  • 子进程钉 X11(GDK_BACKEND / QT_QPA_PLATFORM / SDL_VIDEODRIVER /
    ELECTRON_OZONE_PLATFORM_HINT),Wayland 不泄漏到桌面
  • 写操作只认本插件启动的 display(allowForeignDisplays 可放开)
  • killClientsOnStop 清理 setsid 孤儿进程
  • 泄漏可见化:list 给 ageMinutes/stale,doctor 点名

其它

  • iso_display_doctor:依赖 / GPU / 音频 / 桌面自检,按发行版给安装命令
  • scripts/run-proton.sh:自动探测 Steam 库 / compatdata / Proton / exe
  • 修复:wayvnc 参数、spawn ENOENT 带崩、pkill -f 误杀、并发 start 抢号

安装

plugin_manager(action: "install_bundle", target: "<本目录绝对路径>")

装完先跑 iso_display_doctor。

v1.3.2 — 静默硬保证 + Proton 游戏

Choose a tag to compare

@jingyingruodi jingyingruodi released this 21 Oct 06:54

静默硬保证 + Proton 游戏

修掉两个「会让命令/窗口跑到用户桌面」的严重问题

  • 隔离 display 编号被误判:xauthority 配置过期时,旧实现会把用户桌面的 :1
    当成新建的隔离 display(实测 iso_display_run 返回 DISPLAY=:1、1920x1200)。
    现在改为按进程组归属认 Xwayland,并加两道安全网。
  • Wayland 客户端泄漏:只删 WAYLAND_DISPLAY 时 libwayland 会默认连
    $XDG_RUNTIME_DIR/wayland-0(用户桌面)。现在子进程显式钉到 X11。

Proton / Steam 游戏

  • sway 后端提供 DRI3,Vulkan/GL 直通 GPU
  • 新增 scripts/run-proton.sh:自动探测 Steam 库、compatdata 前缀、Proton 版本、exe
  • 实测 Desynced(UE / DX12)完整渲染、可注入键鼠、桌面零窗口

工具

  • 新增 iso_display_doctor(依赖 / GPU / 目录 / 桌面自检 + 发行版安装命令)
  • iso_display_start 支持按次指定 backend,auto 失败回退 xvfb
  • iso_display_run 支持 env(Proton 变量)并校验 DISPLAY / WAYLAND_DISPLAY
  • iso_display_stop 清理 setsid 起的孤儿进程(killClientsOnStop)

安装

plugin_manager(action: "install_bundle", target: "<本目录绝对路径>")

v1.1.1 —— README 与代码同步 + 状态自愈

Choose a tag to compare

@jingyingruodi jingyingruodi released this 11 Oct 08:14

修复(由另一位维护者审阅发现)

1. README 与代码不同步

  • 开头仍写「(Xephyr)…7 个工具」→ 实际默认后端是 Xvfb,工具 9 个
  • HTTP 路由写成 6 条 → 实际 8 条(新增 /popout、/popin)
  • stateDir 默认值写错;配置表缺 backend / vncPortBase
  • 仍描述「回退到 shell.overlay 浮动面板」→ 该分支早已删除(它造成过 UI 被盖死的事故)

2. 命名不统一

新增「名称沿革」一节,说明改名的根本原因:dsh 宿主模块按包名做进程级缓存,
改 JS 后开关插件 / 重装 bundle / 改入口文件名 / 换目录路径都无法加载新代码,
只有换包名或重启 dsh。

3. 顺带修掉两处真问题

  • 状态目录自愈:listDisplays 会清掉已死 display 的状态记录
  • visible 改为探测进程(process.kill(pid,0)):原先只看有没有记过 viewerPid,
    查看器早已退出时 UI 仍显示「关闭真实窗口」

本机清理

删除遗留的 ~/.dsh-isolated-display、~/.dsh-isolated-x11、~/.dsh-gui-isolation,
只保留当前的 ~/.dsh-headless-display。

v1.1.0 —— 默认纯 headless(不再弹窗)

Choose a tag to compare

@jingyingruodi jingyingruodi released this 11 Oct 02:58

行为变更(重要)

之前隔离 display 用的是 Xephyr,会在用户桌面上开一个窗口,会打扰正在用电脑的人。

现在默认后端是 Xvfb(真 headless):桌面上零窗口,用户零感知;面板里照样能实时查看与操作。

需要看真实画面或亲手操作时,用面板上的 「打开真实窗口」(工具 iso_display_popout):
在该 display 上启动只监听本机回环的 x11vnc + VNC 查看器,关掉即回到静默,程序不重启。

实测(客观验证「静默」)

检查 结果
启动前后,用户桌面 :1 的顶层窗口数 133 → 133(完全没变)
桌面上属于隔离 display 的窗口 0

顺带修复

POST /input 的 display 优先级 bug:query 缺失时 undefined 会覆盖 body 里的 display,
导致侧边栏面板的点击与输入一直返回 500。

已知限制

KDE Wayland 下 XWayland 窗口抬不到原生 Wayland 窗口之上,弹出的查看器可能不会自动到最前(可从任务栏点击)。

新增工具

iso_display_popout / iso_display_popin(HTTP 对应 /gui-isolation/popout、/popin)。

v1.0.1 —— 修复右栏标签页 + MIT 许可证

Choose a tag to compare

@jingyingruodi jingyingruodi released this 11 Oct 02:45

修复

右栏标签页永远不出现的根因:用 ctx.get("betterSidebar") 判断可用性。
better-sidebar 是通过 ctx.provide("betterSidebar", service) 发布服务的,晚于本插件 apply,
所以 ctx.get 拿到 undefined,代码直接 return,标签页从未注册。

改为直接 ctx.inject(["betterSidebar"], cb) —— Cordis 会等待依赖就绪;
真的没有 better-sidebar 时回调不执行,自然不注册任何 UI。

同时补上 TabDescriptor 的 description / order / single: true。

新增

  • MIT 许可证(LICENSE + README 章节)
  • README「维护者必读」补第 4 个坑:betterSidebar 不在 Cordis Service 目录里,cordis_inspect_query 查不到它,别因此以为它不存在

版本

package.json 1.0.0 → 1.0.1

v1.0.0 —— 首个可用版本

Choose a tag to compare

@jingyingruodi jingyingruodi released this 11 Oct 02:20

内容

  • 7 个 Agent 工具:iso_display_start/list/stop/run/capture/windows/input
  • 6 条 HTTP 路由:/gui-isolation/{state,snapshot,windows,start,stop,input}
  • Web 侧边栏标签页:实时画面 + 点画面即点击 + 文本/按键注入 + 窗口列表
  • 零 npm 依赖,无构建步骤

实测(2026-10-11)

项 结果
iso_display_start :9 1280x720
iso_display_capture 1280x720 PNG
iso_display_input(type --window) xev 收到 13 次 KeyPress,键序 dsh-plugin-ok
GET /gui-isolation/state 200 JSON
GET /gui-isolation/snapshot 200 image/png

⚠️ 已知限制

  • Xephyr 不提供 DRI3,跑不了 Vulkan/3D 程序(UE 启动即 EXCEPTION_INT_DIVIDE_BY_ZERO)。2D/X11 程序完全可用。
  • iso_display_run 是同步的,GUI 程序请用 sh -c "setsid <程序> &"。

📌 维护者必读

README 里记录了三个已踩过的坑:

  1. 插件 Config 不能是裸 JSON Schema(Cordis 要 Standard Schema),否则激活失败
  2. 模块按包名做进程级缓存:改 JS 后重装/换目录/改入口文件名都无效,只有换包名或重启 dsh
  3. dsh 宿主进程不继承桌面的 DISPLAY/XAUTHORITY

另有一次事故记录:客户端曾把面板挂到 shell.overlay(frame-wide 浮层)导致整个 UI 被盖死,现已彻底移除该回退。