diff --git a/HDMI-Tool/AGENTS.md b/HDMI-Tool/AGENTS.md index 3c825b8..8b44e4f 100644 --- a/HDMI-Tool/AGENTS.md +++ b/HDMI-Tool/AGENTS.md @@ -60,8 +60,11 @@ HDMI-Tool/ 5. **exe 自包含**:图标经 `--add-data` 内嵌(运行时从 `_MEIPASS` 读);内置五种图案 (红/绿/蓝/棋盘格/网格线)由程序绘制,运行时**不依赖** assets/;solid/bw PNG 只服务自定义栏位。 6. **DPI 感知**:进程启动即 `SetProcessDpiAwareness(2)`,图案窗口按物理像素定位,勿移除。 -7. **启动强制扩展拓扑**:`SetDisplayConfig(SDC_TOPOLOGY_EXTEND|SDC_APPLY)`,静默容错 - (失败不阻断启动)。动机:LT8619C 热插拔后 Windows 可能停留在"仅电脑屏幕"。 +7. **智能显示拓扑(勿改回无条件扩展)**:启动时枚举"接入但不在桌面"的显示器,仅当其 + EDID 厂商字节为转接板签名 `61 93`(压缩码 "XLS",上游 lt8619c_edid.c)时才 + `SetDisplayConfig(SDC_TOPOLOGY_EXTEND|SDC_APPLY)` 拉回桌面,静默容错。动机:板卡 + 热插拔后 Windows 可能停留在"仅电脑屏幕"导致枚举不到板卡。用户主动选择的拓扑 + (家里的"仅第二屏幕"等,未激活的是笔记本内屏,厂商非 XLS)一律不动。 8. **分辨率切换是临时的**:走 `CHANGEDISPLAYSETTINGS`(不写注册表),"恢复"用会话开始时快照。 9. **EDID 来源是注册表**(`HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY\...\Device Parameters\EDID`), 不是 WMI/DDC;exe 的 noconsole 版无 stdout,`--dump/--edid-fields` 只在源码/控制台版可用。 diff --git a/HDMI-Tool/README.md b/HDMI-Tool/README.md index 66dfb49..c16a791 100644 --- a/HDMI-Tool/README.md +++ b/HDMI-Tool/README.md @@ -21,7 +21,7 @@ | 分辨率切换 | "分辨率"下拉列出显卡按该屏 EDID 提供的全部模式;**应用**=临时切换(不写注册表,重启/恢复即还原),**恢复**=回到本次会话切改前的模式 | | 测试图案投屏 | 全屏投到所选显示器,测试卡区域可选 `640x360 / 800x600 / full 铺满`;内置 纯红/纯绿/纯蓝/黑白棋盘格/网格线 + R/G/B 三行 55/AA/FF 纯色值按钮 + 3 个自定义图片栏位 | | 光机控制 | RS485 → 板载 GD32E230 网关 → DLPC3421:开关光机(可定时)、设光强、查电流/温度/版本、TEC 控制、软件重启;收发日志带协议译码(见 §5) | -| 显示拓扑控制 | 启动时强制扩展显示拓扑(复制/仅第二屏等模式下保证 demo 板屏幕独立可控) | +| 智能显示拓扑 | 仅当检测到转接板(EDID 厂商 "XLS")接入但未进入桌面(Windows 卡在"仅电脑屏幕")时才强制扩展把它拉回;你主动选的"仅第二屏幕"等拓扑不会被改动 | | 命令行模式 | 全部功能可脚本化调用,供自动化测试(见 §6、§7) | | 波形分析脚本 | 逻辑分析仪 CSV 波形按占空比签名分段,输出行结构/占空比/消隐明细(见 §7.3) | diff --git a/HDMI-Tool/release/HDMI-Tool.exe b/HDMI-Tool/release/HDMI-Tool.exe index bdcb547..b868d49 100644 Binary files a/HDMI-Tool/release/HDMI-Tool.exe and b/HDMI-Tool/release/HDMI-Tool.exe differ diff --git a/HDMI-Tool/src/hdmi_gui.py b/HDMI-Tool/src/hdmi_gui.py index bab6a00..9faf2c0 100644 --- a/HDMI-Tool/src/hdmi_gui.py +++ b/HDMI-Tool/src/hdmi_gui.py @@ -342,15 +342,43 @@ def apply_mode(device, dm): def ensure_display_extend(): - """把 Windows 显示拓扑强制为"扩展"。 + """仅当转接板显示器"已接入但未进入桌面"时, 才强制扩展拓扑。 - LT8619C 转接板热插拔后, 若 Windows 停留在"仅电脑屏幕"拓扑(会记住上次 + 场景: 板卡热插拔后, 若 Windows 停留在"仅电脑屏幕"拓扑(会记住上次 Win+P 的选择), 板卡的 EDID 虽已安装、GPU 也在按首选时序输出, 但桌面 - 并未扩展过去, get_displays() 就枚举不到这块"显示器"。 + 并未扩展过去, get_displays() 就枚举不到这块"显示器" —— 此时才需要 + 强制扩展把它拉回桌面。 - 返回 SetDisplayConfig 的返回码(0=成功); 任何异常静默, 不影响工具启动。 + 用户主动选择的拓扑(家里的"仅第二屏幕"、手动关闭的显示器等)一律不动: + 枚举"接入但不在桌面"的显示器, 只有其 EDID 厂商是转接板签名 "XLS" + (字节 0x61 0x93, 上游 lt8619c_edid.c) 时才扩展; 笔记本内屏等其它 + 厂商视为用户有意关闭, 不干预。 + + 返回 SetDisplayConfig 的返回码(0=成功, -1=未触发, 其它=异常); 静默容错。 """ try: + board_stuck = False + i = 0 + while True: + dd = DISPLAY_DEVICEW() + dd.cb = ctypes.sizeof(dd) + if not user32.EnumDisplayDevicesW(None, i, ctypes.byref(dd), 0): + break + i += 1 + j = 0 + while True: + m = DISPLAY_DEVICEW() + m.cb = ctypes.sizeof(m) + if not user32.EnumDisplayDevicesW(dd.DeviceName, j, ctypes.byref(m), 0): + break + j += 1 + if m.StateFlags & 0x1: # ATTACHED_TO_DESKTOP: 已在桌面 + continue + raw = read_edid_from_registry(m.DeviceID) + if len(raw or ()) >= 10 and raw[8:10] == b'\x61\x93': + board_stuck = True # 转接板没进桌面 -> 需要拉回 + if not board_stuck: + return -1 SDC_TOPOLOGY_EXTEND = 0x00000004 SDC_APPLY = 0x00000080 return ctypes.windll.user32.SetDisplayConfig( @@ -1648,8 +1676,8 @@ def main(): help='RS485 发送光机命令(可多次), 如 --dlpc-cmd M730S0 --dlpc-cmd M999') args = ap.parse_args() - # LT8619C 板卡热插拔后可能停留在"仅电脑屏幕"拓扑, 先强制扩展, - # 否则 get_displays() 枚举不到板卡的"显示器"(失败静默) + # LT8619C 板卡热插拔后可能停留在"仅电脑屏幕"拓扑: 仅当检测到转接板 + # (EDID 厂商 XLS)接入但未进桌面时才强制扩展, 其余拓扑不动(失败静默) ensure_display_extend() if args.dump or args.edid_fields: