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
|
|
@ -1124,6 +1124,13 @@ impl ConfigClient {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn connector_set_use_native_gamut(&self, connector: Connector, use_native_gamut: bool) {
|
||||
self.send(&ClientMessage::ConnectorSetUseNativeGamut {
|
||||
connector,
|
||||
use_native_gamut,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn connector_get_scale(&self, connector: Connector) -> f64 {
|
||||
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
|
||||
get_response!(res, 1.0, ConnectorGetScale { scale });
|
||||
|
|
|
|||
|
|
@ -816,6 +816,10 @@ pub enum ClientMessage<'a> {
|
|||
position: BarPosition,
|
||||
},
|
||||
GetBarPosition,
|
||||
ConnectorSetUseNativeGamut {
|
||||
connector: Connector,
|
||||
use_native_gamut: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
|
|
@ -338,6 +338,26 @@ impl Connector {
|
|||
pub fn connector_in_direction(self, direction: Direction) -> Connector {
|
||||
get!(Connector(0)).get_connector_in_direction(self, direction)
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
pub fn set_use_native_gamut(self, use_native_gamut: bool) {
|
||||
get!().connector_set_use_native_gamut(self, use_native_gamut);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all available DRM devices.
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ pub struct Output {
|
|||
pub eotf: Option<Eotf>,
|
||||
pub brightness: Option<Option<f64>>,
|
||||
pub blend_space: Option<BlendSpace>,
|
||||
pub use_native_gamut: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use {
|
|||
config::{
|
||||
Output,
|
||||
context::Context,
|
||||
extractor::{Extractor, ExtractorError, fltorint, opt, recover, s32, str, val},
|
||||
extractor::{Extractor, ExtractorError, bol, fltorint, opt, recover, s32, str, val},
|
||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||
parsers::{
|
||||
format::FormatParser,
|
||||
|
|
@ -51,7 +51,7 @@ impl Parser for OutputParser<'_> {
|
|||
let mut ext = Extractor::new(self.cx, span, table);
|
||||
let (
|
||||
(name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val),
|
||||
(color_space, eotf, brightness_val, blend_space),
|
||||
(color_space, eotf, brightness_val, blend_space, use_native_gamut),
|
||||
) = ext.extract((
|
||||
(
|
||||
opt(str("name")),
|
||||
|
|
@ -70,6 +70,7 @@ impl Parser for OutputParser<'_> {
|
|||
recover(opt(str("transfer-function"))),
|
||||
opt(val("brightness")),
|
||||
recover(opt(str("blend-space"))),
|
||||
recover(opt(bol("use-native-gamut"))),
|
||||
),
|
||||
))?;
|
||||
let transform = match transform {
|
||||
|
|
@ -208,6 +209,7 @@ impl Parser for OutputParser<'_> {
|
|||
eotf,
|
||||
brightness,
|
||||
blend_space,
|
||||
use_native_gamut: use_native_gamut.despan(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -862,6 +862,9 @@ impl Output {
|
|||
if let Some(bs) = self.blend_space {
|
||||
c.set_blend_space(bs);
|
||||
}
|
||||
if let Some(use_native_gamut) = self.use_native_gamut {
|
||||
c.set_use_native_gamut(use_native_gamut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1744,6 +1744,10 @@
|
|||
"blend-space": {
|
||||
"description": "The blend space of the output.\n\nThe default is `srgb`.\n",
|
||||
"$ref": "#/$defs/BlendSpace"
|
||||
},
|
||||
"use-native-gamut": {
|
||||
"type": "boolean",
|
||||
"description": "Configures whether the display primaries are used.\n\nBy default, Jay pretends that the display uses sRGB primaries. This is also how\nmost other systems behave. In reality, most displays use a much larger gamut. For\nexample, they advertise that they support 95% of the DCI-P3 gamut. If the display\nis interpreting colors in their native gamut, then colors will appear more\nsaturated than their specification.\n\nIf this is set to `true`, Jay assumes that the display uses the primaries\nadvertised in its EDID. This might produce more accurate colors while also\nallowing color-managed applications to use the full gamut of the display.\n\nThis setting has no effect when the display is explicitly operating in a wide\ncolor space.\n\nThe default is `false`.\n"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -3836,6 +3836,27 @@ The table has the following fields:
|
|||
|
||||
The value of this field should be a [BlendSpace](#types-BlendSpace).
|
||||
|
||||
- `use-native-gamut` (optional):
|
||||
|
||||
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`.
|
||||
|
||||
The value of this field should be a boolean.
|
||||
|
||||
|
||||
<a name="types-OutputMatch"></a>
|
||||
### `OutputMatch`
|
||||
|
|
|
|||
|
|
@ -2069,6 +2069,26 @@ Output:
|
|||
The blend space of the output.
|
||||
|
||||
The default is `srgb`.
|
||||
use-native-gamut:
|
||||
kind: boolean
|
||||
required: false
|
||||
description: |
|
||||
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`.
|
||||
|
||||
|
||||
Transform:
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ request set_blend_space (since = 21) {
|
|||
blend_space: str,
|
||||
}
|
||||
|
||||
request set_use_native_gamut (since = 23) {
|
||||
output: str,
|
||||
use_native_gamut: u32,
|
||||
}
|
||||
|
||||
# events
|
||||
|
||||
event global {
|
||||
|
|
@ -210,3 +215,17 @@ event brightness (since = 16) {
|
|||
event blend_space (since = 21) {
|
||||
blend_space: str,
|
||||
}
|
||||
|
||||
event native_gamut (since = 23) {
|
||||
r_x: pod(f64),
|
||||
r_y: pod(f64),
|
||||
g_x: pod(f64),
|
||||
g_y: pod(f64),
|
||||
b_x: pod(f64),
|
||||
b_y: pod(f64),
|
||||
w_x: pod(f64),
|
||||
w_y: pod(f64),
|
||||
}
|
||||
|
||||
event use_native_gamut (since = 23) {
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue