Merge pull request #428 from mahkoh/jorth/output-brightness
video: allow configuring the output brightness
This commit is contained in:
commit
0ec3cdf608
19 changed files with 343 additions and 40 deletions
|
|
@ -795,6 +795,13 @@ impl Client {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn connector_set_brightness(&self, connector: Connector, brightness: Option<f64>) {
|
||||||
|
self.send(&ClientMessage::ConnectorSetBrightness {
|
||||||
|
connector,
|
||||||
|
brightness,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn connector_get_scale(&self, connector: Connector) -> f64 {
|
pub fn connector_get_scale(&self, connector: Connector) -> f64 {
|
||||||
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
|
let res = self.send_with_response(&ClientMessage::ConnectorGetScale { connector });
|
||||||
get_response!(res, 1.0, ConnectorGetScale { scale });
|
get_response!(res, 1.0, ConnectorGetScale { scale });
|
||||||
|
|
|
||||||
|
|
@ -538,6 +538,10 @@ pub enum ClientMessage<'a> {
|
||||||
color_space: ColorSpace,
|
color_space: ColorSpace,
|
||||||
transfer_function: TransferFunction,
|
transfer_function: TransferFunction,
|
||||||
},
|
},
|
||||||
|
ConnectorSetBrightness {
|
||||||
|
connector: Connector,
|
||||||
|
brightness: Option<f64>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -285,6 +285,22 @@ impl Connector {
|
||||||
pub fn set_colors(self, color_space: ColorSpace, transfer_function: TransferFunction) {
|
pub fn set_colors(self, color_space: ColorSpace, transfer_function: TransferFunction) {
|
||||||
get!().connector_set_colors(self, color_space, transfer_function);
|
get!().connector_set_colors(self, color_space, transfer_function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the brightness of the output.
|
||||||
|
///
|
||||||
|
/// By default or when `brightness` is `None`, the brightness depends on the
|
||||||
|
/// transfer function:
|
||||||
|
///
|
||||||
|
/// - [`TransferFunction::DEFAULT`]: The maximum brightness of the output.
|
||||||
|
/// - [`TransferFunction::PQ`]: 203 cd/m^2.
|
||||||
|
///
|
||||||
|
/// This should only be used with the PQ transfer function. If the default transfer
|
||||||
|
/// function is used, you should instead calibrate the hardware directly.
|
||||||
|
///
|
||||||
|
/// This has no effect unless the vulkan renderer is used.
|
||||||
|
pub fn set_brightness(self, brightness: Option<f64>) {
|
||||||
|
get!().connector_set_brightness(self, brightness);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all available DRM devices.
|
/// Returns all available DRM devices.
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
- Implement cursor-shape-v1 version 2.
|
- Implement cursor-shape-v1 version 2.
|
||||||
- Outputs can now optionally use the BT.2020/PQ color space.
|
- Outputs can now optionally use the BT.2020/PQ color space.
|
||||||
- Implement ext-shell version 7.
|
- Implement ext-shell version 7.
|
||||||
|
- The reference brightness of outputs can now be configured.
|
||||||
|
|
||||||
# 1.9.1 (2025-02-13)
|
# 1.9.1 (2025-02-13)
|
||||||
|
|
||||||
|
|
|
||||||
121
src/cli/randr.rs
121
src/cli/randr.rs
|
|
@ -22,6 +22,7 @@ use {
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
},
|
},
|
||||||
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Args, Debug)]
|
#[derive(Args, Debug)]
|
||||||
|
|
@ -161,6 +162,8 @@ pub enum OutputCommand {
|
||||||
Format(FormatSettings),
|
Format(FormatSettings),
|
||||||
/// Change color settings.
|
/// Change color settings.
|
||||||
Colors(ColorsSettings),
|
Colors(ColorsSettings),
|
||||||
|
/// Change the output brightness.
|
||||||
|
Brightness(BrightnessArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ValueEnum, Debug, Clone)]
|
#[derive(ValueEnum, Debug, Clone)]
|
||||||
|
|
@ -367,6 +370,43 @@ fn transfer_function_possible_values() -> Vec<PossibleValue> {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Args, Debug, Clone)]
|
||||||
|
pub struct BrightnessArgs {
|
||||||
|
/// The brightness of standard white in cd/m^2 or `default` to use the default
|
||||||
|
/// brightness.
|
||||||
|
///
|
||||||
|
/// The default brightness depends on the transfer function:
|
||||||
|
///
|
||||||
|
/// - default: the maximum display brightness
|
||||||
|
/// - PQ: 203 cd/m^2.
|
||||||
|
///
|
||||||
|
/// When using the default transfer function, you likely want to set this to `default`
|
||||||
|
/// and adjust the display hardware brightness setting instead.
|
||||||
|
///
|
||||||
|
/// This has no effect unless the vulkan renderer is used.
|
||||||
|
#[clap(verbatim_doc_comment, value_parser = parse_brightness)]
|
||||||
|
brightness: Brightness,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Brightness {
|
||||||
|
Default,
|
||||||
|
Lux(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("Value is neither `default` nor a floating point value")]
|
||||||
|
struct ParseBrightnessError;
|
||||||
|
|
||||||
|
fn parse_brightness(s: &str) -> Result<Brightness, ParseBrightnessError> {
|
||||||
|
if s == "default" {
|
||||||
|
return Ok(Brightness::Default);
|
||||||
|
}
|
||||||
|
f64::from_str(s)
|
||||||
|
.map(Brightness::Lux)
|
||||||
|
.map_err(|_| ParseBrightnessError)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main(global: GlobalArgs, args: RandrArgs) {
|
pub fn main(global: GlobalArgs, args: RandrArgs) {
|
||||||
with_tool_client(global.log_level.into(), |tc| async move {
|
with_tool_client(global.log_level.into(), |tc| async move {
|
||||||
let idle = Rc::new(Randr { tc: tc.clone() });
|
let idle = Rc::new(Randr { tc: tc.clone() });
|
||||||
|
|
@ -396,7 +436,7 @@ struct Connector {
|
||||||
pub output: Option<Output>,
|
pub output: Option<Output>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Default)]
|
||||||
struct Output {
|
struct Output {
|
||||||
pub scale: f64,
|
pub scale: f64,
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
|
|
@ -424,6 +464,8 @@ struct Output {
|
||||||
pub current_color_space: Option<String>,
|
pub current_color_space: Option<String>,
|
||||||
pub supported_transfer_functions: Vec<String>,
|
pub supported_transfer_functions: Vec<String>,
|
||||||
pub current_transfer_function: Option<String>,
|
pub current_transfer_function: Option<String>,
|
||||||
|
pub brightness_range: Option<(f64, f64)>,
|
||||||
|
pub brightness: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
@ -684,6 +726,26 @@ impl Randr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
OutputCommand::Brightness(a) => {
|
||||||
|
self.handle_error(randr, move |msg| {
|
||||||
|
eprintln!("Could not change the brightness: {}", msg);
|
||||||
|
});
|
||||||
|
match a.brightness {
|
||||||
|
Brightness::Default => {
|
||||||
|
tc.send(jay_randr::UnsetBrightness {
|
||||||
|
self_id: randr,
|
||||||
|
output: &args.output,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Brightness::Lux(lux) => {
|
||||||
|
tc.send(jay_randr::SetBrightness {
|
||||||
|
self_id: randr,
|
||||||
|
output: &args.output,
|
||||||
|
lux,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
}
|
}
|
||||||
|
|
@ -906,6 +968,15 @@ impl Randr {
|
||||||
.iter()
|
.iter()
|
||||||
.for_each(|tf| handle_tf(tf));
|
.for_each(|tf| handle_tf(tf));
|
||||||
}
|
}
|
||||||
|
if let Some((min, max)) = o.brightness_range {
|
||||||
|
println!(" min brightness: {:>10.4} cd/m^2", min);
|
||||||
|
println!(" max brightness: {:>10.4} cd/m^2", max);
|
||||||
|
} else {
|
||||||
|
println!(" max brightness: {:>10.4} cd/m^2 (implied)", 80.0);
|
||||||
|
}
|
||||||
|
if let Some(lux) = o.brightness {
|
||||||
|
println!(" brightness: {:>10.4} cd/m^2", lux);
|
||||||
|
}
|
||||||
if o.modes.is_not_empty() && modes {
|
if o.modes.is_not_empty() && modes {
|
||||||
println!(" modes:");
|
println!(" modes:");
|
||||||
for mode in &o.modes {
|
for mode in &o.modes {
|
||||||
|
|
@ -975,21 +1046,7 @@ impl Randr {
|
||||||
serial_number: msg.serial_number.to_string(),
|
serial_number: msg.serial_number.to_string(),
|
||||||
width_mm: msg.width_mm,
|
width_mm: msg.width_mm,
|
||||||
height_mm: msg.height_mm,
|
height_mm: msg.height_mm,
|
||||||
modes: Default::default(),
|
..Default::default()
|
||||||
current_mode: None,
|
|
||||||
non_desktop: false,
|
|
||||||
vrr_capable: false,
|
|
||||||
vrr_enabled: false,
|
|
||||||
vrr_mode: VrrMode::NEVER,
|
|
||||||
vrr_cursor_hz: None,
|
|
||||||
tearing_mode: TearingMode::NEVER,
|
|
||||||
formats: vec![],
|
|
||||||
format: None,
|
|
||||||
flip_margin_ns: None,
|
|
||||||
supported_color_spaces: vec![],
|
|
||||||
current_color_space: None,
|
|
||||||
supported_transfer_functions: vec![],
|
|
||||||
current_transfer_function: None,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||||
|
|
@ -997,31 +1054,13 @@ impl Randr {
|
||||||
let c = data.connectors.last_mut().unwrap();
|
let c = data.connectors.last_mut().unwrap();
|
||||||
c.output = Some(Output {
|
c.output = Some(Output {
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
transform: Transform::None,
|
|
||||||
manufacturer: msg.manufacturer.to_string(),
|
manufacturer: msg.manufacturer.to_string(),
|
||||||
product: msg.product.to_string(),
|
product: msg.product.to_string(),
|
||||||
serial_number: msg.serial_number.to_string(),
|
serial_number: msg.serial_number.to_string(),
|
||||||
width_mm: msg.width_mm,
|
width_mm: msg.width_mm,
|
||||||
height_mm: msg.height_mm,
|
height_mm: msg.height_mm,
|
||||||
modes: Default::default(),
|
|
||||||
current_mode: None,
|
|
||||||
non_desktop: true,
|
non_desktop: true,
|
||||||
vrr_capable: false,
|
..Default::default()
|
||||||
vrr_enabled: false,
|
|
||||||
vrr_mode: VrrMode::NEVER,
|
|
||||||
vrr_cursor_hz: None,
|
|
||||||
tearing_mode: TearingMode::NEVER,
|
|
||||||
formats: vec![],
|
|
||||||
format: None,
|
|
||||||
flip_margin_ns: None,
|
|
||||||
supported_color_spaces: vec![],
|
|
||||||
current_color_space: None,
|
|
||||||
supported_transfer_functions: vec![],
|
|
||||||
current_transfer_function: None,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
||||||
|
|
@ -1102,6 +1141,18 @@ impl Randr {
|
||||||
let output = c.output.as_mut().unwrap();
|
let output = c.output.as_mut().unwrap();
|
||||||
output.current_transfer_function = Some(msg.transfer_function.to_string());
|
output.current_transfer_function = Some(msg.transfer_function.to_string());
|
||||||
});
|
});
|
||||||
|
jay_randr::BrightnessRange::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();
|
||||||
|
output.brightness_range = Some((msg.min, msg.max));
|
||||||
|
});
|
||||||
|
jay_randr::Brightness::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();
|
||||||
|
output.brightness = Some(msg.lux);
|
||||||
|
});
|
||||||
tc.round_trip().await;
|
tc.round_trip().await;
|
||||||
let x = data.borrow_mut().clone();
|
let x = data.borrow_mut().clone();
|
||||||
x
|
x
|
||||||
|
|
|
||||||
|
|
@ -536,6 +536,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
vrr_mode: Cell::new(VrrMode::NEVER),
|
vrr_mode: Cell::new(VrrMode::NEVER),
|
||||||
vrr_cursor_hz: Default::default(),
|
vrr_cursor_hz: Default::default(),
|
||||||
tearing_mode: Cell::new(&TearingMode::Never),
|
tearing_mode: Cell::new(&TearingMode::Never),
|
||||||
|
brightness: Cell::new(None),
|
||||||
});
|
});
|
||||||
let connector = Rc::new(DummyOutput {
|
let connector = Rc::new(DummyOutput {
|
||||||
id: state.connector_ids.next(),
|
id: state.connector_ids.next(),
|
||||||
|
|
|
||||||
|
|
@ -1126,6 +1126,16 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_connector_set_brightness(
|
||||||
|
&self,
|
||||||
|
connector: Connector,
|
||||||
|
brightness: Option<f64>,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
let connector = self.get_output_node(connector)?;
|
||||||
|
connector.global.set_brightness(brightness);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_set_vrr_mode(
|
fn handle_set_vrr_mode(
|
||||||
&self,
|
&self,
|
||||||
connector: Option<Connector>,
|
connector: Option<Connector>,
|
||||||
|
|
@ -2023,6 +2033,12 @@ impl ConfigProxyHandler {
|
||||||
} => self
|
} => self
|
||||||
.handle_connector_set_colors(connector, color_space, transfer_function)
|
.handle_connector_set_colors(connector, color_space, transfer_function)
|
||||||
.wrn("connector_set_colors")?,
|
.wrn("connector_set_colors")?,
|
||||||
|
ClientMessage::ConnectorSetBrightness {
|
||||||
|
connector,
|
||||||
|
brightness,
|
||||||
|
} => self
|
||||||
|
.handle_connector_set_brightness(connector, brightness)
|
||||||
|
.wrn("connector_set_brightness")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl Global for JayCompositorGlobal {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self) -> u32 {
|
fn version(&self) -> u32 {
|
||||||
15
|
16
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_caps(&self) -> ClientCaps {
|
fn required_caps(&self) -> ClientCaps {
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ const TEARING_SINCE: Version = Version(3);
|
||||||
const FORMAT_SINCE: Version = Version(8);
|
const FORMAT_SINCE: Version = Version(8);
|
||||||
const FLIP_MARGIN_SINCE: Version = Version(10);
|
const FLIP_MARGIN_SINCE: Version = Version(10);
|
||||||
const COLORIMETRY_SINCE: Version = Version(15);
|
const COLORIMETRY_SINCE: Version = Version(15);
|
||||||
|
const BRIGHTNESS_SINCE: Version = Version(16);
|
||||||
|
|
||||||
impl JayRandr {
|
impl JayRandr {
|
||||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||||
|
|
@ -187,6 +188,22 @@ impl JayRandr {
|
||||||
color_space: node.global.bcs.get().name(),
|
color_space: node.global.bcs.get().name(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if self.version >= BRIGHTNESS_SINCE {
|
||||||
|
if let Some(lum) = node.global.luminance {
|
||||||
|
self.client.event(BrightnessRange {
|
||||||
|
self_id: self.id,
|
||||||
|
min: lum.min,
|
||||||
|
max: lum.max,
|
||||||
|
max_fall: lum.max_fall,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if let Some(lux) = node.global.persistent.brightness.get() {
|
||||||
|
self.client.event(Brightness {
|
||||||
|
self_id: self.id,
|
||||||
|
lux,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_error(&self, msg: &str) {
|
fn send_error(&self, msg: &str) {
|
||||||
|
|
@ -464,6 +481,26 @@ impl JayRandrRequestHandler for JayRandr {
|
||||||
c.connector.set_colors(cs, tf);
|
c.connector.set_colors(cs, tf);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_brightness(&self, req: SetBrightness<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let Some(c) = self.get_output_node(req.output) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
c.global.set_brightness(Some(req.lux));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unset_brightness(
|
||||||
|
&self,
|
||||||
|
req: UnsetBrightness<'_>,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let Some(c) = self.get_output_node(req.output) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
c.global.set_brightness(None);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object_base! {
|
object_base! {
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ pub struct PersistentOutputState {
|
||||||
pub vrr_mode: Cell<&'static VrrMode>,
|
pub vrr_mode: Cell<&'static VrrMode>,
|
||||||
pub vrr_cursor_hz: Cell<Option<f64>>,
|
pub vrr_cursor_hz: Cell<Option<f64>>,
|
||||||
pub tearing_mode: Cell<&'static TearingMode>,
|
pub tearing_mode: Cell<&'static TearingMode>,
|
||||||
|
pub brightness: Cell<Option<f64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Hash, Debug)]
|
#[derive(Eq, PartialEq, Hash, Debug)]
|
||||||
|
|
@ -332,9 +333,21 @@ impl WlOutputGlobal {
|
||||||
pub fn update_color_description(&self) -> bool {
|
pub fn update_color_description(&self) -> bool {
|
||||||
let mut luminance = Luminance::SRGB;
|
let mut luminance = Luminance::SRGB;
|
||||||
let tf = match self.btf.get() {
|
let tf = match self.btf.get() {
|
||||||
BackendTransferFunction::Default => TransferFunction::Srgb,
|
BackendTransferFunction::Default => {
|
||||||
|
if let Some(brightness) = self.persistent.brightness.get() {
|
||||||
|
let output_max = match self.luminance {
|
||||||
|
None => 80.0,
|
||||||
|
Some(v) => v.max,
|
||||||
|
};
|
||||||
|
luminance.white.0 = luminance.max.0 * brightness / output_max;
|
||||||
|
}
|
||||||
|
TransferFunction::Srgb
|
||||||
|
}
|
||||||
BackendTransferFunction::Pq => {
|
BackendTransferFunction::Pq => {
|
||||||
luminance = Luminance::ST2084_PQ;
|
luminance = Luminance::ST2084_PQ;
|
||||||
|
if let Some(brightness) = self.persistent.brightness.get() {
|
||||||
|
luminance.white.0 = brightness;
|
||||||
|
}
|
||||||
TransferFunction::St2084Pq
|
TransferFunction::St2084Pq
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -368,6 +381,12 @@ impl WlOutputGlobal {
|
||||||
self.linear_color_description.set(cd_linear.clone());
|
self.linear_color_description.set(cd_linear.clone());
|
||||||
self.color_description.set(cd.clone()).id != cd.id
|
self.color_description.set(cd.clone()).id != cd.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_brightness(&self, brightness: Option<f64>) {
|
||||||
|
self.persistent.brightness.set(brightness);
|
||||||
|
self.update_color_description();
|
||||||
|
self.state.damage(self.pos.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
|
global_base!(WlOutputGlobal, WlOutput, WlOutputError);
|
||||||
|
|
|
||||||
|
|
@ -119,6 +119,7 @@ impl ConnectorHandler {
|
||||||
vrr_mode: Cell::new(self.state.default_vrr_mode.get()),
|
vrr_mode: Cell::new(self.state.default_vrr_mode.get()),
|
||||||
vrr_cursor_hz: Cell::new(self.state.default_vrr_cursor_hz.get()),
|
vrr_cursor_hz: Cell::new(self.state.default_vrr_cursor_hz.get()),
|
||||||
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
||||||
|
brightness: Cell::new(None),
|
||||||
});
|
});
|
||||||
self.state
|
self.state
|
||||||
.persistent_output_states
|
.persistent_output_states
|
||||||
|
|
|
||||||
|
|
@ -332,7 +332,7 @@ impl ToolClient {
|
||||||
self_id: s.registry,
|
self_id: s.registry,
|
||||||
name: s.jay_compositor.0,
|
name: s.jay_compositor.0,
|
||||||
interface: JayCompositor.name(),
|
interface: JayCompositor.name(),
|
||||||
version: s.jay_compositor.1.min(15),
|
version: s.jay_compositor.1.min(16),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
});
|
});
|
||||||
self.jay_compositor.set(Some(id));
|
self.jay_compositor.set(Some(id));
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,7 @@ pub struct Output {
|
||||||
pub format: Option<Format>,
|
pub format: Option<Format>,
|
||||||
pub color_space: Option<ColorSpace>,
|
pub color_space: Option<ColorSpace>,
|
||||||
pub transfer_function: Option<TransferFunction>,
|
pub transfer_function: Option<TransferFunction>,
|
||||||
|
pub brightness: Option<Option<f64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
toml::{
|
toml::{
|
||||||
toml_span::{DespanExt, Span, Spanned},
|
toml_span::{DespanExt, Span, Spanned, SpannedExt},
|
||||||
toml_value::Value,
|
toml_value::Value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -51,7 +51,7 @@ impl Parser for OutputParser<'_> {
|
||||||
let mut ext = Extractor::new(self.cx, span, table);
|
let mut ext = Extractor::new(self.cx, span, table);
|
||||||
let (
|
let (
|
||||||
(name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val),
|
(name, match_val, x, y, scale, transform, mode, vrr_val, tearing_val, format_val),
|
||||||
(color_space, transfer_function),
|
(color_space, transfer_function, brightness_val),
|
||||||
) = ext.extract((
|
) = ext.extract((
|
||||||
(
|
(
|
||||||
opt(str("name")),
|
opt(str("name")),
|
||||||
|
|
@ -68,6 +68,7 @@ impl Parser for OutputParser<'_> {
|
||||||
(
|
(
|
||||||
recover(opt(str("color-space"))),
|
recover(opt(str("color-space"))),
|
||||||
recover(opt(str("transfer-function"))),
|
recover(opt(str("transfer-function"))),
|
||||||
|
opt(val("brightness")),
|
||||||
),
|
),
|
||||||
))?;
|
))?;
|
||||||
let transform = match transform {
|
let transform = match transform {
|
||||||
|
|
@ -171,6 +172,15 @@ impl Parser for OutputParser<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut brightness = None;
|
||||||
|
if let Some(value) = brightness_val {
|
||||||
|
match value.parse(&mut BrightnessParser) {
|
||||||
|
Ok(v) => brightness = Some(v),
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Could not parse brightness setting: {}", self.cx.error(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(Output {
|
Ok(Output {
|
||||||
name: name.despan().map(|v| v.to_string()),
|
name: name.despan().map(|v| v.to_string()),
|
||||||
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
|
match_: match_val.parse_map(&mut OutputMatchParser(self.cx))?,
|
||||||
|
|
@ -184,6 +194,7 @@ impl Parser for OutputParser<'_> {
|
||||||
format,
|
format,
|
||||||
color_space,
|
color_space,
|
||||||
transfer_function,
|
transfer_function,
|
||||||
|
brightness,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,3 +239,34 @@ impl Parser for OutputsParser<'_> {
|
||||||
.map(|v| vec![v])
|
.map(|v| vec![v])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct BrightnessParser;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum BrightnessParserError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Expected(#[from] UnexpectedDataType),
|
||||||
|
#[error("Expected `default`")]
|
||||||
|
UnexpectedString(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser for BrightnessParser {
|
||||||
|
type Value = Option<f64>;
|
||||||
|
type Error = BrightnessParserError;
|
||||||
|
const EXPECTED: &'static [DataType] = &[DataType::Float, DataType::Integer, DataType::String];
|
||||||
|
|
||||||
|
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
|
||||||
|
if string == "default" {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
Err(BrightnessParserError::UnexpectedString(string.to_string()).spanned(span))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_integer(&mut self, _span: Span, integer: i64) -> ParseResult<Self> {
|
||||||
|
Ok(Some(integer as _))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_float(&mut self, _span: Span, float: f64) -> ParseResult<Self> {
|
||||||
|
Ok(Some(float))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -593,6 +593,9 @@ impl Output {
|
||||||
let tf = self.transfer_function.unwrap_or(TransferFunction::DEFAULT);
|
let tf = self.transfer_function.unwrap_or(TransferFunction::DEFAULT);
|
||||||
c.set_colors(cs, tf);
|
c.set_colors(cs, tf);
|
||||||
}
|
}
|
||||||
|
if let Some(brightness) = self.brightness {
|
||||||
|
c.set_brightness(brightness);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -423,6 +423,22 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"Brightness": {
|
||||||
|
"description": "The brightness setting of an output.\n",
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "The default brightness setting.\n",
|
||||||
|
"enum": [
|
||||||
|
"default"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"description": "The brightness in cd/m^2.\n"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"Color": {
|
"Color": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A color.\n\nThe format should be one of the following:\n\n- `#rgb`\n- `#rrggbb`\n- `#rgba`\n- `#rrggbba`\n"
|
"description": "A color.\n\nThe format should be one of the following:\n\n- `#rgb`\n- `#rrggbb`\n- `#rgba`\n- `#rrggbba`\n"
|
||||||
|
|
@ -1168,6 +1184,10 @@
|
||||||
"transfer-function": {
|
"transfer-function": {
|
||||||
"description": "The transfer function of the output.\n",
|
"description": "The transfer function of the output.\n",
|
||||||
"$ref": "#/$defs/TransferFunction"
|
"$ref": "#/$defs/TransferFunction"
|
||||||
|
},
|
||||||
|
"brightness": {
|
||||||
|
"description": "The brightness of the output.\n\nThis setting has no effect unless the vulkan renderer is used.\n",
|
||||||
|
"$ref": "#/$defs/Brightness"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|
|
||||||
|
|
@ -575,6 +575,34 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
The value of this field should be a [DrmDeviceMatch](#types-DrmDeviceMatch).
|
The value of this field should be a [DrmDeviceMatch](#types-DrmDeviceMatch).
|
||||||
|
|
||||||
|
|
||||||
|
<a name="types-Brightness"></a>
|
||||||
|
### `Brightness`
|
||||||
|
|
||||||
|
The brightness setting of an output.
|
||||||
|
|
||||||
|
Values of this type should have one of the following forms:
|
||||||
|
|
||||||
|
#### A string
|
||||||
|
|
||||||
|
The default brightness setting.
|
||||||
|
|
||||||
|
The string should have one of the following values:
|
||||||
|
|
||||||
|
- `default`:
|
||||||
|
|
||||||
|
The default brightness setting.
|
||||||
|
|
||||||
|
The behavior depends on the transfer function:
|
||||||
|
|
||||||
|
- `default`: The maximum brightness of the output.
|
||||||
|
- `PQ`: 203 cd/m^2
|
||||||
|
|
||||||
|
|
||||||
|
#### A number
|
||||||
|
|
||||||
|
The brightness in cd/m^2.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-Color"></a>
|
<a name="types-Color"></a>
|
||||||
### `Color`
|
### `Color`
|
||||||
|
|
||||||
|
|
@ -2561,6 +2589,14 @@ The table has the following fields:
|
||||||
|
|
||||||
The value of this field should be a [TransferFunction](#types-TransferFunction).
|
The value of this field should be a [TransferFunction](#types-TransferFunction).
|
||||||
|
|
||||||
|
- `brightness` (optional):
|
||||||
|
|
||||||
|
The brightness of the output.
|
||||||
|
|
||||||
|
This setting has no effect unless the vulkan renderer is used.
|
||||||
|
|
||||||
|
The value of this field should be a [Brightness](#types-Brightness).
|
||||||
|
|
||||||
|
|
||||||
<a name="types-OutputMatch"></a>
|
<a name="types-OutputMatch"></a>
|
||||||
### `OutputMatch`
|
### `OutputMatch`
|
||||||
|
|
|
||||||
|
|
@ -1641,6 +1641,13 @@ Output:
|
||||||
required: false
|
required: false
|
||||||
description: |
|
description: |
|
||||||
The transfer function of the output.
|
The transfer function of the output.
|
||||||
|
brightness:
|
||||||
|
ref: Brightness
|
||||||
|
required: false
|
||||||
|
description: |
|
||||||
|
The brightness of the output.
|
||||||
|
|
||||||
|
This setting has no effect unless the vulkan renderer is used.
|
||||||
|
|
||||||
|
|
||||||
Transform:
|
Transform:
|
||||||
|
|
@ -2794,3 +2801,25 @@ TransferFunction:
|
||||||
description: The default transfer function (usually sRGB).
|
description: The default transfer function (usually sRGB).
|
||||||
- value: pq
|
- value: pq
|
||||||
description: The PQ transfer function.
|
description: The PQ transfer function.
|
||||||
|
|
||||||
|
|
||||||
|
Brightness:
|
||||||
|
kind: variable
|
||||||
|
description: |
|
||||||
|
The brightness setting of an output.
|
||||||
|
variants:
|
||||||
|
- kind: string
|
||||||
|
description: |
|
||||||
|
The default brightness setting.
|
||||||
|
values:
|
||||||
|
- value: default
|
||||||
|
description: |
|
||||||
|
The default brightness setting.
|
||||||
|
|
||||||
|
The behavior depends on the transfer function:
|
||||||
|
|
||||||
|
- `default`: The maximum brightness of the output.
|
||||||
|
- `PQ`: 203 cd/m^2
|
||||||
|
- kind: number
|
||||||
|
description: |
|
||||||
|
The brightness in cd/m^2.
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,15 @@ request set_colors (since = 15) {
|
||||||
transfer_function: str,
|
transfer_function: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
request set_brightness (since = 16) {
|
||||||
|
output: str,
|
||||||
|
lux: pod(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
request unset_brightness (since = 16) {
|
||||||
|
output: str,
|
||||||
|
}
|
||||||
|
|
||||||
# events
|
# events
|
||||||
|
|
||||||
event global {
|
event global {
|
||||||
|
|
@ -182,3 +191,13 @@ event supported_transfer_function (since = 15) {
|
||||||
event current_transfer_function (since = 15) {
|
event current_transfer_function (since = 15) {
|
||||||
transfer_function: str,
|
transfer_function: str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event brightness_range (since = 16) {
|
||||||
|
min: pod(f64),
|
||||||
|
max: pod(f64),
|
||||||
|
max_fall: pod(f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
event brightness (since = 16) {
|
||||||
|
lux: pod(f64),
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue