feat(clients/windows): GPU picker, disconnect shortcut, richer stream HUD

- Settings gains a GPU selector (shown only on multi-GPU boxes): the picked
  DXGI adapter drives decode + present, persisted as Settings.adapter and
  applied at the next stream - gpu.rs now caches the shared device keyed by
  the resolved preference (env PUNKTFUNK_ADAPTER > Settings > the window's
  monitor's adapter) so a change needs no app restart.
- Ctrl+Alt+Shift+D disconnects the session (consumed locally, captured or
  released): the hook releases capture and trips the session stop flag,
  plumbed through the stream-page handoff; the pump winds down and the UI
  navigates back to the host list.
- Stream HUD extended: codec chip (HEVC/H.264/AV1), display-side line from
  the render thread (presents/s + capture-to-decoded vs capture-to-on-glass
  p50), session line (host name, duration, network-lost frames, skipped
  backlog frames), and both shortcut hints incl. the new disconnect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 16:41:16 +02:00
parent 76e2a1f5a7
commit 9d6798b8e0
9 changed files with 257 additions and 71 deletions
+7 -2
View File
@@ -95,10 +95,14 @@ impl PartialEq for Svc {
}
}
/// Cross-thread handoff from the session pump (off-thread) to the stream page (UI thread).
/// Cross-thread handoff from the session pump (off-thread) to the stream page (UI thread):
/// the connector (input sends), the decoded-frame channel (render thread), and the session's
/// stop flag (the disconnect shortcut trips it).
#[derive(Default)]
pub(crate) struct Shared {
pub(crate) handoff: Mutex<Option<(Arc<NativeClient>, crate::session::FrameRx)>>,
#[allow(clippy::type_complexity)]
pub(crate) handoff:
Mutex<Option<(Arc<NativeClient>, crate::session::FrameRx, Arc<AtomicBool>)>>,
pub(crate) target: Mutex<Target>,
/// Latest stream stats, written by the session's event loop and mirrored into reactor state
/// by the HUD poll thread to drive the overlay.
@@ -231,6 +235,7 @@ fn root(cx: &mut RenderCx, ctx: &Arc<AppCtx>) -> Element {
set_hud.call(stream::HudSample {
stats: *shared.stats.lock().unwrap(),
captured: crate::input::is_captured(),
present: crate::render::present_stats(),
});
})
.ok();