mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
10a726d007
commit
acebeb12b6
@@ -152,7 +152,15 @@ notify-rust = { version = "4", default-features = false, features = ["dbus"] }
|
||||
# (the `core` subcommand). The main binary itself is windows-subsystem so
|
||||
# launching the Tauri app from Explorer does not pop a console; the helper
|
||||
# CEF subprocess re-execs inherit that same subsystem.
|
||||
windows-sys = { version = "0.59", features = ["Win32_System_Console", "Win32_Foundation"] }
|
||||
windows-sys = { version = "0.59", features = [
|
||||
"Win32_System_Console",
|
||||
"Win32_Foundation",
|
||||
# EnumWindows / ShowWindow / SW_HIDE / SW_SHOW used by the main-window
|
||||
# close handler. tauri-runtime-cef's window.hide() / minimize() target a
|
||||
# cef::Window internal handle, not the visible Chrome_WidgetWin_1
|
||||
# top-level frame, so we walk the OS window list ourselves (#1607).
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
||||
+126
-11
@@ -822,7 +822,96 @@ fn mascot_native_window_is_open() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Hide or show the OS top-level main-window frame on Windows by enumerating
|
||||
/// this process's top-level windows and matching the visible
|
||||
/// `Chrome_WidgetWin_1` host. `WebviewWindow::hwnd()` from the vendored CEF
|
||||
/// runtime returns a `cef::Window` internal handle that `ShowWindow` rejects,
|
||||
/// so we walk the OS window list instead (#1607). Empirically there is one
|
||||
/// matching top-level frame; the single-instance lock window uses class
|
||||
/// `com.openhuman.app-sic` and is excluded.
|
||||
///
|
||||
/// `SW_HIDE` removes the frame from screen AND taskbar — full hide-to-tray as
|
||||
/// PR #1548 intended. On restore, the IsWindowVisible filter excludes hidden
|
||||
/// windows, so we also accept currently-hidden Chrome_WidgetWin_1 frames when
|
||||
/// `hide=false`.
|
||||
#[cfg(target_os = "windows")]
|
||||
fn set_main_window_hidden(hide: bool) {
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
use windows_sys::Win32::Foundation::{BOOL, HWND, LPARAM};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
EnumWindows, GetClassNameW, GetWindowThreadProcessId, IsWindowVisible, ShowWindow, SW_HIDE,
|
||||
SW_SHOW,
|
||||
};
|
||||
|
||||
struct EnumCtx {
|
||||
target_pid: u32,
|
||||
action: i32,
|
||||
require_visible: bool,
|
||||
matched: u32,
|
||||
}
|
||||
|
||||
unsafe extern "system" fn enum_proc(hwnd: HWND, lparam: LPARAM) -> BOOL {
|
||||
let ctx = unsafe { &mut *(lparam as *mut EnumCtx) };
|
||||
let mut pid: u32 = 0;
|
||||
unsafe { GetWindowThreadProcessId(hwnd, &mut pid) };
|
||||
if pid != ctx.target_pid {
|
||||
return 1;
|
||||
}
|
||||
if ctx.require_visible && unsafe { IsWindowVisible(hwnd) } == 0 {
|
||||
return 1;
|
||||
}
|
||||
let mut class_buf = [0u16; 64];
|
||||
let n = unsafe { GetClassNameW(hwnd, class_buf.as_mut_ptr(), class_buf.len() as i32) };
|
||||
if n <= 0 {
|
||||
return 1;
|
||||
}
|
||||
let class = OsString::from_wide(&class_buf[..n as usize])
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
if class != "Chrome_WidgetWin_1" {
|
||||
return 1;
|
||||
}
|
||||
unsafe { ShowWindow(hwnd, ctx.action) };
|
||||
ctx.matched += 1;
|
||||
1
|
||||
}
|
||||
|
||||
let action = if hide { SW_HIDE } else { SW_SHOW };
|
||||
let mut ctx = EnumCtx {
|
||||
target_pid: std::process::id(),
|
||||
action,
|
||||
// Hide path: only touch currently-visible frames. Show path: also
|
||||
// pick up frames already in the SW_HIDE state.
|
||||
require_visible: hide,
|
||||
matched: 0,
|
||||
};
|
||||
unsafe { EnumWindows(Some(enum_proc), &mut ctx as *mut _ as LPARAM) };
|
||||
log::info!(
|
||||
"[window-hide] EnumWindows: action={} matched={} pid={}",
|
||||
if hide { "SW_HIDE" } else { "SW_SHOW" },
|
||||
ctx.matched,
|
||||
ctx.target_pid,
|
||||
);
|
||||
}
|
||||
|
||||
fn show_main_window(app: &AppHandle<AppRuntime>) -> Result<(), String> {
|
||||
// On Windows: surface the OS top-level Chrome_WidgetWin_1 frame BEFORE
|
||||
// any Tauri lookups. After our close handler's SW_HIDE the runtime
|
||||
// briefly drops the `WebviewWindow` record for "main" (CEF treats the
|
||||
// hidden host as gone), so `get_webview_window("main")` returns None
|
||||
// and the early `?` below would abort before SW_SHOW fires (#1607).
|
||||
// EnumWindows + SW_SHOW operates directly on the OS HWND that
|
||||
// survived independently, and the runtime re-tracks the window once
|
||||
// it's visible again.
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
set_main_window_hidden(false);
|
||||
if let Some(webview) = app.get_webview("main") {
|
||||
let _ = webview.show();
|
||||
let _ = webview.set_focus();
|
||||
}
|
||||
}
|
||||
let window = app
|
||||
.get_webview_window("main")
|
||||
.ok_or_else(|| "main window not found".to_string())?;
|
||||
@@ -2106,16 +2195,23 @@ pub fn run() {
|
||||
);
|
||||
}
|
||||
}
|
||||
// Intercept the main window's close request on macOS AND Windows
|
||||
// so the user can re-open the app from the tray icon. Letting the
|
||||
// OS destroy the webview makes `get_webview_window("main")` return
|
||||
// None on the next tray-click and surfaces as
|
||||
// `[tray] failed to show main window from tray click: main window
|
||||
// not found` (OPENHUMAN-TAURI-2X — 21 events, Windows only).
|
||||
// Intercept the main window's close request on macOS so the user
|
||||
// can re-open the app from the tray icon. Letting the OS destroy
|
||||
// the webview makes `get_webview_window("main")` return None on
|
||||
// the next tray-click and surfaces as `[tray] failed to show main
|
||||
// window from tray click: main window not found`
|
||||
// (OPENHUMAN-TAURI-2X — 21 events, Windows only).
|
||||
//
|
||||
// macOS uses `window.hide()` because the vendored CEF runtime
|
||||
// routes that through `set_application_visibility(false)` at the
|
||||
// NSApplication level (`tauri-runtime-cef/src/lib.rs:588`), which
|
||||
// hides the CEF browser surface together with the host window.
|
||||
// Windows is handled in the separate arm below — see issue #1607.
|
||||
//
|
||||
// Linux is left out: `setup_tray` early-returns on Linux because
|
||||
// tray creation panics inside GTK during packaged runs, so the
|
||||
// hide-on-close behavior would strand the user with no way back.
|
||||
#[cfg(any(target_os = "macos", target_os = "windows"))]
|
||||
#[cfg(target_os = "macos")]
|
||||
RunEvent::WindowEvent {
|
||||
label,
|
||||
event: WindowEvent::CloseRequested { api, .. },
|
||||
@@ -2129,6 +2225,17 @@ pub fn run() {
|
||||
let _ = window.hide();
|
||||
}
|
||||
}
|
||||
// Windows: full hide-to-tray.
|
||||
//
|
||||
// PR #1548 routed Windows X click into the same prevent_close +
|
||||
// `window.hide()` branch as macOS, but on Windows the vendored
|
||||
// CEF runtime's WindowMessage::Hide / Minimize / Restore
|
||||
// (`tauri-runtime-cef/src/cef_impl.rs`) only operate on a
|
||||
// `cef::Window` internal handle that does not correspond to the
|
||||
// visible `Chrome_WidgetWin_1` top-level frame — `ShowWindow`
|
||||
// calls against it are silent no-ops. We bypass the runtime
|
||||
// entirely and walk the OS window list to issue SW_HIDE / SW_SHOW
|
||||
// directly on the matching top-level frame (issue #1607).
|
||||
#[cfg(target_os = "windows")]
|
||||
RunEvent::WindowEvent {
|
||||
label,
|
||||
@@ -2136,12 +2243,20 @@ pub fn run() {
|
||||
..
|
||||
} if label == "main" => {
|
||||
log::info!(
|
||||
"[window] close requested on main window — hiding to tray instead of destroying"
|
||||
"[window] close requested on main window — hiding to tray"
|
||||
);
|
||||
api.prevent_close();
|
||||
if let Some(window) = app_handle.get_webview_window("main") {
|
||||
let _ = window.hide();
|
||||
}
|
||||
// Hide the OS top-level Chrome_WidgetWin_1 frame via
|
||||
// EnumWindows + SW_HIDE — full hide-to-tray as PR #1548
|
||||
// intended. `window.hide()` and `window.minimize()` through
|
||||
// the vendored CEF runtime are no-ops on Windows because
|
||||
// `WebviewWindow::hwnd()` returns a cef::Window proxy handle
|
||||
// rather than the visible top-level frame; we walk the OS
|
||||
// window list directly instead (#1607). SW_HIDE on the host
|
||||
// frame cascades to all child HWNDs (including the CEF
|
||||
// browser surface), so no separate `webview.hide()` is
|
||||
// needed and `show_main_window` only has to issue SW_SHOW.
|
||||
set_main_window_hidden(true);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
RunEvent::Reopen { .. } => {
|
||||
|
||||
Reference in New Issue
Block a user