forked from unom/punktfunk
The July 2026 windows-rs brings a reconciler keyed-child-order fix (#4728), widget validation (#4727), a DPI collision fix (#4751), icon elements (#4736), multi-window support (#4730) and scroll virtualization (#4710) — the re-render fixes the Windows client has been working around at the architecture level. All three pinned deps (windows-reactor, windows, windows-reactor-setup) move together so windows-core stays unified across the swap-chain hand-off, and pf-client-core moves with them. The bulk of the diff is #4689: windows/windows-sys now generate straight from the Windows SDK, so the `Win32_*` namespace features became one feature per SDK header (winuser, dxgi, d3d11, …), the PascalCase namespace modules became header-named modules, struct-returning COM methods take explicit out-params and return HRESULT, Win32 functions return their raw BOOL/HANDLE instead of Result, and flag constants are plain integers. Both crates' Win32 code is rewritten to that shape; behaviour is unchanged on every path. Riding along, all already stale before the bump: the README and the three Windows workflows stop claiming windows-reactor's build.rs needs CARGO_WORKSPACE_DIR (that build.rs no longer exists — staging moved to windows-reactor-setup via OUT_DIR); the README layout section stops describing modules that moved into the session binary long ago and gains the manual smoke checklist; the notices generator learns the SPDX for crates that ship license files without a `license` field, which turns windows-reactor-setup's UNKNOWN into MIT OR Apache-2.0; and the crate records its real rust-version (1.96) instead of inheriting the workspace's 1.82. Verified: cargo check/clippy/fmt clean on punktfunk-client-windows, pf-client-core and punktfunk-client-session; both bins build; --discover finds the LAN hosts; the GUI shell comes up (WinAppSDK bootstrap intact under the new reactor-setup). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
79 lines
3.1 KiB
Rust
79 lines
3.1 KiB
Rust
//! Hide/restore the shell's top-level window around a spawned session, so exactly ONE
|
|
//! Punktfunk window is visible at a time: the spawned stream/browse window IS the app
|
|
//! while it runs (hidden = no taskbar entry, no Alt-Tab ghost), and the shell reappears
|
|
//! the moment the child exits — every exit path funnels through the spawn reader's
|
|
//! `Exited` event (clean end, error, crash, Disconnect kill), so the shell can never stay
|
|
//! hidden with no child.
|
|
//!
|
|
//! windows-reactor exposes no window handle, so the HWND is resolved by its (unique)
|
|
//! title and cached — the same pattern as `app::apply_window_icon_when_ready` and
|
|
//! `stream::window_dpi`.
|
|
|
|
use std::sync::atomic::{AtomicIsize, Ordering};
|
|
use windows::Win32::windef::{HWND, RECT};
|
|
use windows::Win32::winuser::{
|
|
FindWindowW, GetWindowRect, IsWindow, SetForegroundWindow, ShowWindow, SW_HIDE, SW_SHOW,
|
|
};
|
|
|
|
static SHELL_HWND: AtomicIsize = AtomicIsize::new(0);
|
|
|
|
fn shell_hwnd() -> Option<HWND> {
|
|
// SAFETY: the cached value is an `HWND` this process obtained itself; it is re-validated with
|
|
// `IsWindow` before use (a stale handle is treated as absent), and the lookup calls take only
|
|
// static wide literals.
|
|
unsafe {
|
|
let cached = SHELL_HWND.load(Ordering::Relaxed);
|
|
if cached != 0 {
|
|
let h = HWND(cached as *mut _);
|
|
if IsWindow(Some(h)).as_bool() {
|
|
return Some(h);
|
|
}
|
|
}
|
|
let h = FindWindowW(None, windows::core::w!("Punktfunk"));
|
|
if h.0.is_null() {
|
|
return None;
|
|
}
|
|
SHELL_HWND.store(h.0 as isize, Ordering::Relaxed);
|
|
Some(h)
|
|
}
|
|
}
|
|
|
|
/// Hide the shell while a spawned session window is up. Called on the child's
|
|
/// `{"ready":true}` (its window has presented — never earlier, so a failed connect keeps
|
|
/// the shell in view with its error banner).
|
|
pub(crate) fn hide() {
|
|
if let Some(h) = shell_hwnd() {
|
|
// SAFETY: `h` is the validated shell window handle from `shell_hwnd`; `ShowWindow` takes it
|
|
// plus a plain flag and dereferences nothing.
|
|
unsafe {
|
|
let _ = ShowWindow(h, SW_HIDE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Bring the shell back (and to the foreground) when the child exits. Safe to call when
|
|
/// it was never hidden — showing a visible window is a no-op.
|
|
pub(crate) fn restore() {
|
|
if let Some(h) = shell_hwnd() {
|
|
// SAFETY: as `hide` — `h` is the validated shell window handle, and both calls take only
|
|
// that handle plus a plain flag.
|
|
unsafe {
|
|
let _ = ShowWindow(h, SW_SHOW);
|
|
let _ = SetForegroundWindow(h);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// The shell window's top-left in desktop coordinates — passed to the spawned session
|
|
/// (`--window-pos`) so its window opens on the SAME monitor, roughly where the shell is,
|
|
/// and the visibility handoff reads as one window changing content.
|
|
pub(crate) fn position() -> Option<(i32, i32)> {
|
|
let h = shell_hwnd()?;
|
|
let mut r = RECT::default();
|
|
// SAFETY: `h` is the validated shell window handle and `r` is a live local the call fills.
|
|
if !unsafe { GetWindowRect(h, &mut r) }.as_bool() {
|
|
return None;
|
|
}
|
|
Some((r.left, r.top))
|
|
}
|