cmm: enable using the display primaries in SDR mode
This commit is contained in:
parent
2b7b3b5310
commit
67760e270e
19 changed files with 259 additions and 21 deletions
|
|
@ -2,11 +2,14 @@ use {
|
|||
crate::{
|
||||
backend::{BackendColorSpace, BackendEotfs},
|
||||
cli::GlobalArgs,
|
||||
cmm::cmm_primaries::Primaries,
|
||||
format::{Format, XRGB8888},
|
||||
ifs::wl_output::BlendSpace,
|
||||
scale::Scale,
|
||||
tools::tool_client::{Handle, ToolClient, with_tool_client},
|
||||
utils::{errorfmt::ErrorFmt, transform_ext::TransformExt},
|
||||
utils::{
|
||||
debug_fn::debug_fn, errorfmt::ErrorFmt, ordered_float::F64, transform_ext::TransformExt,
|
||||
},
|
||||
wire::{JayRandrId, jay_compositor, jay_randr},
|
||||
},
|
||||
clap::{
|
||||
|
|
@ -167,6 +170,30 @@ pub enum OutputCommand {
|
|||
Brightness(BrightnessArgs),
|
||||
/// Change the blend space.
|
||||
BlendSpace(BlendSpaceArgs),
|
||||
/// Change whether the display primaries are used.
|
||||
UseNativeGamut(UseNativeGamutArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
pub struct UseNativeGamutArgs {
|
||||
/// Configures whether the display primaries are used.
|
||||
///
|
||||
/// By default, Jay pretends that the display uses sRGB primaries. This is also how
|
||||
/// most other systems behave. In reality, most displays use a much larger gamut. For
|
||||
/// example, they advertise that they support 95% of the DCI-P3 gamut. If the display
|
||||
/// is interpreting colors in their native gamut, then colors will appear more
|
||||
/// saturated than their specification.
|
||||
///
|
||||
/// If this is set to `true`, Jay assumes that the display uses the primaries
|
||||
/// advertised in its EDID. This might produce more accurate colors while also
|
||||
/// allowing color-managed applications to use the full gamut of the display.
|
||||
///
|
||||
/// This setting has no effect when the display is explicitly operating in a wide
|
||||
/// color space.
|
||||
///
|
||||
/// The default is `false`.
|
||||
#[arg(action = clap::ArgAction::Set)]
|
||||
pub use_native_gamut: bool,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
|
|
@ -499,6 +526,8 @@ struct Output {
|
|||
pub brightness_range: Option<(f64, f64)>,
|
||||
pub brightness: Option<f64>,
|
||||
pub blend_space: Option<String>,
|
||||
pub native_gamut: Option<Primaries>,
|
||||
pub use_native_gamut: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -789,6 +818,19 @@ impl Randr {
|
|||
blend_space: &a.blend_space,
|
||||
});
|
||||
}
|
||||
OutputCommand::UseNativeGamut(a) => {
|
||||
self.handle_error(randr, move |msg| {
|
||||
eprintln!(
|
||||
"Could not change whether the compositor uses the native gamut: {}",
|
||||
msg,
|
||||
);
|
||||
});
|
||||
tc.send(jay_randr::SetUseNativeGamut {
|
||||
self_id: randr,
|
||||
output: &args.output,
|
||||
use_native_gamut: a.use_native_gamut as _,
|
||||
});
|
||||
}
|
||||
}
|
||||
tc.round_trip().await;
|
||||
}
|
||||
|
|
@ -1024,6 +1066,25 @@ impl Randr {
|
|||
if let Some(bs) = &o.blend_space {
|
||||
println!(" blend space: {bs}");
|
||||
}
|
||||
if let Some(p) = &o.native_gamut {
|
||||
println!(
|
||||
" native gamut:{}",
|
||||
debug_fn(|f| {
|
||||
if o.use_native_gamut {
|
||||
f.write_str(" (used for default color space)")?;
|
||||
}
|
||||
Ok(())
|
||||
}),
|
||||
);
|
||||
println!(
|
||||
" red: {:.6} {:.6} green: {:.6} {:.6}",
|
||||
p.r.0.0, p.r.1.0, p.g.0.0, p.g.1.0
|
||||
);
|
||||
println!(
|
||||
" blue: {:.6} {:.6} white: {:.6} {:.6}",
|
||||
p.b.0.0, p.b.1.0, p.wp.0.0, p.wp.1.0
|
||||
);
|
||||
}
|
||||
if o.modes.is_not_empty() && modes {
|
||||
println!(" modes:");
|
||||
for mode in &o.modes {
|
||||
|
|
@ -1204,6 +1265,24 @@ impl Randr {
|
|||
let output = c.output.as_mut().unwrap();
|
||||
output.blend_space = Some(msg.blend_space.to_string());
|
||||
});
|
||||
jay_randr::NativeGamut::handle(tc, randr, data.clone(), |data, msg| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
let output = c.output.as_mut().unwrap();
|
||||
let primaries = Primaries {
|
||||
r: (F64(msg.r_x), F64(msg.r_y)),
|
||||
g: (F64(msg.g_x), F64(msg.g_y)),
|
||||
b: (F64(msg.b_x), F64(msg.b_y)),
|
||||
wp: (F64(msg.w_x), F64(msg.w_y)),
|
||||
};
|
||||
output.native_gamut = Some(primaries);
|
||||
});
|
||||
jay_randr::UseNativeGamut::handle(tc, randr, data.clone(), |data, _| {
|
||||
let mut data = data.borrow_mut();
|
||||
let c = data.connectors.last_mut().unwrap();
|
||||
let output = c.output.as_mut().unwrap();
|
||||
output.use_native_gamut = true;
|
||||
});
|
||||
tc.round_trip().await;
|
||||
data.borrow_mut().clone()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ use {
|
|||
HeadManagers, HeadState, jay_head_manager_session_v1::handle_jay_head_manager_done,
|
||||
},
|
||||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||
wl_output::{BlendSpace, OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
wl_seat::handle_position_hint_requests,
|
||||
wl_surface::{
|
||||
NoneSurfaceExt, xdg_surface::handle_xdg_surface_configure_events,
|
||||
|
|
@ -629,16 +629,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
model: "jay-dummy-output".to_string(),
|
||||
serial_number: "".to_string(),
|
||||
});
|
||||
let persistent_state = Rc::new(PersistentOutputState {
|
||||
transform: Default::default(),
|
||||
scale: Default::default(),
|
||||
pos: Default::default(),
|
||||
vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
vrr_cursor_hz: Default::default(),
|
||||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
brightness: Cell::new(None),
|
||||
blend_space: Cell::new(BlendSpace::Srgb),
|
||||
});
|
||||
let persistent_state = Rc::new(PersistentOutputState::default());
|
||||
let mode = backend::Mode {
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
|
|
|||
|
|
@ -1353,6 +1353,16 @@ impl ConfigProxyHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_connector_set_use_native_gamut(
|
||||
&self,
|
||||
connector: Connector,
|
||||
use_native_gamut: bool,
|
||||
) -> Result<(), CphError> {
|
||||
let connector = self.get_output_node(connector)?;
|
||||
connector.set_use_native_gamut(use_native_gamut);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_set_float_above_fullscreen(&self, above: bool) {
|
||||
self.state.float_above_fullscreen.set(above);
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
|
|
@ -3316,6 +3326,12 @@ impl ConfigProxyHandler {
|
|||
ClientMessage::SeatEnableUnicodeInput { seat } => self
|
||||
.handle_seat_enable_unicode_input(seat)
|
||||
.wrn("seat_enable_unicode_input")?,
|
||||
ClientMessage::ConnectorSetUseNativeGamut {
|
||||
connector,
|
||||
use_native_gamut,
|
||||
} => self
|
||||
.handle_connector_set_use_native_gamut(connector, use_native_gamut)
|
||||
.wrn("connector_set_use_native_gamut")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
22
|
||||
23
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ const FLIP_MARGIN_SINCE: Version = Version(10);
|
|||
const COLORIMETRY_SINCE: Version = Version(15);
|
||||
const BRIGHTNESS_SINCE: Version = Version(16);
|
||||
const BLEND_SPACE_SINCE: Version = Version(21);
|
||||
const NATIVE_GAMUT_SINCE: Version = Version(23);
|
||||
|
||||
impl JayRandr {
|
||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||
|
|
@ -215,6 +216,23 @@ impl JayRandr {
|
|||
blend_space: node.global.persistent.blend_space.get().name(),
|
||||
});
|
||||
}
|
||||
if self.version >= NATIVE_GAMUT_SINCE {
|
||||
let p = &node.global.primaries;
|
||||
self.client.event(NativeGamut {
|
||||
self_id: self.id,
|
||||
r_x: p.r.0.0,
|
||||
r_y: p.r.1.0,
|
||||
g_x: p.g.0.0,
|
||||
g_y: p.g.1.0,
|
||||
b_x: p.b.0.0,
|
||||
b_y: p.b.1.0,
|
||||
w_x: p.wp.0.0,
|
||||
w_y: p.wp.1.0,
|
||||
});
|
||||
if node.global.persistent.use_native_gamut.get() {
|
||||
self.client.event(UseNativeGamut { self_id: self.id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_error(&self, msg: &str) {
|
||||
|
|
@ -551,6 +569,18 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
c.set_blend_space(space);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_use_native_gamut(
|
||||
&self,
|
||||
req: SetUseNativeGamut<'_>,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
let Some(c) = self.get_output_node(req.output) else {
|
||||
return Ok(());
|
||||
};
|
||||
c.set_use_native_gamut(req.use_native_gamut != 0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
|
|
|
|||
|
|
@ -140,6 +140,7 @@ pub struct PersistentOutputState {
|
|||
pub tearing_mode: Cell<&'static TearingMode>,
|
||||
pub brightness: Cell<Option<f64>>,
|
||||
pub blend_space: Cell<BlendSpace>,
|
||||
pub use_native_gamut: Cell<bool>,
|
||||
}
|
||||
|
||||
impl Default for PersistentOutputState {
|
||||
|
|
@ -153,6 +154,7 @@ impl Default for PersistentOutputState {
|
|||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
brightness: Default::default(),
|
||||
blend_space: Cell::new(BlendSpace::Srgb),
|
||||
use_native_gamut: Cell::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -384,18 +386,25 @@ impl WlOutputGlobal {
|
|||
let target_primaries;
|
||||
match self.bcs.get() {
|
||||
BackendColorSpace::Default => {
|
||||
named_primaries = NamedPrimaries::Srgb;
|
||||
primaries = named_primaries.primaries();
|
||||
if self.persistent.use_native_gamut.get()
|
||||
&& self.primaries != NamedPrimaries::Srgb.primaries()
|
||||
{
|
||||
named_primaries = None;
|
||||
primaries = self.primaries;
|
||||
} else {
|
||||
named_primaries = Some(NamedPrimaries::Srgb);
|
||||
primaries = NamedPrimaries::Srgb.primaries();
|
||||
}
|
||||
target_primaries = primaries;
|
||||
}
|
||||
BackendColorSpace::Bt2020 => {
|
||||
named_primaries = NamedPrimaries::Bt2020;
|
||||
primaries = named_primaries.primaries();
|
||||
named_primaries = Some(NamedPrimaries::Bt2020);
|
||||
primaries = NamedPrimaries::Bt2020.primaries();
|
||||
target_primaries = self.primaries;
|
||||
}
|
||||
}
|
||||
let cd = self.state.color_manager.get_description(
|
||||
Some(named_primaries),
|
||||
named_primaries,
|
||||
primaries,
|
||||
luminance,
|
||||
tf,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ impl ConnectorHandler {
|
|||
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
||||
brightness: Cell::new(None),
|
||||
blend_space: Cell::new(BlendSpace::Srgb),
|
||||
use_native_gamut: Cell::new(false),
|
||||
});
|
||||
self.state
|
||||
.persistent_output_states
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(22),
|
||||
version: s.jay_compositor.1.min(23),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
|
|
@ -1005,6 +1005,17 @@ impl OutputNode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_use_native_gamut(&self, use_native_gamut: bool) {
|
||||
let old = self
|
||||
.global
|
||||
.persistent
|
||||
.use_native_gamut
|
||||
.replace(use_native_gamut);
|
||||
if old != use_native_gamut {
|
||||
self.update_color_description();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_blend_space(&self, blend_space: BlendSpace) {
|
||||
let old = self.global.persistent.blend_space.replace(blend_space);
|
||||
if old != blend_space {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue