* test: remove tests that do not pin user-visible behavior
Apply the AGENTS.md test rules to the existing Rust and Flutter suites.
A test is kept when it is the only coverage of user- or API-visible
behavior, the regression test of a reported or fixed bug, a security
boundary (one test per rejection class), or a check against an external
reference. Tests that assert private state or the order of internal
steps, pin review-round corner cases, or repeat what a kept test already
covers are removed.
Rust: 679 -> 298 tests. Dart: 118 -> 45 tests.
Test-only helpers left without callers go with them
(applyLastOnlineStateForTesting, terminalKeyboardRowWidth,
debugBufferedInputCount, tokio's test-util dev feature), and cfg gates
and visibility that existed only for the deleted tests are narrowed.
Runtime behavior is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R
* test: keep tests that pin security and user-visible behavior
Restore six tests that review showed are the only coverage of their
behavior:
- update metadata rejects an artifact file name with path components;
- ICE candidates from another session are dropped;
- KCP delivers every multi-segment frame queued before a close;
- set_files rejects an empty name in a multi-file transfer;
- a lopsided elongated cursor keeps its centre hotspot (#16242);
- toggling Show hidden during a pending same-path read issues a new read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R
* test: keep offline update and key-exchange downgrade rejections
Restore three tests that are the only coverage of their rejection class:
- offline update metadata rejects a signed artifact with an http or
credential-bearing URL;
- offline verification rejects a renamed local file and a version that
does not match the release;
- a key-exchange pick lowered in transit leaves both sides unable to
decrypt, so a v0 fallback after a failed v1 decrypt cannot pass
unnoticed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R
* test: keep the decomposed-grapheme and reconnect-cancel tests
Each catches a plausible wrong change that passes every kept test:
mapping the first scalar under a locked Ctrl turns é into \x05,
and a reconnect that does not fail pending reads leaves them hanging
until the 30 s timeout.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WS1HTjp6UQuG43KEcQPq1R
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* scrap: tone-map HDR desktops to SDR in the Windows capturer
With HDR enabled, Windows composes the desktop as linear scRGB in
R16G16B16A16_FLOAT and places SDR white at the user's "SDR content
brightness" (DISPLAYCONFIG_SDR_WHITE_LEVEL / 1000, e.g. 2.5 for 200
nits) rather than at 1.0. The legacy IDXGIOutput1::DuplicateOutput we
used always converts that surface to BGRA8, and it does so by clipping,
so everything above ~40% linear brightness lands on white. That is the
washed-out / overexposed / high-contrast picture reported for HDR hosts
in #5368, #9951, #12707 and discussion #7652.
Ask for the desktop with IDXGIOutput5::DuplicateOutput1 and the format
list [R16G16B16A16_FLOAT, B8G8R8A8_UNORM]. An SDR desktop still yields
BGRA8 and takes the unchanged path. A float frame is converted on the
GPU by a small pixel shader: divide by the SDR white level, clamp, apply
the sRGB transfer. SDR content round-trips exactly, HDR highlights clip
at white, the same result the local user sees. The white level is read
per output through DisplayConfigGetDeviceInfo and refreshed once a
second, so dragging the Windows slider tracks remotely. The converted
BGRA8 texture feeds both the staging-copy (CPU) path and the vram
(hwcodec texture) path; rotation is unchanged. Toggling HDR mid-session
invalidates the duplication as before, and the recreated capturer
re-detects the format.
The shaders are compiled at runtime from a dynamically loaded
d3dcompiler_47.dll so no new import is added to the binary. If the DLL,
the compile or any D3D object creation fails, a process-wide flag makes
later capturers fall back to the legacy DuplicateOutput, i.e. today's
behaviour.
This deliberately stops at SDR on the controlled side, with no option
and no protocol change, which is how OBS, Sunshine (client without HDR)
and macOS screen capture handle it. Real HDR pass-through would need
10-bit capture, Main10/AV1-10 encoders in the hwcodec fork, colour
metadata on the wire and, decisively, an HDR output path on the
controller: Flutter's desktop external textures are BGRA8888/RGBA8888
only on every platform, so nothing would be visible. When that changes
it should follow the Sunshine/Moonlight pattern: an hdr capability bit
advertised by the controller behind an explicit user toggle, negotiated
like i444.
Type-checked against x86_64-pc-windows-msvc (with and without the vram
feature); not yet run on an HDR machine, which needs Windows 10 1703+
with HDR on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
(cherry picked from commit ca3c2f8ac7d1549a40855411b0539a69c82d7223)
* scrap: stay on DXGI when the HDR conversion fails, load the compiler once
Review follow-up:
- A tone-map failure surfaced as a capture error, and the capture loop
answers any DXGI error by switching the capturer to GDI for the rest
of its life. The capturer now drops the tone-map, re-creates the
duplication with the legacy DuplicateOutput (DXGI's clipped BGRA8,
the pre-HDR behaviour) and returns WouldBlock, so the session stays
on DXGI and simply fetches the next frame.
- Only failures that cannot succeed anywhere in the process (no
d3dcompiler_47.dll, shaders that do not compile) set the global
UNAVAILABLE flag; D3D object creation failures stay with the capturer
that hit them, so another adapter or a recreated capturer tries again.
- D3DCompile is resolved once through a OnceLock instead of a
LoadLibrary per capturer that was never freed.
- The module doc now states what this pass is: normalization of an HDR
desktop to SDR, with everything above SDR white clipping, not a tone
map, and why a roll-off is deliberately not applied. The earlier
claim that clipping matches what the local user sees was wrong for
HDR content on an HDR display.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
* scrap: only apply the SDR white level to outputs DXGI reports as HDR
Review follow-up:
- An FP16 desktop is not necessarily HDR. Since Windows 11 22H2 an
Advanced Color (WCG) SDR display is composed in FP16 scRGB too, and
there 1.0 is the display's reference white, not 80 nits, so the
SDRWhiteLevel scaling does not apply (Microsoft: "Reference white
applies only to HDR displays"). Query IDXGIOutput6::GetDesc1 and treat
the output as HDR only for DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
the documented Win32 check; a non-HDR FP16 frame now gets just the
scRGB -> sRGB transfer. Without IDXGIOutput6 (before Windows 10 1803)
FP16 can only mean HDR.
- The SDR white level is now Option: a failed query or a reported 0 is
"unknown" on both the initial and the refresh path, an HDR output with
an unknown level logs a warning and keeps the 80-nit assumption until
the periodic re-query succeeds. DISPLAYCONFIG_DEVICE_INFO_GET_SDR_WHITE_LEVEL
needs Windows 10 1709, not 1703 as an earlier message said.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
* scrap: re-read the Advanced Color state while capturing, gate FP16 on IDXGIOutput6
Review follow-up:
- The HDR/WCG decision was taken once when the conversion was created.
HDR can be switched on or off, and a WCG desktop can turn into an HDR
one, without the duplication being invalidated, so the choice between
"divide by the SDR white level" and "no scaling" could go stale. The
once-a-second refresh now re-reads it: it keeps an IDXGIFactory1,
and when IsCurrent reports FALSE it creates a fresh factory and
re-finds the output by GDI name, since a stale factory's outputs keep
stale descriptions (this is the procedure the GetDesc1 docs require).
On a change it switches modes, re-reads the white level and updates
the shader constant; an unreadable level keeps the last known one.
- Float frames are only requested when IDXGIOutput6 exists, as
Microsoft's duplication sample does. That interface is also what tells
HDR from WCG, so there is no longer a state where FP16 is requested
without being able to interpret it. IDXGIOutput6 dates from Windows 10
1703, not 1803 as the previous commit said; 1803 added IDXGIFactory6.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
* scrap: replace the DXGI factory and output together when re-reading HDR state
Review follow-up: when the fresh factory did not find the output by name,
the refresh kept the new, current factory next to the old output, and
because IsCurrent then reported TRUE it never enumerated again, so the
stale output could stay forever. enumerate_output6 now returns both or
neither, and the refresh only replaces the pair together; a null factory
forces another enumeration on the next refresh while the old output is
still read in the meantime. The constructor gets the same guarantee for
free, since a failed lookup leaves the factory null there too.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
* scrap: keep enumerate_output6's both-or-neither promise when IDXGIOutput6 is missing
Review nit: a matched output whose IDXGIOutput6 query fails returned the
fresh factory next to a null output, which let the constructor pair a
current factory with the capturer's fallback output. Return neither in
that case so the next refresh enumerates again.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F
* scrap: log DuplicateOutput1 failures before fallback
Signed-off-by: 21pages <sunboeasy@gmail.com>
* scrap: avoid null duplication after HDR fallback failure
Signed-off-by: 21pages <sunboeasy@gmail.com>
* scrap: load the D3D compiler securely from System32
Signed-off-by: 21pages <sunboeasy@gmail.com>
* scrap: release the frame through the state machine when abandoning the tone-map
master's FrameState machine is left at Acquired by the raw ReleaseFrame(),
so the next release_frame() hits DXGI_ERROR_INVALID_CALL on the fresh
duplication and the capture loop falls back to GDI -- the outcome the
fallback exists to avoid.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Signed-off-by: 21pages <sunboeasy@gmail.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: 21pages <sunboeasy@gmail.com>
* Updated build.rs to tell RustC that dxgi, quartz and x11 are expected configurations.
Added lifetime annotations to various methods in common/aom.rs and common/vpxcodec.rs.
Updated common/vpx.rs to allow unused_imports in the generated bindings.
Updated dxgi/mag.rs to allow non_snake_case identifiers like "dwFilterMode".
* Added lifetime annotations to methods in common/hwcodec.rs and common/vram.rs.
* Switched syntax for the rustc-check-cfg directive emitted by build.rs in the scrap crate to use syntax compatible with Rust toolchain version 1.75. The double-colon syntax requires 1.77 or newer, but the older single-colon syntax works fine on newer versions for this directive.
* Update libs/scrap/build.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Revert apparently-erroneous AI suggestion. It's usually pretty good, but not always right it seems. :-)
This reverts commit bf862b13f6.
* Removed redundant configuration directives from libs/scrap/build.rs.
---------
Co-authored-by: RustDesk <71636191+rustdesk@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Rotate ID3D11Texture2D after duplication with d3d11 video processor.
* If display is not rotated, nothing will be created; If the rotation
fails, it will use the old fallback logic
TODO:
* If changing from Landscape to Landscape(flipped) during capture, the resolution is
not changed, video service fallback to gdi directly.
Signed-off-by: 21pages <sunboeasy@gmail.com>
* update hwcodec, gpucodec repo is merged to hwcodec
Signed-off-by: 21pages <pages21@163.com>
* rename gpucodec.rs to vram.rs
Signed-off-by: 21pages <pages21@163.com>
* rename all gpucodec to vram, because vram is a feature of hwcodec
Signed-off-by: 21pages <pages21@163.com>
* use one check process and one config file
* set check encode image size to 720p
Signed-off-by: 21pages <pages21@163.com>
---------
Signed-off-by: 21pages <pages21@163.com>