fix(app-iced): 子进程统一走 platform::child::quiet(消灭启动闪窗)+ 守卫射程扩到整棵 crates/(M217)
用户报「CI 构建版启动时底下闪一个控制台」:M213 统一了 crates/core/src 的 20 处 Command::new,但守卫射程只有 core/src,app-iced 的 reg.exe(每次 GUI 启动都跑)漏网。 - crates/core/tests/child_quiet_guard.rs:扫描根 crates/core/src → crates/(1219 个 .rs), 角色清单改成相对 crates/ 的路径(文件角色钉精确处数,目录角色至少一处); 「扫不到文件」兜底断言保留,另加角色文件锚点兜底;顺带修掉全树扫描会撞上的 「纯 //! 结论页没有代码行」误判 - app-iced:platform/locale.rs 的 reg.exe、app/update/update_action.rs、 app/boot/fallback.rs、app/boot/termux_window.rs 的 xdpyinfo、 renderer/acceptance/locale.rs 的 defaults 全部改走 quiet(后三处是同一 exe / POSIX 上的 pass-through,为的是规则整体可被守卫钉住) - app/dialog/export.rs 的 explorer / open / xdg-open 保持裸构造并写清「窗口就是产品」, presenter/src/host/opener.rs 同理登记成角色(不在本 mission 改动范围) 门禁:fmt/check --workspace --all-targets/test --workspace -j 4/clippy --workspace --all-targets -D warnings/全仓 400 行 + <2 字节 .rs 扫描,六步退出码全 0,CHAIN_EXIT=0。
This commit is contained in:
1 parent
7521c47944
commit
f8acb50156
7 files changed
+225
-165
No files matched your search
@@ -108,7 +108,10 @@ pub(super) fn fallback_command(
|
||||
args: impl IntoIterator<Item = std::ffi::OsString>,
|
||||
fallback: Kind,
|
||||
) -> std::process::Command {
|
||||
let mut command = std::process::Command::new(program);
|
||||
// 子进程就是本 GUI exe 自己(PE Subsystem=2,本来就不会分到控制台),所以这一处今天闪不了;
|
||||
// 走 `quiet` 是为了让「app-iced 里除 opener 外不留裸构造」这条规则整体可被守卫钉住——将来
|
||||
// 某天把降级重开换成控制台辅助程序时,不会从这里漏出一块黑框(M217)。
|
||||
let mut command = unlucky_core::platform::child::quiet(program);
|
||||
command.args(args).envs(fallback_env(fallback));
|
||||
command
|
||||
.stdin(std::process::Stdio::inherit())
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
//!
|
||||
//! 只在 Termux + 有 `DISPLAY` 时探测;`xdpyinfo` 缺失/解析失败一律回默认,不影响其它平台。
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use unlucky_core::platform::paths::ProcessEnv;
|
||||
use unlucky_core::platform::termux;
|
||||
|
||||
@@ -54,7 +52,11 @@ pub(super) fn fit_screen(default: (f32, f32), min: (f32, f32), screen: (f32, f32
|
||||
|
||||
/// 当前 X 屏幕尺寸(`xdpyinfo` 的 `dimensions: WxH pixels`)。
|
||||
fn x_screen_size() -> Option<(f32, f32)> {
|
||||
let output = Command::new("xdpyinfo").output().ok()?;
|
||||
// 非 Windows 上 `quiet` 是原样返回(POSIX 没有「spawn 就新开一个终端」这回事);走它是为了
|
||||
// 不在整棵 `crates/` 的子进程守卫里开一个「Termux 专用」例外(M217)。
|
||||
let output = unlucky_core::platform::child::quiet("xdpyinfo")
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -166,6 +166,18 @@ pub(crate) fn open_dir(dir: &std::path::Path) -> Result<(), String> {
|
||||
spawn_opener(dir).map_err(|error| format!("{}(打开失败:{error})", dir.display()))
|
||||
}
|
||||
|
||||
/// 平台各自的「打开这个目录」实现。
|
||||
///
|
||||
/// ## 为什么这三处**故意**不走 `unlucky_core::platform::child::quiet`(M217)
|
||||
///
|
||||
/// 这一处 spawn 的**目的就是让桌面上出现一个可见的东西**(文件管理器窗口 / Finder 窗口 /
|
||||
/// 桌面环境的文件管理器)。`quiet` 的语义是「把子进程的窗口抑制掉」,用在这里是把目的本身
|
||||
/// 抑制掉——改错了用户点「打开文件夹」就只看到「打不开」的报错。
|
||||
///
|
||||
/// 而且从窗口角度也不需要它:`explorer.exe` / `open` / `xdg-open` 自己都是 GUI 侧程序,由
|
||||
/// 无控制台的 GUI 父进程 spawn 时 Windows 不会给它们新建控制台。所以这三处在
|
||||
/// `crates/core/tests/child_quiet_guard.rs` 的 `ALLOWED_ROLES` 里登记成「目录打开器」角色,
|
||||
/// **不是**漏网。别把它们改成 `quiet`。
|
||||
#[cfg(target_os = "windows")]
|
||||
fn spawn_opener(dir: &std::path::Path) -> Result<(), String> {
|
||||
// `explorer` 不用 shell:路径里的空格与 `&` 不会被当成命令分隔符。
|
||||
|
||||
@@ -71,7 +71,12 @@ impl App {
|
||||
match std::env::current_exe() {
|
||||
Ok(exe) => {
|
||||
let args: Vec<std::ffi::OsString> = std::env::args_os().skip(1).collect();
|
||||
match std::process::Command::new(&exe).args(&args).spawn() {
|
||||
// 子进程是本 GUI exe 自己(Subsystem=2,不会分到控制台);走 `quiet` 是为了让
|
||||
// 「app-iced 里除 opener 外不留裸构造」这条规则整体可被守卫钉住(M217)。
|
||||
let spawned = unlucky_core::platform::child::quiet(&exe)
|
||||
.args(&args)
|
||||
.spawn();
|
||||
match spawned {
|
||||
Ok(child) => diag!(
|
||||
"[update] 已拉起新进程 pid={}:{}",
|
||||
child.id(),
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
//! 只有注册表知道答案」的问题,它用 `reg.exe query` 解决,`unsafe` 一个没加。本 crate 的 `unsafe`
|
||||
//! 只允许出现在 [`crate::platform::win32`](D36 的纪律),所以这里照抄那条路。
|
||||
//!
|
||||
//! 子进程**一律**经 `unlucky_core::platform::child::quiet` 起(M217):GUI 父进程自己没控制台,
|
||||
//! 裸构造会给 `reg.exe` 新建一块控制台窗口,用户看到的就是启动时闪一下黑框。
|
||||
//!
|
||||
//! 为什么不 `GetUserDefaultLocaleName`:它要 FFI,而它对**本用途**并不比注册表准——Windows 的
|
||||
//! 「区域格式」与「显示语言」本来就是两个设置,两者都有各自的说法(见下面的诚实边界)。
|
||||
//!
|
||||
@@ -83,7 +86,10 @@ impl RegIo for HostReg {
|
||||
return None;
|
||||
}
|
||||
|
||||
let output = std::process::Command::new(program)
|
||||
// **必须**走 `quiet`(M217):本 crate 的父进程是没有控制台的 GUI(PE Subsystem=2),
|
||||
// 裸 `Command::new("reg.exe")` 会让 Windows 给子进程新建一块控制台——用户看到的就是
|
||||
// 「启动时底下闪一个黑框」。这条路径每次 GUI 启动都跑,所以它闪得准时。
|
||||
let output = unlucky_core::platform::child::quiet(program)
|
||||
.args(args)
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
@@ -249,7 +249,9 @@ impl LocaleSource for AppleLanguages {
|
||||
fn locale(&self) -> Option<String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let output = std::process::Command::new("defaults")
|
||||
// macOS 上 `quiet` 是原样返回(POSIX 不因 spawn 新开终端);走它是为了不在整棵
|
||||
// `crates/` 的子进程守卫里为这条 macOS 专用路径开例外(M217)。
|
||||
let output = unlucky_core::platform::child::quiet("defaults")
|
||||
.args(["read", "-g", "AppleLanguages"])
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
Reference in new issue
Block a user