From 2f3a6789dea8d668864302be79a7567e2baf584e Mon Sep 17 00:00:00 2001 From: rustdesk Date: Wed, 2 Sep 2026 16:00:47 +0800 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01RLb1dNdhFqUQExJPANjo1F --- libs/scrap/src/dxgi/hdr.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/scrap/src/dxgi/hdr.rs b/libs/scrap/src/dxgi/hdr.rs index cdef55e0c..5aa9b44e8 100644 --- a/libs/scrap/src/dxgi/hdr.rs +++ b/libs/scrap/src/dxgi/hdr.rs @@ -158,6 +158,8 @@ impl HdrToSdr { )?; let ps = ComPtr(ps); + // Not found leaves the factory null, so the first refresh enumerates + // again instead of trusting the capturer's possibly stale output. let (factory, mut output6) = enumerate_output6(device_name); if output6.is_null() { output6 = query_output6(output as *mut IUnknown); @@ -332,9 +334,12 @@ impl HdrToSdr { self.queried_at = Instant::now(); if self.factory.is_null() || (*self.factory.0).IsCurrent() == FALSE { let (factory, output6) = enumerate_output6(&self.device_name); - self.factory = factory; if !output6.is_null() { + self.factory = factory; self.output6 = output6; + } else { + // Keep reading the old output, but enumerate again next time. + self.factory = ComPtr(ptr::null_mut()); } } let is_hdr = output_is_hdr(self.output6.0).unwrap_or(self.is_hdr); @@ -410,6 +415,8 @@ unsafe fn query_output6(object: *mut IUnknown) -> ComPtr { // A fresh factory sees the current display configuration; the output is found // by its GDI name because the outputs of a stale factory keep stale descriptions. +// Returns both or neither: a non-null factory guarantees the output came from +// its topology, so a caller that sees a null factory knows to enumerate again. unsafe fn enumerate_output6( device_name: &[WCHAR; 32], ) -> (ComPtr, ComPtr) { @@ -441,7 +448,7 @@ unsafe fn enumerate_output6( } } } - (factory, ComPtr(ptr::null_mut())) + (ComPtr(ptr::null_mut()), ComPtr(ptr::null_mut())) } fn other(msg: impl Into) -> io::Error {