feat(clients/windows): GPU picker, disconnect shortcut, richer stream HUD
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m16s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 59s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m6s
apple / swift (push) Successful in 1m11s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m16s
apple / screenshots (push) Successful in 5m30s
android / android (push) Successful in 3m21s
ci / web (push) Successful in 52s
ci / rust (push) Successful in 1m26s
ci / docs-site (push) Successful in 59s
deb / build-publish (push) Successful in 3m19s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
ci / bench (push) Successful in 4m36s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m50s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 9m45s
docker / deploy-docs (push) Successful in 17s

- 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 5ef63756ea
commit bf799b41e3
9 changed files with 257 additions and 71 deletions
+22 -3
View File
@@ -23,7 +23,9 @@
//! **click on the stream** re-engages it. Losing foreground also releases the lock so the cursor
//! is never stranded; regaining it while still captured re-locks. When "capture system
//! shortcuts" is off in Settings, Alt+Tab / Alt+Esc / Ctrl+Esc / the Win keys act on the local
//! desktop instead of being forwarded.
//! desktop instead of being forwarded. **Ctrl+Alt+Shift+D disconnects** the session (consumed
//! locally, works captured or released while our window is foreground): it trips the session's
//! stop flag, the pump winds down, and the event loop navigates back to the host list.
use punktfunk_core::client::NativeClient;
use punktfunk_core::config::Mode;
@@ -34,7 +36,7 @@ use std::sync::{Arc, Mutex};
use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, POINT, RECT, WPARAM};
use windows::Win32::Graphics::Gdi::ClientToScreen;
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::Input::KeyboardAndMouse::VK_Q;
use windows::Win32::UI::Input::KeyboardAndMouse::{VK_D, VK_Q};
use windows::Win32::UI::WindowsAndMessaging::{
CallNextHookEx, ClipCursor, GetClientRect, GetForegroundWindow, SetCursorPos,
SetWindowsHookExW, ShowCursor, UnhookWindowsHookEx, HC_ACTION, HHOOK, KBDLLHOOKSTRUCT,
@@ -46,6 +48,8 @@ use windows::Win32::UI::WindowsAndMessaging::{
struct State {
connector: Arc<NativeClient>,
mode: Mode,
/// The session's stop flag (Ctrl+Alt+Shift+D trips it; the pump then ends the session).
stop: Arc<AtomicBool>,
/// Our window handle, stored as the raw `isize` so `State` is `Send` (`HWND` is not).
hwnd: isize,
/// User intent: forward input to the host (toggled by Ctrl+Alt+Shift+Q / click-to-capture).
@@ -99,11 +103,18 @@ fn set_captured(st: &mut State, on: bool) {
/// Install the hooks for a streaming session. Call from the UI thread once the window is shown.
/// `inhibit_shortcuts` forwards system shortcuts (Alt+Tab, Win, …) to the host; off = local.
pub fn install(connector: Arc<NativeClient>, mode: Mode, inhibit_shortcuts: bool) {
/// `stop` is the session's stop flag, tripped by the disconnect shortcut.
pub fn install(
connector: Arc<NativeClient>,
mode: Mode,
inhibit_shortcuts: bool,
stop: Arc<AtomicBool>,
) {
let hwnd = unsafe { GetForegroundWindow() };
let mut st = State {
connector,
mode,
stop,
hwnd: hwnd.0 as isize,
captured: false,
inhibit_shortcuts,
@@ -266,6 +277,14 @@ unsafe extern "system" fn kbd_proc(code: i32, wparam: WPARAM, lparam: LPARAM) ->
tracing::info!(captured = on, "capture toggled (Ctrl+Alt+Shift+Q)");
return LRESULT(1);
}
// Disconnect: Ctrl+Alt+Shift+D (consumed locally). Release capture immediately so
// the cursor is free while the session winds down and the UI navigates home.
if !up && vk == VK_D.0 && st.ctrl && st.alt && st.shift {
set_captured(st, false);
st.stop.store(true, Ordering::SeqCst);
tracing::info!("disconnect requested (Ctrl+Alt+Shift+D)");
return LRESULT(1);
}
if st.captured {
// With shortcut capture off, hand Alt+Tab & co. to the local desktop —
// neither forwarded nor swallowed.