Repository navigation
Conversation
… 148 (#1237) * fix(qt): show stream notices at the minimum window size with stats open The stream toast column gated its own visibility on a fixed 148 px budget. At the 960x540 minimum with the stats overlay expanded the panel pushes the column to y 378, so the budget demanded 526 of the 516 available and the column hid itself. Because `active` was defined as `visible && streaming`, hiding destroyed the queued notice instead of deferring it: neither the colour-format notice nor the HDR downgrade notice was ever created. Gate on the column's real stack height instead, and arm it independently of its own visibility so a stack that does not fit is deferred rather than dropped. Without the second clause the gate oscillates whenever the stack genuinely does not fit, because hiding collapses the height to zero. Pre-existing defect: DesktopStreamOverlayHost.qml is absent from #1232's diff and the gate is byte-identical at its merge parent. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): keep stream notices pending until they are actually presented Review found that a hidden stack kept running the notice observers, so claimHdrNotice persisted the game as shown and the four-second timers expired while the notice was never on screen. Leaving stats open past the timeout discarded the notice and permanently suppressed that game. Presentation of the two consuming notices now sits behind the column's visibility: a notice that cannot fit stays queued with no claim and no display lifetime until there is room for it. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): hold the packet-loss notice while the toast stack is hidden Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…den (#1241) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… ZIP only (#1243) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ays a real RTT (#1242) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…keyframes; flag decode-bound streams (#1244) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…riting the host's (#1245) * fix(release): configure candidate graphics and signing keychains (#931) * fix(release): pass inline requirements to macOS codesign (#932) * Add Android download link to README Added a link to download the Android build in the README. * Update README.md * fix(qt): map regional keyboards from physical keys (#1010) * Fix: 1. Dead keys (å ä ö § ´ ¨ and the rest) 2. No keyboard layout setting 3. Clock 4 hours off * fix(qt): complete regional keyboard mapping and session layout ownership --------- Co-authored-by: Thorulf <228320212+Thorulf@users.noreply.github.com> * release: prepare stable v1.0.2 (#1086) * fix(streamer): negotiate compatible Alliance video SETUP forms * chore(streamer): diagnose rejected video Transport fields safely * fix(linux): discover host VA-API drivers in AppImage launches (#935) * feat(qt): explain cloud session setup and native connection progress * fix(session): preserve terminal and allocation ownership across recovery * test: align recovery and package fixtures with session contracts * fix(auth): enforce credential and account lifecycle ownership * fix(auth): restore secure grants when legacy cleanup is deferred * fix(alliance): scope provider routing and session ownership * fix(session): retain seat ownership across account generations * style(auth): center the desktop sign-in card * fix(catalog): track complete library and sync convergence * fix(catalog): preserve traversal ownership across launch and sync * feat(library): add cloud ownership and favorites with guarded launches * feat(search): query remote games from the command palette * fix(search): preserve local command keyboard activation * feat(settings): share validated language and stream profile choices * fix(test): locate bundled core through the application target * fix(catalog): omit campaign dependency from targeted game lookups * fix: restore authenticated launches and display chroma fallback (#949) * fix(diagnostics): retain redacted catalog error details * fix(catalog): send linked-account queries as JSON * fix(catalog): accept nullable definition lists * fix(catalog): normalize the no-subscription sentinel * fix(streaming): display chroma fallback and notify once (#950) * fix(streaming): preserve playback on chroma-only fallback * feat(streaming): notify once when video color format changes * test(qt): run Windows color notices on the native platform * fix(streaming): debounce connection warnings and clear them on recovery (#952) * fix(ui): keep game details padded and accessible (#953) * fix(ui): keep game details padded and accessible * fix(core): reject requests outside the negotiated ready state * fix(test): polish game-details layout before measuring it (#954) * Fix bounded Opus PLC on the embedded Linux audio path * Preserve physical gameplay key identity on Linux * Isolate embedded Linux audio failures from video * Keep Discord presence on a bounded persistent IPC connection * Preserve bounded Windows cancellation recovery for Discord * Publish Linux video readiness after entering Running * Satisfy Windows Clippy for Discord pending I/O errors * Validate VAAPI color profiles on the selected decode device * Keep Qt and core media paths on the same Pictures root * Make media path regression tests portable to Windows * Carry validated display luminance into HDR session requests * Measure native frame stages and Qt presentation progress * Include presentation timing tests in the CI target contract * Launch the eligible Steam client for persistent storage management * Publish decoder progress even when frame admission stops * Keep store launch progress and failures on the storage screen * Select explicit embedded Linux software decoding * Gate 360 FPS on entitlement and effective hardware decoding * feat(core): receive account push updates natively * feat(streaming): negotiate bandwidth-saving quality policy * feat(network): measure selected-zone UDP payload reachability * fix(streaming): recover stalls from actual frame progress * Make push lifecycle tests deterministic under contention * Receive complete UDP probe replies before correlation * Pace software decode fixture using observed decoder admission * Forward complete Sony snapshots through session-owned native HID * Keep nightly versions ahead of published stable releases * fix: send private NACK-v2 feedback for Mjolnir video * fix: pace Mjolnir NACK retries using current RTT * feat: add desktop PrintedWaste queue selection * fix: require queue capability during core handshake * fix: align NVST color negotiation with the official client (#981) * fix(updater): repair discovery, portable installation and failure reporting (#982) * fix(updater): repair release discovery and Linux update delivery * fix(ci): use verified Ubuntu mirror failover * fix(updater): repair portable replacement and surface install failures * refactor: simplify and align desktop settings (#983) * refactor: simplify and align desktop settings * fix: preserve resolution stepper and frame rate pills * fix: tighten settings spacing and expose codec selection * fix: bound each network probe attempt by its original deadline * fix(ui): unify brand lockup and repair desktop misalignments (#984) Replace the hand-drawn onboarding mark with a shared DesktopBrandLockup backed by a hi-res mark cropped from the canonical logo, and fix the misaligned, clipping, and out-of-reach desktop elements found in the screenshot audit: About brand row and toggle icons, empty store shelf headers colliding with the loading state, compact settings nav sections cut off past the fold, dead Updates header search, library cell geometry that clipped artwork at scale, and the empty-home See all count. * fix(streamer): harden IPv6 RTSPS endpoints without widening trust * fix(qt): repair provider, login, account and recovery flows * fix(test): run queue selector tests in the production Basic style * fix(test): cover queue selector tests in the CI unit-test contract * fix(test): set production font in queue selector tests for warning-free runs * fix(test): load bundled mono fonts in queue selector tests * fix(test): select Basic style via environment to avoid a new Windows DLL dependency * fix(test): scope queue selector CI coverage off Windows pending local diagnosis * fix(alliance): fall back Digevo prod endpoint to LATAM West NVIDIA discovery still advertises https://prod.DIG.geforcenow.nvidiagrid.net/ for Digevo, but that hostname has no DNS records globally, so region discovery, VPC lookup, and all CloudMatch routing for Digevo accounts failed. Route Digevo to the working regional endpoint https://latam-west.dig.geforcenow.nvidiagrid.net/ (server NPA-DIG-SCL-01, region LATAM West), verified live with serverInfo, session list, session create, and first video frame at 1080p60 H264. The fallback stays within the existing HTTPS nvidiagrid.net trust policy; no protocol or ABI change. * fix(alliance): engage provider fallback only when discovery host is unreachable (#991) Follow-up to the Digevo LATAM West fallback. Alliance prod.* endpoints are geo-steered: prod.dig resolves to the nearest PoP (e.g. LATAM North via Colombia) from inside the footprint and NXDOMAINs outside it. The unconditional rewrite forced all Digevo users onto LATAM West and broke steering for North users. The fallback table now engages per-provider only when the advertised host fails DNS resolution, keeping native geo-steering untouched when prod works (VPN / in-footprint) while still rescuing out-of-footprint users. Resolver is injectable for deterministic tests. * streamer(vulkan): prioritize capable dGPUs and fix AppImage GLib clash - Filter and score Vulkan physical devices based on video decode queue isolation and PRIME offload variables instead of indexing device 0. - Fall through to alternative physical devices when queue isolation fails. - Exclude host GLib and GObject binaries from the AppImage packaging pipeline to resolve host VA-API driver dlopen symbol clashes. - Ensure proper sync object drain before Vulkan teardown under Wayland. * fix(test): isolate Vulkan device scoring preferences from process env Score computation now takes an explicit VulkanDevicePreferences value, with a thin from_env wrapper preserving the OPENNOW_VK_* / PRIME behavior. The unit tests exercise the pure function directly, so they no longer mutate process-wide env vars and cannot flake when cargo runs them in parallel. Also addresses review nits: sort_by_key, drop the redundant unsafe around the safe options builder, and fall back to default device selection if the index string ever fails CString conversion instead of unwrapping. * fix(streamer): re-sweep video SETUP on a bounded pace when the rig has no peer yet A rig whose video streamer is still starting answers SETUP with 200 but an empty Transport header. The negotiator fired all URI/Transport forms in under a second and terminally failed with missing-video-peer, never giving the rig a chance. Re-sweep on a bounded pace (3s pauses, at most 3 extra rounds) when at least one 200 arrived without a usable peer, inside the existing 20s request budget. Pure rejections still fail fast, the first SETUP request is byte-identical, and the terminal missing-video-peer code is unchanged after exhaustion. New video-setup-retry log line keeps rounds observable without logging addresses. * fix(streamer): align 10-bit color negotiation and codec gating with official client CloudMatch now sends the official 15-field requestedStreamingFeatures set (codec, bitrate, vsync, channel count, QoS, touch, and dynamic policy stay client-side/RTSP-only); session metadata drops non-official pairs and externalAppId. Codec selection stays client-side end to end with settings fallbacks, and HDR validation uses the normalized codec. NVST ANNOUNCE states color explicitly per the vendor capture (literal bitDepth plus chroma_format_idc, dynamicRangeMode for HDR only) and carries maxCodec/maxH264 profile/level for encoder init, replacing the delta-suppressed 0/1 encoding that left seats unable to start Main10 video. Settings pages grey out codecs per selected color quality via core-resolved descriptors (H.264 is 8-bit 4:2:0 only, AV1 is 4:2:0 only), gate 10-bit/HDR on Ultimate/Performance membership and 4:4:4 on Ultimate plus Windows/macOS, and heal persisted invalid codec/color combos toward Auto on first launch. * fix(lint): satisfy rustfmt and clippy 1.96 nonminimal-bool rustfmt formatting for the color-negotiation changes; clippy-suggested De Morgan simplifications including two pre-existing nonminimal-bool sites surfaced by the current stable toolchain. * fix(lint): drop unused mut in codec omission test * fix(qml): pass codec argument through in codecDisabledByProfile ReferenceError (code vs codec) broke the desktop codec row bindings whenever decoder capabilities were present, failing settings-streaming acceptance on capability fixtures. * fix(vaapi): export the DRM PRIME surface after it is decoded into `to_native_handle` exported the surface immediately after allocating it, and every decoded frame then replayed that cached descriptor. A driver is not required to have the final buffer ready at allocation time. `libva-v4l2_request-avd` documents both halves of the problem: a surface exported before it is ever decoded into gets standalone dma-buf backing that "is dropped once the surface is bound to a real decode context", and its CAPTURE queue "is configured lazily on the first frame, after the codec controls (SPS etc.) have been set". The cached descriptor therefore refers to a throwaway allocation whose geometry was never negotiated, and the picture is unusable while decoding itself succeeds. Defer the export to first use, which happens after `sync()` in `drain_events`, and cache it from there. The driver returns a stable dma-buf per VASurfaceID once the surface is bound, so the descriptor stays valid for the frame's lifetime. cros-libva deliberately prevents constructing its safe descriptor outside the crate, so the deferred export uses `vaExportSurfaceHandle` directly and owns the returned file descriptors. * fix(vulkan): import DMA-BUF frames at their coded height A decoder may pad its output taller than the visible picture; the Apple AVD block emits 1920x1088 for a 1080p stream. The chroma plane then sits at `pitch * 1088`, but the imported image was created at the visible height, so a driver that derives plane offsets from the image extent rather than honouring the explicit plane layout read chroma from `pitch * 1080` instead. Luma is unaffected because it starts at offset 0, so the picture keeps its shape and only its colour is displaced: a vertically offset ghost of the image in its own chroma. Derive the coded height from the exporter's chroma offset and create the image with it, so both readings agree whether or not the explicit layout is honoured, then scale sampling by `visible / coded` to keep the padding rows off screen. When there is no padding the scale is 1.0 and the path is unchanged. The fragment shader scaled texture coordinates about the centre, which would shift the picture by half the cropped padding; anchor it at the origin instead. Existing callers pass a scale of 1.0, for which both forms are identical. Also fix two spec violations in the same importers, which is what allowed the explicit layout to be disregarded in the first place: * `VkSubresourceLayout::size` must be 0 in every element of `pPlaneLayouts` (VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-size-02267). * DMA-BUF images produced by V4L2/VA-API come from a foreign queue family, not another Vulkan device, so the acquire and release barriers must use `VK_QUEUE_FAMILY_FOREIGN_EXT`. The SAND importer already did this; the generic one did not. * fix(core): carry width and height in the negotiated stream profile The profile exposed the negotiated geometry only as a `resolution` string. `ShellStore.normalizedStreamingSession` splits it into `width` and `height`, but the desktop stream screen reads those fields off the session it is given, so they can arrive undefined. `StreamVideoItem` then receives `Qt.size(0, 0)`, and `aspectFitRect` returns the full item rect for an empty video size, stretching the picture to the window. On a 16:9 display this is invisible, because filling the window is also the correct result. On anything else the picture is distorted: a 16:9 stream on a 3024x1964 panel is stretched about 15% vertically. The same rect drives input mapping, so pointer coordinates are mapped through the distorted geometry too. Emit `width` and `height` next to `resolution` so consumers do not have to re-parse the string. Both are null when the monitor does not report its geometry, which is the existing behaviour. * fix(streamer): stop SETUP at first 200 and fall back to bundle peer when rigs omit Transport (#998) Live YES/ABYA rigs 200 the first video SETUP with an empty Transport header and 400 every further form; ANNOUNCE after such a burst is then rejected, while a single SETUP followed by ANNOUNCE succeeds. Sweeping all forms poisons these sessions, and the old code did exactly that. Stop each sweep at the first 200 (with or without a peer). Peerless 200s still re-issue the same stopped-at form on the bounded #993 pace for late-starting encoders, and terminal exhaustion with a CloudMatch bundle peer proceeds on it (mirroring the official client, which carries on when the server omits transport) instead of failing the seat. Pure rejections still fail fast; the first SETUP request is byte-identical. Also adds redaction-safe DESCRIBE shape logging and an OPENNOW_STREAMER_LOG_FILE sink for the standalone child so headless negotiation failures stay diagnosable. * fix(vulkan): apply the coded-height import to the standalone presenter Review follow-up to "import DMA-BUF frames at their coded height". The coded height was derived by dividing the chroma offset by the luma pitch, which silently assumes the luma plane starts at offset 0. Both offsets are relative to the same object and the importers accept a nonzero luma offset, so a buffer placed after a header reported too many rows and cropped a picture that needed no cropping. Derive it from the distance between the two offsets instead. That derivation now lives in `format.rs` as `nv12_coded_height`, shared by both importers and unit-tested directly, since it needs no device. The divisibility and visible-height guards are unchanged, and anything that does not describe a cleanly padded pair still falls back to the visible height. `VulkanPresenter` carried none of this. It is a second DMA-BUF importer with its own shader, so a padded buffer kept exactly the chroma ghost the other path had just been fixed for. It now imports at the coded height and crops in `nv12.frag`. `nv12.frag` already had a `texture_scale`, but it is the letterbox mapping: centred, with the bars painted black outside the source. The padding crop has to be anchored at the origin and applied after the bounds test, so it is a separate `coded_scale` rather than a change to the existing factor. It is `[1.0, 1.0]` for every unpadded source, which is every upload path. `nv12.frag.spv` is regenerated with `glslc -O`, which is what the tracked binary was built with. The presenter also still acquired and released its imported images from `VK_QUEUE_FAMILY_EXTERNAL`, promising a matching release from a Vulkan device that never comes. Both barriers now use `VK_QUEUE_FAMILY_FOREIGN_EXT`, and `VK_EXT_queue_family_foreign` joins the extensions the presenter probes and enables: without it the import has no legal way to take ownership, so it belongs in the contract rather than being optional. For the embedded path the extension was already requested, but `hasDmabufImportContract` did not require it while the image importer had started issuing foreign-queue transfers. It does now, matching `hasDmabufBufferImportContract`. * fix(streamer): speed up software decode and NV12 upload Cap FFmpeg software frame threads and set low-delay so extra logical CPUs do not add a frame of latency each. Pack YUV420P into tight NV12 without a bilinear scale, and keep the Vulkan upload buffer mapped. Co-authored-by: Zortos <zortosdev@proton.me> * fix(streamer): keep a queued keyframe when the D3D11 decode queue overflows Intel iGPU sessions were falling through to the Microsoft software H.264 decoder, then wiping the only queued IDR on every overflow. That requested a new keyframe for each delta and froze the picture. Preserve a queued keyframe, ask for one IDR per gap, and try unscoped hardware MFTs before the software fallback. Co-authored-by: Zortos <zortosdev@proton.me> * fix(streamer): keep software decode slice-threaded and drop corrupt frames Frame threading still held about one frame per worker before the first output. Slice-only threading keeps the 2–4 thread budget inside a single frame. Corrupt software frames now fail as ReferenceLost so recovery matches V4L2. Co-authored-by: Zortos <zortosdev@proton.me> * fix(qt): choose a hybrid GPU that can hardware-decode Automatic indexed only the high-performance adapter, so a GeForce MX110 with no decoder failed the embedded session before Intel HD Graphics 620 was considered. Index H.264, HEVC, and AV1 on each adapter, select the first one that can decode, and list those codecs under Stream settings. Co-authored-by: Zortos <zortosdev@proton.me> * fix(windows): try untagged hardware decoders before the Microsoft MFT Intel Quick Sync does not publish MFT_ENUM_ADAPTER_LUID, so a hardware search filtered to the Iris Xe adapter is empty and the Microsoft H.264 MFT decodes on the CPU. When that filter matches nothing, enumerate hardware transforms with no adapter attribute first. Co-authored-by: Zortos <zortosdev@proton.me> * fix(streamer): satisfy clippy and the Windows HID start test hold_keyframe_request is only called from the embedded D3D11 path, so non-Windows clippy rejected the library as dead code. Gate it the same way as the other Windows queue helpers, and keep the unit tests. The HID lifecycle test sent video to a closed UDP port. On Windows that resets the socket, the bundle thread exits, and HID unbinds before the test can see the binding start() installed. Hold a live peer instead. Co-authored-by: Zortos <zortosdev@proton.me> * fix(streamer): ignore Windows ICMP port unreachable on NVST UDP Windows reports a closed peer as WSAECONNRESET. The video receiver treated that as fatal, exited, and closed the HID endpoint while the session was still connected. Disable SIO_UDP_CONNRESET on NVST sockets and keep a connection-reset receive in the poll loop. Co-authored-by: Zortos <zortosdev@proton.me> * fix(qt): stop windows.h from expanding the decoder profile bound MSVC defines min as a macro, so std::min in the adapter decode index was a syntax error. Bound the profile count with a comparison and include Windows headers with NOMINMAX. Co-authored-by: Zortos <zortosdev@proton.me> * fix(streamer): keep HID bound when Windows reports ICMP port unreachable A hole-punch ping to a closed UDP peer makes Winsock return WSAECONNRESET. Treating that as a fatal receive error exited the NVST worker and closed the HID endpoint before the session start assertion. Disable SIO_UDP_CONNRESET on reserved sockets and ignore ConnectionReset on the UDP receive and send paths. Co-authored-by: Zortos <zortosdev@proton.me> * fix(updates): quote MSI INSTALL_ROOT to avoid msiexec 1639 and clear terminal transactions (#996) * feat(update): install Windows with setup.exe and swap the ZIP New installs copy the portable tree to %LOCALAPPDATA%\OpenNOW without an MSI product. Later updates keep the signed ZIP rename path. An MSI-registered tree is told to replace itself once with setup.exe and is not handed to msiexec. Co-authored-by: Zortos <zortosdev@proton.me> * fix(release): require setup.exe in the platform artifact set The candidate inventory already publishes Windows setup.exe, so nightly assembly and promotion must treat those files as required packages. Signature and checksum checks stay exact; only the frozen counts of ten and twelve platform artifacts change. Co-authored-by: Zortos <zortosdev@proton.me> * fix(qt): map German gameplay keys from the physical position Windows forwarded the active layout's virtual key, so ü was sent as the ö position and the hyphen key was sent as ß. Gameplay input now uses the set-1 scan code's US position, which is what the remote layout expects. Co-authored-by: Zortos <zortosdev@proton.me> * fix: keep a 220 kbps bitrate request below the 1 Mbps floor A configured max bitrate such as 0.22 Mbps is stored and announced as 220 kbps instead of being raised to 1000 kbps. A missing max bitrate stays 75 Mbps, and whole-number settings remain JSON integers. Co-authored-by: Zortos <zortosdev@proton.me> * fix(qt): map macOS gameplay keys from the physical position macOS hardware key codes now select the US virtual key on every layout, matching the Windows scan-code and Linux XKB paths. German ü and hyphen stay on their physical positions, and AZERTY or Cyrillic characters cannot move the key. Co-authored-by: Zortos <zortosdev@proton.me> * fix: resolve pre-nightly reliability and packaging audit findings (#1011) * fix: resolve pre-nightly reliability and packaging audit findings * fix: port regional keyboard mappings onto dev * fix(ci): restore timestamped package compiler caches on all platforms (#1012) * perf(streamer): skip stale decoded frames before conversion (#1016) * fix(streamer): use bundle peer without retrying SETUP (#1015) * feat(qt): redesign stream shortcut settings (#1017) * fix(qt): allow clearing stream shortcut bindings * test(qt): assert clearable stop shortcut contract * feat(qt): redesign shortcut bindings with inline editing * fix(qt): keep shortcut bindings alive through teardown * fix(qt): expose missed UI labels to localization (#1018) * fix(streamer): encode the native microphone in 10 ms frames (#1014) * fix(streamer): encode the native microphone in 10 ms frames The native microphone uplink reached the network, but the cloud PC's GeForce NOW microphone stayed silent: Steam's voice test showed no input on any device. The server's mic config sets `mic.frameSize:10`, and the uplink sent 20 ms Opus frames. Encoding 10 ms frames (480 samples at 48 kHz) is enough to make the microphone audible, with nothing else changed. Tested end to end on Linux (Asahi) by trying one change at a time. Sending on SSRC 5 (the SSRC the server gives the browser client), on the game-audio mid, and wrapped as RED level 3 all left the microphone silent, even combined, while 20 ms frames were used. The 10 ms change alone, on the existing SSRC 1 / mid 2 / plain Opus path, made it work. * test(streamer): name the microphone encoder test for its 10 ms frame * fix(streamer): recover stalled video before decoder submission (#1019) * fix(streamer): recover VA-API frame export failures (#1020) * fix(streaming): route Alliance sessions and align NVST feedback (#1022) * fix(streamer): preserve SDR colorimetry across resolution and HDR transitions (#1021) * fix(streamer): preserve SDR colorimetry across HDR transitions * test(streamer): accept explicit SDR output before HDR transition * fix(streamer): align custom SCTP channel reliability (#1024) * fix(streamer): preserve server-owned ANNOUNCE settings (#1025) * fix(streamer): leave unverified QoS v7 fields unset (#1027) * fix(core): advertise native WebSocket signalling metadata (#1023) * fix(streamer): gate input activation until PLAY succeeds (#1026) * fix(streamer): batch and negotiate QoS feedback (#1028) * fix(test): exercise current fullscreen stats shortcut in smoke (#1030) * fix(core): persist large Windows sessions securely across restarts (#1031) * fix(core): persist large Windows sessions securely across restarts * test: make Windows credential migration failure fixture portable * fix(streamer): honor negotiated pacing feedback mode (#1033) * fix(streamer): derive bundle ICE identity from advertised port (#1035) * fix(qt): reset queue poll failure budget on success (#1036) * fix(streamer): correct Linux frame pacing mismatch thresholds (#1037) * fix(core): gate Auto AV1 on known capabilities (#1040) * fix(streamer): bound backlog and preserve reference recovery (#1042) * fix(core): cancel fresh CloudMatch sessions on returned control host (#1034) * fix(core): cancel fresh sessions on returned control host * fix(core): fail closed on persisted session cleanup stops * fix(core): preserve explicitly selected CloudMatch zones (#1038) * fix(core): encode HDR minimum luminance in protocol units (#1039) * fix(streamer): honor bounded negotiated QoS batch size (#1044) * fix(streamer): avoid startup IDR and probe primary port (#1041) * fix(streamer): omit unused channels on dedicated video (#1043) * fix(windows): keep SDR matrix through HDR output transition (#1045) * diagnostics(windows): describe decoder color mismatch * fix(windows): follow SDR matrix on HDR output transition * fix(streamer): report measured frame receive timing * fix(streamer): report measured bandwidth feedback * diagnostics(streamer): record reported bandwidth with received video bytes * fix(hdr): use measured Windows monitor metadata for native HDR * fix(stats): report measured stream UDP receive rate * fix(qt): preserve exclusive controller selection on disconnect (#1061) * fix(diagnostics): preserve safe stream recovery and decode progress (#1062) * fix(qt): restore Swedish OEM input positions (#1063) * fix(streamer): rearm decode watchdog after recovered output (#1064) * fix(qt): retire stale polls when replacing store sign-in (#1065) * fix(qt): retire polls when replacing account sign-in * fix(qt): keep intentional cancellations out of global errors * fix(streamer): preserve atomic updates across Rust versions (#1073) * fix(streamer): poll media deadlines after STUN datagrams (#1066) * fix(build): run the license generator for the Rust host target (#1068) * fix(linux): select a usable native H.264 VA-API device (#1069) * fix(qt): confirm native window close without disturbing sessions (#1070) * test(qt): cover native window close confirmation * fix(qt): confirm native window close without disturbing sessions * fix(startup): enforce process-lifetime profile and endpoint ownership (#1071) * fix(streamer): keep unresolvable packet trains from lowering bandwidth estimate (#1057) A frame whose packets arrive within the receive clock's resolution (most often a single-packet frame in a static scene) has its receive span clamped to 0.5 ms, so bytes/0.5 ms was averaged in as measured capacity. For a ~1 KB packet that is ~16.5 Mbps, which the server then honored, shrinking frames further and holding the stream at a low bitrate. Treat trains shorter than 1 ms as a lower bound: they may raise the estimate but no longer drag it down. Co-authored-by: Dinoway <26033046+Dinoway@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(core): send clientTimezoneOffset as local minus UTC (#1058) #877 replaced the hardcoded 0 with UTC-minus-local milliseconds, but CloudMatch applies the value as local minus UTC. A client at UTC-5 sent +18,000,000 and the VM clock came up at UTC+5, ten hours ahead. Android already sends local minus UTC via TimeZone.getOffset. Flip the sign and cover it with fixed-offset tests, since the existing test compared the body against the same expression it was built from. Co-authored-by: Dinoway <26033046+Dinoway@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(auth): add private JSON fallback for unavailable credential stores (#1076) * fix(desktop): reconcile restarted sessions and serialize settings writes (#1074) * fix(desktop): close account, settings, and recovery audit gaps (#1082) * fix(desktop): close account, settings, and recovery audit gaps * test(core): isolate media CLI profiles with atomic temporary directories * fix(updater): tolerate transient Windows metadata readers * fix(qt): label 4:4:4 color profiles as coming soon (#1083) --------- Co-authored-by: Zortos <230910855+capy-ai[bot]@users.noreply.github.com> Co-authored-by: Kief5555 <67562560+Kief5555@users.noreply.github.com> Co-authored-by: quadsteel <blacksteel@outlook.com.gr> Co-authored-by: makhov <makhov.alex@gmail.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com> Co-authored-by: Dinoway <Dinowaygaming@gmail.com> Co-authored-by: Dinoway <26033046+Dinoway@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(flatpak): pin QT_QPA_PLATFORM to wayland;xcb in the sandbox Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> Co-authored-by: Kiefer <67562560+Kief5555@users.noreply.github.com> Co-authored-by: Thorulf <228320212+Thorulf@users.noreply.github.com> Co-authored-by: quadsteel <blacksteel@outlook.com.gr> Co-authored-by: makhov <makhov.alex@gmail.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: cursor[bot] <206951365+cursor[bot]@users.noreply.github.com> Co-authored-by: Dinoway <Dinowaygaming@gmail.com> Co-authored-by: Dinoway <26033046+Dinoway@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…1247) * feat(qt): add --fullscreen and --quit-after-session launch options Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(qt): add --fullscreen and --quit-after-session launch options Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): quit after a server-ended session; drop the stray plugin archive Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…up (#1248) * fix(qt): hold the quit while a replacement launch is set up Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): hold --quit-after-session while a replacement launch is set up Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: drop the stray plugin archive Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ops crackling (#1251) * fix(linux-audio): keep a 40 ms lead in the pw-cat pipe so playback stops crackling Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(linux-audio): leave the pw-cat pipe at its default capacity Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…no in-stream hint bar (#1250) * feat(deck): Steam Deck identity by default, View+Menu session chord, no in-stream hint bar Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(deck): hold the first chord button briefly so View+Menu never reaches the game Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(deck): time the View+Menu chord per event and never replay chord taps together or across capture Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(deck): end a pending chord tap before recording the next chord press Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(decky): add an OpenNOW plugin for Steam Game Mode Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): confirm before switching games, wait for exit, lock settings writes, flag unknown versions Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(decky): implement design B (slim launcher, full-screen settings, play-bar button) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): run flatpak with the host's libraries, not Decky's bundled ones Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): put Play on OpenNOW in Steam's new game-page play bar Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): play from the game page through the OpenNOW shortcut instead of adding one Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): set shortcut launch options with SetShortcutLaunchOptions and restore them once OpenNOW starts Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): resume the game already streaming and report when OpenNOW doesn't start Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): ask whether to resume or restart a game that's already open in OpenNOW Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): wait for the catalog before matching a game page Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ci(decky): run the plugin workflow on a Blacksmith runner Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(decky): stop tracking the built plugin zip Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ci: run the required Qt checks on Decky plugin pull requests Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…he keyring (#1252) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(decky): let the plugin install and follow OpenNOW nightlies Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): keep the new channel's release when an old-channel install finishes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…lugin download link (#1254) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(decky): detect and install OpenNOW on DroidDeck Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): bound the DroidDeck helper and keep the arch lookup off the event loop Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(decky): keep the watchdog until the DroidDeck helper is reaped Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…l it's fixed (#1256) * fix(auth): measure ID-token expiry from its lifetime, not the local wall clock Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(auth): refuse sign-in and warn when the system clock is wrong Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…s advice (#1261) * fix(qt): wrap the decoder-limited notice instead of eliding its advice The decoder-limited stream notice carries a 94-character instruction. Without formatNotice it took the single-line path, so the half of the sentence that says what to do was cut off. Every other long-copy notice in the stack already wraps. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(qt): pin the decoder notice's wrapped copy at three window sizes The notice could be raised by no shipped fixture, so nothing painted it. The new fixture drives the decoder-limited state through ShellStore's queue-dropped hook and asserts the instruction is not truncated, wraps, grows the notice, and stays inside the window. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): reserve the toast column for the notices that are showing The gate reserved pendingNotices x tallest-child-height, so a tall notice reserved its height for every pending notice even when it was not on screen. Summing the pending notices keeps a wrapped notice honest: it reserves the wrapped height only while it is the one on screen. The estimate still reads real child heights, so there is no fixed-height constant and no loop through the column's own visible. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…k identity (#1259) The core turns identifyAsSteamDeck on once on Steam Deck hardware and records steamDeckIdentityDefaultApplied so it never overrides the choice afterwards. The plugin wrote identifyAsSteamDeck without that marker, so a user who turned identity off in the plugin before OpenNOW had applied its default saw the core turn it back on at the next start. Writing the marker with the choice keeps the core contract unchanged and makes the plugin's setting stick. Verified with python3 -m unittest discover -s tests -t . (82 tests) and by feeding the written file to the core under Deck DMI, which now keeps identifyAsSteamDeck false instead of re-applying its default. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…11 (#1262) * fix(streamer): send the native mic as plain Opus on the bundle's PT 111 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(streamer): send the native mic as plain Opus on the bundle's PT 111 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…1263) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…t sessions start (#1265) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…controls (#1264) * fix(input): surface hidraw permission failures for wheels and flight controls Refs #1258 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(input): drain HID diagnostics through the poll batch so startup messages are not lost Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…#1266) Replace the Raspberry Pi FFmpeg fork with FFmpeg 9.0 plus the V4L2 Request hwaccel patches so mainline stateless decoders (Rockchip Hantro/rkvdec/VPU981, Allwinner, Raspberry Pi) are detected and used. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…lock dialog (#1267) * fix(auth): keep the clock-skew refusal after a provider refresh The auth.session.get reply that follows a provider refresh recomputed authState from the session alone, so the refusal the user was reading turned back into "idle" and vanished from the sign-in screen at every size. Settle it instead: a signed-in result always wins, and an existing refusal stands until the next attempt or a deliberate reset. Logout still resets explicitly. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): show the clock warning while a signed-out launch is pending The dialog gated on updaterSessionSafe, so a pending --launch-app-id launch -- the path that lands a signed-out user on sign-in -- closed the gate and the warning never appeared. Split the gate: sessionWorkIdle keeps the session-work half, the updater waits for the pending launch on top of it, and the clock warning takes the session-work half alone. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): give the clock dialog the keyboard so Escape and Return close it A modal only receives Escape once an item inside it holds the focus, and the sign-in screen behind it claims the focus while its primary button finishes loading, so the dialog takes the keyboard when it opens and takes it back when it loses it. The Basic style's button does not activate on Return, so the dialog accepts that key itself. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…de decorations (#1268) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(windows): suspend capture through the native window move loop Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(windows): run hidden HWND move-loop regression in CI Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(windows): retain native stream-item move-loop coverage Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(windows): retain row-level native move-loop CI results Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(windows): execute the native stream-item move-loop probe in CI Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(qt): use the supported focus-window injection API Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… node (#1277) * fix(input): show NO HID PATH for allowlisted devices without a hidraw node Refs #1258 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(input): wait for HID discovery after hotplug before reporting NO HID PATH Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(input): include <utility> for std::exchange in ControllerInput Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(qt): bound stream presentation and enable negotiated Cloud G-Sync pacing Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): report Cloud G-Sync eligibility without claiming scanout Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(qt): deploy native presentation test runtime on macOS Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(qt): run presentation policy suites in PR checks Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(streaming): preserve and pace sender-timed GPU frames Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * ci: allow manual CodeQL validation on test branches Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…1366) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…nly (#1281) The catalog support gate dropped in-game settings persistence for titles whose variants do not advertise IN_GAME_SETTINGS_PERSISTENCE_ENABLED (Diablo IV, appId 102348311), sending enablePersistingInGameSettings: false in every session request while the official Bifrost client sends the toggle value unconditionally. Settings then reset between sessions on affected titles even for entitled members with the toggle enabled. Honor the user toggle only, matching the official client, and update the unit test to the new semantics. Fixes #1280 Co-authored-by: Cursor <cursoragent@cursor.com>
…detected (#1301) Mesa radeonsi rejects H.264 decode contexts below 64x64 with VA_STATUS_ERROR_RESOLUTION_NOT_SUPPORTED. The vendored cros-codecs VaapiBackend and OpenNOW's capability probe both created a 16x16 placeholder context, so on AMD iGPUs (verified on Renoir, Mesa 26.2.5) the vaapi backend was reported unavailable ('VA-API cannot create the initial H.264 decoder context: resolution not supported') and 'auto' ended in streamer_codec_unavailable. The placeholder is replaced by new_sequence once the SPS arrives, so use a shared INITIAL_CONTEXT_SIZE of 64. Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
…m toggle and pacer fixes (#1300) * feat(qt): frame generation levels, capability test, target FPS and display refresh Frame generation grows from a single 2x switch into a measured feature set: - Levels off/2x/3x/4x (`frameGeneration`), a target output rate (`frameGenerationTargetFps`, 0 = follow the level; the level is derived as round(target / stream fps)) and a display refresh override (`frameGenerationDisplayHz`, 0 = the screen OpenNOW is on). The core validates all of them plus a bounded `frameGenerationProfiles` map. - StreamFramePacer schedules N-1 generated frames per source interval (`generated()` / `takeNext()`), the display gate scales with the factor and the fixed 120 FPS ceiling is replaced by a 4 ms source floor. The interpolator contract reports how many frames it can synthesise (`generatedCount()` / `generatedTexture()`); the block-matching engine stays midpoint-only, so 3x/4x need the FG-Legacy engine. - FrameGenerationBenchmark runs the engine offscreen on a private QRhi at the selected stream resolution with procedural frames and reports p50/p95 per generated frame (GPU timestamps when the backend delivers them, wall time around the blocking offscreen frame otherwise); `evaluate()` allows a level when p95 * (N-1) fits 60 % of a source interval, the display shows fps * N and the engine supports N. Results are saved per preset (engine|gpu|WxH|fps). Levels and targets stay locked in Settings until a passing profile exists; a selection the test rules out resets to Off. SettingsState owns the logic; desktop and console screens share it. - Settings rows: level segments, Run test, a GitHub link to the Framegen repository, Target FPS and Display refresh; stats badge FRAME GEN Nx; "Source rate too high" replaces the 120 FPS limit label. - FG-Legacy attribution: third_party/framegen notices and a THIRD_PARTY_NOTICES section; docs/fg-legacy.md describes the design. Tests: framepacer (N-x schedule, display scaling, floor), a new framegenerationbenchmark test (decision rules + a real measurement), nativeframegeneration/streamvideo/frameinterpolator/localization, the frame-generation QML acceptance (locked until tested, unlocked by a profile) and the Rust settings tests. QML smoke tests that open the stream route only run with a visible window on Windows. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(qt): frame generation mode selector; capability test gates the feature - One mode control instead of three parallel settings: Off / Multiplier / Target FPS / Display (`frameGenerationMode`). Only the control the mode needs is shown: multiplier chips, or the output rates the display can show at 2x..4x the stream rate. Display mode takes the largest allowed multiplier for the detected refresh rate. Profiles saved before the mode existed with a level set read as multiplier mode. - The capability test is its own row (status + Run test + Framegen on GitHub). Until it has passed for the current preset the feature is unavailable everywhere: mode controls are disabled and the stream surfaces ignore a stored level (`frameGenerationFactor()` is 0 unless the profile allows the requested factor). The stats acceptance seeds a passing profile; the QML acceptance checks the locked and unlocked states. - Display refresh override moves under Advanced (Auto shows the detected rate); the first-run Boost hint mentions the test. - Measurement falls back to wall time around the blocking offscreen frame when the backend reports no GPU timestamps (D3D11 does for offscreen frames); `gpuTimed` reflects it. New opennow-framegenerationbenchmark-tests covers the decision rules and a real measurement. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(qt): decide frame-generation levels live and name the real reason The capability test measured the GPU correctly but the verdict was frozen at test time and every empty result read "Too slow", even when the display was the limit. Profiles now store the measurement only, keyed by engine, GPU and resolution (the cost does not depend on the frame rate); which levels fit is recomputed from the selected frame rate and the current display whenever either changes. The status names the actual reason: the display cannot show fps x 2, the GPU exceeds the budget, or the engine is midpoint-only; chip hints follow the same reasons. Measurements saved with the old fps-suffixed key remain valid. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(qt): frame generation capability dialog with the full rate x level table Run test now opens a dialog that shows every option at once instead of one verdict for the current preset: the detected display rate with its override, the stream rate chips, the mode chips, and a table with one row per stream rate and one cell per level (2x, 3x, 4x). Each cell shows the output rate and a check mark or the reason it does not fit (display, GPU, engine); greyed cells explain the numbers on hover. Clicking a row picks the stream rate, clicking a cell the level or target, and Apply writes fps, mode and level together. The GPU is measured once when no profile exists. SettingsState gains frameGenerationVerdictFor(fps) and frameGenerationMatrix(); --smoke-frame-generation-dialog seeds a 2 ms / 240 Hz measurement and opens the dialog for screenshots. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(qt): frame generation in the stream menu and on a shortcut; tolerant pacer; persisted choices Ctrl+G gains a Frame generation tile while the feature is in use (on, or switched off from the menu this session); switching it off remembers mode, level and target in memory and switching it on restores them. The same toggle is bound to shortcutToggleFrameGeneration (default Ctrl+Shift+G) and reports through the stream control message. Frame generation choices now go through setSetting, so they persist across restarts (they were only applied to the local settings map before). The presenter acquires the latest decoded frame, so a burst skips one source frame: StreamFramePacer now treats a single skipped frame as continuous once the cadence is known (per-frame deltas feed the cadence estimate) and only starts the two-second overload cooldown after four collisions within a second; a single early arrival just drops the stale schedule. The stats overlay names the reason behind a Discontinuity. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * feat(qt): block-matching engine generates 3x and 4x; capability test times every level The synthesize pass takes t: the previous frame travels t of its forward flow and the current frame (1 - t) of its backward flow, blended 1 - t : t, so one pair yields N - 1 frames at t = k / N. Each generated frame has its own render target and its own uniform buffer per slot, so the passes of one pair never overwrite each other's t inside a command buffer. MaximumFactor is 4; midpointTexture() keeps returning the frame nearest t = 0.5. The capability test now times 2x, 3x and 4x in turn (one second each) and stores p95Ms, p95Ms3 and p95Ms4; evaluate() uses the measured per-level cost and falls back to p95 x (N - 1) for profiles from before. The engine maximum comes from the installed engine, not the stored profile, so an earlier measurement no longer caps the levels. On the RTX 4060 Ti the synthesis pass is nearly free next to the flow (0.23 / 0.27 / 0.23 ms per source frame at 640x360). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(qt): presenter keeps every frame of a 3x or 4x pair selectable StreamVideoTextureRenderer held three external texture slots (two history frames and the midpoint), so the second generated frame of a 3x or 4x pair failed selectTexture() and generation switched itself off with 'unavailable'. Five slots now cover the two history frames plus up to three generated frames. New tst_nativeframegeneration case drains 4x pairs through the production callback and asserts the status never drops to unavailable. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(qt): steady frame-generation pacing on bursty streams and high factors The display gate used a stricter tolerance than the capability test and judged it on a single frame's timestamp delta, so 60 FPS x 4 on a 240 Hz panel flipped between active and display-refresh with every jittery frame. The pacer now smooths the source interval with the median of the last eight per-frame timestamp deltas (arrival deltas as the fallback) and applies the test's 0.975 tolerance. A source frame that lands on an unfinished pair is a burst, not overload: the stale schedule is dropped and the newest pair wins. Overload is reserved for a stalled presenter, which the render callback reports when nothing new has been swapped for max(100 ms, 4 x interval). Every frame-generation status change is written to qt-native.log with the pacer's numbers, throttled to five lines a second, so flicker can be diagnosed from a real session. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(qt): name the paused frame-generation state for what it is The pacer's overload state means the display stopped taking frames, not GPU load; the stats overlay now says so instead of 'Overloaded'. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(qt): lower the factor instead of stopping when the display is slightly short; log GUI stalls A live '60 FPS' cloud stream ran at 61.7 FPS, so 4x needed ~247 Hz and the 240 Hz panel kept flipping generation into display-refresh. The display gate now tolerates 5 % overshoot and, when the display is still too slow, lowers the factor for the pair (the render callback ingests and schedules the pacer's effective factor); only when 2x does not fit is generation off. The live log also showed a three-frame sequence gap every second. Frames are requested from the GUI thread, so StreamVideoItem now runs a 10 ms GUI-thread probe and logs stalls of 35 ms or more to qt-native.log. A wall-clock sensitive native test step was tightened so its arrival cadence stays inside the pacer's window. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(qt): Experimental badge and engine notice for frame generation; live capture diagnostic Settings rows can carry a status pill; frame generation shows an Experimental badge and, on desktop, console and in the capability dialog, a notice that the current engine was not designed for games and shows strong artifacts on thin edges and fast motion until the FlyHigh model replaces it. Diagnostics: OPENNOW_FG_CAPTURE_DIR makes the render callback save the pair and every generated frame as PNG a few times per session, and OPENNOW_FG_DUMP runs the engine on two image files in tst_frameinterpolator (skipped otherwise). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * style(core): rustfmt frame generation settings tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(qt): preserve benchmark device ownership and profile validity Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(qt): align recovery fixture with sender cadence Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(streaming): preserve skipped-pair span and reject raw arrival gaps Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(windows): protect frame-generation minimum calls from macros Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(framegen): persist benchmark acceptance results in CI Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: zortos293 <65777760+zortos293@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(network): measure region latency with a bounded HTTPS round trip VPN tunnels make TCP connect RTT lie. Time an HTTPS HEAD on a keep-alive connection instead, with redirects disabled, DNS pinned to pre-resolved addresses so Client drop cannot outlive the timeout, and fresh-TLS outliers retried then dropped. Co-authored-by: Cursor <cursoragent@cursor.com> * style(core): format HTTPS region measurements Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(network): bound region measurement DNS resolution Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(network): normalize accepted socket blocking mode Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(network): exclude fresh connections from region RTT samples Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(network): route region pings through configured or system proxies Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: zortos293 <65777760+zortos293@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…1367) * fix(session): announce real display DPI instead of hardcoded Mac 144 Compute physical DPI as (logical size × devicePixelRatio) / panel mm and send it in clientRequestMonitorSettings so remote Windows UI scaling matches Retina and other HiDPI displays. Also expose GFN's Mac 3.5K (3456x2160) preset in the 16:10 catalog. Co-authored-by: Cursor <cursoragent@cursor.com> * style(core): format monitor DPI fallback Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: zortos293 <65777760+zortos293@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…#1405) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(webrtc): align native signaling with the v3 wire contract Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(test): normalize accepted signaling socket mode Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* feat(settings): add seven shared resolution presets Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(settings): synchronize native resolution fallback catalog Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promote Qt/native 1.0.4 from dev to main. Dev has advanced beyond the audited nightly source b3fe710. Later changes cover Linux HID identity, Windows modifier-side/AltGr routing, Wayland Vulkan swapchain teardown, native mouse latency diagnostics, consuming launch intent after successful session creation, blocked flight-device statistics, and the sender-timing/bounded-presentation integration from #1298. The current streamer boundary is FFI ABI 18; the historical ABI-16 nightly and earlier Qt test counts below do not validate this new native integration.
The PR's live head must pass its own current checks and completed review before promotion. Earlier b3fe710 verification does not establish readiness of later commits. Main promotion also remains held for explicit merge confirmation.
The previous release audit fixed allocation/cancellation, account/settings scope, graceful shutdown/recording finalization, stream/ad identity, native startup/input bounds, decoder/color policy, authenticated packaging/Decky installs, modal keyboard ownership and Vulkan Wayland framing. Acceptance-only #1357 preserves SDL virtual controller edges without changing normal input or weakening assertions. Verified head88dba73e and devb3fe7102 have identical tree0e1e99dccda9571900880512db6b41a35e57d469. That source passed575 Qt tests, six focused targets,48 overlap repeats, seven PR CI checks and clean Capy review. Older raw machine-local logs were lost in workspace replacement; durable conversation/CI/review evidence remains. The original ARM64 stall was not instrumented and is not claimed as uniquely explained.
Published nightly v1.0.4-nightly.1515.1 remains verified at exact b3fe710, not the later dev tip. Run40598756074 passed native ARM64 packaging/acceptance, signing and publication. Independent public downloads passed the complete30-file inventory,14 Ed25519 manifests,29 checksums and exact release-tag/source verification. Public notes retain platform-signing warnings and hardware/recording limitations. Earlier1511 attempts were not published: GitHub HTTP500, then the repaired ARM64 acceptance-driver failure.
The owner waived unavailable physical GPU/Wayland validation and chose community issue feedback. ZOR-37 is canceled as waived, not passed. Historical cloud baseline/fixed failures do not establish a driver root cause or physical-GPU validation. Other hardware/gameplay coverage is not claimed. Recording initial A/V offsets and their real-world synchronization impact remain unmeasured. For #1298, live sender-paced activation, accepted server Cloud G-Sync and the reported 240-FPS smoothness gap remain unverified. It does not invoke the private NVIDIA VRR setter or establish physical scanout/Reflex behavior; retain the feature's documented hardware limitations in future release notes.
Specific main-merge confirmation is still requested in ZOR-38; no main promotion, production candidate or stable publication has occurred in this release task. Production must build an exact-main immutable candidate, verify inventory/checksums/update signatures and required platform signing, then promote those same bytes without rebuilding. The hardware waiver bypasses none of these checks. Retain the validation limits and existing platform-signing warnings in public notes.