mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-18 18:31:02 +03:00
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)
Derived from https://github.com/quadrupleslap/scrap
scrap
Scrap records your screen! At least it does if you're on Windows, macOS, or Linux.
Usage
[dependencies]
scrap = "0.5"
Its API is as simple as it gets!
struct Display; /// A screen.
struct Frame; /// An array of the pixels that were on-screen.
struct Capturer; /// A recording instance.
impl Capturer {
/// Begin recording.
pub fn new(display: Display) -> io::Result<Capturer>;
/// Try to get a frame.
/// Returns WouldBlock if it's not ready yet.
pub fn frame<'a>(&'a mut self) -> io::Result<Frame<'a>>;
pub fn width(&self) -> usize;
pub fn height(&self) -> usize;
}
impl Display {
/// The primary screen.
pub fn primary() -> io::Result<Display>;
/// All the screens.
pub fn all() -> io::Result<Vec<Display>>;
pub fn width(&self) -> usize;
pub fn height(&self) -> usize;
}
impl<'a> ops::Deref for Frame<'a> {
/// A frame is just an array of bytes.
type Target = [u8];
}
The Frame Format
- The frame format is guaranteed to be packed BGRA.
- The width and height are guaranteed to remain constant.
- The stride might be greater than the width, and it may also vary between frames.
System Requirements
| OS | Minimum Requirements |
|---|---|
| macOS | macOS 10.8 |
| Linux | XCB + SHM + RandR |
| Windows | DirectX 11.1 |