From 5affbb05a6acfbf0dca5cf08b1d0ba09e8f2bf9b Mon Sep 17 00:00:00 2001 From: ShakaRover Date: Fri, 2 Oct 2026 17:39:44 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(linux):=20=E8=A7=84=E9=81=BF=20WebKitGT?= =?UTF-8?q?K=20=E9=BB=91=E5=B1=8F=E4=B8=8E=20Wayland=20DMABUF=20=E5=B4=A9?= =?UTF-8?q?=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - background_color 会让 WebKitGTK(2.5x) webview 整体不渲染,仅非 Linux 平台设置 - Wayland 下强制 WEBKIT_DISABLE_DMABUF_RENDERER=1,避免 Gdk Error 71 崩溃 - 主窗口改用 tauri.linux.conf.json,去掉 background_color --- package-lock.json | 4 ++-- src-tauri/src/lib.rs | 28 ++++++++++++++++++++-------- src-tauri/src/main.rs | 15 +++++++++++++++ src-tauri/tauri.linux.conf.json | 17 +++++++++++++++++ 4 files changed, 54 insertions(+), 10 deletions(-) create mode 100644 src-tauri/tauri.linux.conf.json diff --git a/package-lock.json b/package-lock.json index 013977d..92037a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "zcode-switch", - "version": "1.3.2", + "version": "1.7.0-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "zcode-switch", - "version": "1.3.2", + "version": "1.7.0-beta.1", "license": "MIT", "dependencies": { "@tauri-apps/api": "^2.1.1" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index d7b0fb5..e5a6c5d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -806,15 +806,22 @@ fn open_captcha_window(app: &AppHandle, auto: bool) -> Result<(), String> { } return Ok(()); } - let win = tauri::WebviewWindowBuilder::new( + // Linux: window background_color 会让 WebKitGTK(2.5x) 整个 webview 不渲染(黑屏), + // 前端页面自带深色背景,这里仅在非 Linux 平台设置以避免启动白闪。 + #[allow(unused_mut)] + let mut builder = tauri::WebviewWindowBuilder::new( app, "captcha", tauri::WebviewUrl::App("captcha.html".into()), ) .title(i18n::tr("title.captcha")) - .theme(Some(tauri::Theme::Dark)) - .background_color(tauri::window::Color(10, 10, 12, 255)) - .inner_size(w, h) + .theme(Some(tauri::Theme::Dark)); + #[cfg(not(target_os = "linux"))] + { + builder = builder.background_color(tauri::window::Color(10, 10, 12, 255)); + } + let win = builder + .inner_size(w, h) .min_inner_size(340.0, 280.0) .maximizable(false) .resizable(false) @@ -907,15 +914,20 @@ async fn open_settings(app: AppHandle) -> Result<(), String> { let _ = win.set_focus(); return Ok(()); } - let win = tauri::WebviewWindowBuilder::new( + #[allow(unused_mut)] + let mut builder = tauri::WebviewWindowBuilder::new( &app, "settings", tauri::WebviewUrl::App("settings.html".into()), ) .title(i18n::tr("title.settings")) - .theme(Some(tauri::Theme::Dark)) - .background_color(tauri::window::Color(10, 10, 12, 255)) - .inner_size(w, h) + .theme(Some(tauri::Theme::Dark)); + #[cfg(not(target_os = "linux"))] + { + builder = builder.background_color(tauri::window::Color(10, 10, 12, 255)); + } + let win = builder + .inner_size(w, h) .min_inner_size(440.0, 540.0) .resizable(true) .additional_browser_args("--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --no-proxy-server") diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b398c67..2ab4312 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -35,7 +35,22 @@ fn ensure_cli_console() { } } +#[cfg(target_os = "linux")] +fn linux_webkit_workaround() { + // WebKitGTK 的 DMABUF 渲染器在 Wayland 下会触发 Gdk Error 71(protocol + // error)直接崩溃,NVIDIA 驱动上必现。回退到旧渲染路径。 + let on_wayland = match std::env::var("WAYLAND_DISPLAY") { + Ok(v) => !v.trim().is_empty(), + Err(_) => std::env::var("XDG_SESSION_TYPE").map(|v| v.trim() == "wayland").unwrap_or(false), + }; + if on_wayland && std::env::var_os("WEBKIT_DISABLE_DMABUF_RENDERER").is_none() { + std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } +} + fn main() { + #[cfg(target_os = "linux")] + linux_webkit_workaround(); let args: Vec = std::env::args().collect(); if let Some(pos) = args.iter().position(|a| a == "--cli") { let cli_args: Vec = args.iter().skip(pos + 1).cloned().collect(); diff --git a/src-tauri/tauri.linux.conf.json b/src-tauri/tauri.linux.conf.json new file mode 100644 index 0000000..a832f75 --- /dev/null +++ b/src-tauri/tauri.linux.conf.json @@ -0,0 +1,17 @@ +{ + "app": { + "windows": [ + { + "title": "Z·SWITCH", + "width": 680, + "height": 820, + "minWidth": 520, + "minHeight": 640, + "resizable": true, + "center": true, + "theme": "Dark", + "additionalBrowserArgs": "--disable-features=msWebOOUI,msPdfOOUI,msSmartScreenProtection --no-proxy-server" + } + ] + } +} From fcdc1dba608d1c0665dec18b889a6f9e65bd76fb Mon Sep 17 00:00:00 2001 From: ShakaRover Date: Fri, 2 Oct 2026 17:49:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(store):=20=E4=BF=AE=E5=A4=8D=E7=AC=AC?= =?UTF-8?q?=E4=BA=8C=E6=AC=A1=E5=BC=BA=E5=88=B6=E5=88=87=E6=8D=A2=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E9=87=8D=E5=90=AF=20ZCode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 自己 spawn 的 ZCode 子进程从不回收,退出后残留僵尸进程, pgrep 把僵尸算作运行中,导致下次 kill_zcode 误判超时、切换中止。 - launch_zcode 后台线程 wait 回收子进程 - zcode_running(非 Windows)按 ps state 过滤僵尸 pid --- src-tauri/src/store.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src-tauri/src/store.rs b/src-tauri/src/store.rs index 108842f..7525397 100644 --- a/src-tauri/src/store.rs +++ b/src-tauri/src/store.rs @@ -328,11 +328,29 @@ pub fn zcode_running() -> bool { return false; } ["zcode", "ZCode"].iter().any(|name| { - no_window("pgrep") - .args(["-x", name]) - .output() - .map(|o| o.status.success()) - .unwrap_or(false) + let out = match no_window("pgrep").args(["-x", name]).output() { + Ok(o) => o, + Err(_) => return false, + }; + if !out.status.success() { + return false; + } + // pgrep 会把僵尸进程也算匹配上;我们 spawn 的 ZCode 退出后若尚未回收 + // 就是僵尸,必须按进程状态排除,否则强制切换的 kill 会误判超时。 + String::from_utf8_lossy(&out.stdout) + .split_whitespace() + .any(|pid| { + no_window("ps") + .args(["-o", "state=", "-p", pid]) + .output() + .ok() + .map(|o| { + let st = String::from_utf8_lossy(&o.stdout); + let st = st.trim(); + !st.is_empty() && !st.starts_with('Z') + }) + .unwrap_or(false) + }) }) } @@ -398,9 +416,13 @@ pub fn launch_zcode(path: &str) -> Result<(), String> { if !p.exists() { return Err(trf("err.zcode.missing", &[("path", path)])); } - detached(Command::new(&p)) + let mut child = detached(Command::new(&p)) .spawn() .map_err(|e| trf("err.zcode.launch", &[("e", &e.to_string())]))?; + // 后台回收,避免 ZCode 退出后残留僵尸进程(详见 zcode_running 注释)。 + std::thread::spawn(move || { + let _ = child.wait(); + }); Ok(()) }