feat(clients/windows): single-window handoff, shared settings store, console-UI surfacing
UX polish batch 1 (clients/windows/ui-polish-plan.md, workstreams W0/A1/B/C/E):
- W0: the shell's duplicated trust/settings structs are gone — src/trust.rs
re-exports pf-client-core's, so the shell and the spawned session binary share
ONE Settings shape over client-windows-settings.json. Fixes real bugs: the
shell's saves no longer drop session-side fields; the stats-overlay toggle
(show_hud -> show_stats, serde alias migrates old files) and the forwarded-
controller pick now actually reach the spawned session. The "Streaming engine"
Settings combo is gone (PUNKTFUNK_BUILTIN_STREAM=1 stays as the dev A/B knob).
- Forwarded-controller pinning by stable key (vid:pid:name, pf-client-core's
format): persisted as forward_pad, applied by the shell's own service at
startup AND by the session binary in session_params (both OSes — the session
never applied the pin before).
- A1 single window: the shell hides itself when the spawned session window
presents its first frame ({"ready":true}) and restores + foregrounds on the
child's exit — exactly one visible Punktfunk window at any time. Restore runs
before the request-access cancel gate so a Ready/Cancel race can't strand the
shell hidden.
- C console UI: punktfunk-session --browse gains --json-status (ready when the
library window presents; error line on a failed start), and the shell
surfaces it — "Open console UI" on paired hosts' menus, plus a controller-
detected hint card targeting the most recent paired host (x64 only; aarch64
ships no skia ui feature). Browse spawns hide/restore the shell like streams.
- B responsive: minimum window size (420x360); the hosts header collapses to
icon-only buttons below 700 px; the session status card shrinks instead of
clipping (max_width); busy pages, help actions, licenses and long labels wrap.
- E polish: session exe gets the app icon (winresource + WM_SETICON via the new
pf-presenter win32.rs); both exes share an explicit AppUserModelID on
unpackaged runs (packaged identity wins) so the windows group as one taskbar
app across the visibility handoff; "Start streams fullscreen" toggle passes
--fullscreen (GTK parity); connects stamp KnownHost::last_used; the connect
target is stashed for every route so "Connecting to X" always names the host.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,8 @@ mod render;
|
||||
#[cfg(windows)]
|
||||
mod session;
|
||||
#[cfg(windows)]
|
||||
mod shell_window;
|
||||
#[cfg(windows)]
|
||||
mod spawn;
|
||||
#[cfg(windows)]
|
||||
mod trust;
|
||||
@@ -58,6 +60,7 @@ fn main() {
|
||||
use windows::Win32::System::Console::{AttachConsole, ATTACH_PARENT_PROCESS};
|
||||
let _ = AttachConsole(ATTACH_PARENT_PROCESS);
|
||||
}
|
||||
set_app_user_model_id();
|
||||
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
@@ -100,6 +103,27 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tag unpackaged (dev) runs with the explicit AppUserModelID that `pf-presenter`'s
|
||||
/// session window also adopts, so the shell and the stream window group as ONE taskbar
|
||||
/// app across the shell⇄session visibility handoff. MSIX runs already carry the package
|
||||
/// identity — overriding it would detach the window from the Start-menu pin, so packaged
|
||||
/// processes are left alone. Must run before any window exists.
|
||||
#[cfg(windows)]
|
||||
fn set_app_user_model_id() {
|
||||
use windows::Win32::Foundation::APPMODEL_ERROR_NO_PACKAGE;
|
||||
use windows::Win32::Storage::Packaging::Appx::GetCurrentPackageFullName;
|
||||
use windows::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
|
||||
unsafe {
|
||||
let mut len: u32 = 0;
|
||||
// No buffer: just probe whether the process has package identity.
|
||||
if GetCurrentPackageFullName(&mut len, None) != APPMODEL_ERROR_NO_PACKAGE {
|
||||
return; // packaged (or indeterminate) — leave the identity alone
|
||||
}
|
||||
// Must stay in sync with pf-presenter's win32.rs, or the windows stop grouping.
|
||||
let _ = SetCurrentProcessExplicitAppUserModelID(windows::core::w!("unom.punktfunk.client"));
|
||||
}
|
||||
}
|
||||
|
||||
/// `--headless --connect host[:port] …`: connect from the CLI, count frames, print stats — the
|
||||
/// Windows analogue of `punktfunk-probe`.
|
||||
#[cfg(windows)]
|
||||
@@ -192,6 +216,7 @@ fn run_headless_cli(args: &[String], identity: (String, String)) {
|
||||
port,
|
||||
fp_hex: trust::hex(&fp),
|
||||
paired: true,
|
||||
last_used: None,
|
||||
mac: Vec::new(),
|
||||
});
|
||||
let _ = k.save();
|
||||
|
||||
Reference in New Issue
Block a user