From 0b610c8307bc4c5045b31560f1124c0e40bb7aaa Mon Sep 17 00:00:00 2001 From: insideTheFlux Date: Sun, 25 Oct 2026 22:05:23 -0700 Subject: [PATCH] LR-PREF-DISPLAY: an unreadable Windows advanced-colour state leaves previews sRGB When the display configuration, the window's display path or its advanced-colour state can't be read, `monitor_color` answered with the monitor's profile as if Windows weren't colour-managing the desktop. If it was (HDR, Auto Color Management), previews were converted twice. It now answers `Unknown`, as the crate's other failures do, so previews stay sRGB (Windows 10 before 1709 and drivers that don't report the state included). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GfjNkJiMUagz3DN3zoV2rV --- crates/wincolor/src/lib.rs | 31 ++++++++++++++++++++++++++----- docs/display-profiles.md | 3 ++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/crates/wincolor/src/lib.rs b/crates/wincolor/src/lib.rs index 1e7cec935a..8a1112ad0d 100644 --- a/crates/wincolor/src/lib.rs +++ b/crates/wincolor/src/lib.rs @@ -61,6 +61,19 @@ fn managed(flags: u32) -> bool { flags & 0b110 != 0 } +/// The answer Windows' own colour management settles, `None` when the monitor's profile decides: +/// [`MonitorColor::ManagedByWindows`] while it colour-manages the desktop, and +/// [`MonitorColor::Unknown`] when its state can't be read (`managed` is `None`), since applying the +/// monitor's profile on top of Windows' conversion would convert twice. +#[cfg(any(windows, test))] +fn settled_by_windows(managed: Option) -> Option { + match managed { + Some(true) => Some(MonitorColor::ManagedByWindows), + Some(false) => None, + None => Some(MonitorColor::Unknown), + } +} + /// The text of a NUL-terminated UTF-16 buffer (all of it without a NUL). #[cfg(any(windows, test))] fn until_nul(units: &[u16]) -> &[u16] { @@ -70,7 +83,7 @@ fn until_nul(units: &[u16]) -> &[u16] { #[cfg(windows)] #[allow(unsafe_code)] mod win32 { - use super::{MonitorColor, managed, until_nul}; + use super::{MonitorColor, managed, settled_by_windows, until_nul}; use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; use std::path::PathBuf; @@ -100,11 +113,11 @@ mod win32 { pub fn monitor_color(hwnd: isize) -> MonitorColor { let Some(device) = monitor_device(hwnd) else { return MonitorColor::Unknown }; - if managed_by_windows(&device) == Some(true) { - return MonitorColor::ManagedByWindows; + // (an advanced-colour state that can't be read, as on Windows before 10 1709 or with a + // driver that doesn't say, is unknown: previews then stay sRGB, as before profiles were read) + if let Some(answer) = settled_by_windows(managed_by_windows(&device)) { + return answer; } - // (an unknown advanced-colour state, as on Windows before 10 1709 or with a driver that - // doesn't say, counts as not managed) match monitor_id(&device) { Some(id) => wcs_profile(&id), None => icm_profile(&device).map_or(MonitorColor::Unknown, MonitorColor::Profile), @@ -323,6 +336,14 @@ mod tests { assert!(!managed(0b1000)); } + #[test] + fn an_unreadable_advanced_colour_state_is_unknown_rather_than_a_profile() { + assert_eq!(settled_by_windows(Some(true)), Some(MonitorColor::ManagedByWindows)); + assert_eq!(settled_by_windows(Some(false)), None, "an SDR desktop: the monitor's profile decides"); + // Windows may be converting already: a profile on top would convert twice + assert_eq!(settled_by_windows(None), Some(MonitorColor::Unknown)); + } + #[test] fn utf16_text_stops_at_its_nul() { assert_eq!(until_nul(&[65, 66, 0, 67]), &[65, 66]); diff --git a/docs/display-profiles.md b/docs/display-profiles.md index 25735dee37..1a63ce7de5 100644 --- a/docs/display-profiles.md +++ b/docs/display-profiles.md @@ -26,7 +26,8 @@ a restart. (GDI's `GetICMProfileW` keeps answering the profile a process first s monitor's device can't be found.) With no profile assigned, previews are sRGB. **While Windows colour-manages the desktop itself** (HDR on, or Auto Color Management on Windows 11: its advanced-colour state, from `DisplayConfigGetDeviceInfo`), it treats an ordinary app's output as sRGB and converts it for the monitor, so previews -stay sRGB rather than be converted twice. The log says which applies (`screen profile: …`) whenever it changes. +stay sRGB rather than be converted twice. So do they when that state can't be read (Windows 10 before 1709, a driver +that doesn't report it). The log says which applies (`screen profile: …`) whenever it changes. ## Using it