video: allow configuring the output brightness
This commit is contained in:
parent
7d0c9e2c3f
commit
27025a565c
19 changed files with 343 additions and 40 deletions
121
src/cli/randr.rs
121
src/cli/randr.rs
|
|
@ -22,6 +22,7 @@ use {
|
|||
str::FromStr,
|
||||
time::Duration,
|
||||
},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
|
|
@ -161,6 +162,8 @@ pub enum OutputCommand {
|
|||
Format(FormatSettings),
|
||||
/// Change color settings.
|
||||
Colors(ColorsSettings),
|
||||
/// Change the output brightness.
|
||||
Brightness(BrightnessArgs),
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Debug, Clone)]
|
||||
|
|
@ -367,6 +370,43 @@ fn transfer_function_possible_values() -> Vec<PossibleValue> {
|
|||
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) {
|
||||
with_tool_client(global.log_level.into(), |tc| async move {
|
||||
let idle = Rc::new(Randr { tc: tc.clone() });
|
||||
|
|
@ -396,7 +436,7 @@ struct Connector {
|
|||
pub output: Option<Output>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct Output {
|
||||
pub scale: f64,
|
||||
pub width: i32,
|
||||
|
|
@ -424,6 +464,8 @@ struct Output {
|
|||
pub current_color_space: Option<String>,
|
||||
pub supported_transfer_functions: Vec<String>,
|
||||
pub current_transfer_function: Option<String>,
|
||||
pub brightness_range: Option<(f64, f64)>,
|
||||
pub brightness: Option<f64>,
|
||||
}
|
||||
|
||||
#[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;
|
||||
}
|
||||
|
|
@ -906,6 +968,15 @@ impl Randr {
|
|||
.iter()
|
||||
.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 {
|
||||
println!(" modes:");
|
||||
for mode in &o.modes {
|
||||
|
|
@ -975,21 +1046,7 @@ impl Randr {
|
|||
serial_number: msg.serial_number.to_string(),
|
||||
width_mm: msg.width_mm,
|
||||
height_mm: msg.height_mm,
|
||||
modes: 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,
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
jay_randr::NonDesktopOutput::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -997,31 +1054,13 @@ impl Randr {
|
|||
let c = data.connectors.last_mut().unwrap();
|
||||
c.output = Some(Output {
|
||||
scale: 1.0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transform: Transform::None,
|
||||
manufacturer: msg.manufacturer.to_string(),
|
||||
product: msg.product.to_string(),
|
||||
serial_number: msg.serial_number.to_string(),
|
||||
width_mm: msg.width_mm,
|
||||
height_mm: msg.height_mm,
|
||||
modes: Default::default(),
|
||||
current_mode: None,
|
||||
non_desktop: true,
|
||||
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,
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
jay_randr::VrrState::handle(tc, randr, data.clone(), |data, msg| {
|
||||
|
|
@ -1102,6 +1141,18 @@ impl Randr {
|
|||
let output = c.output.as_mut().unwrap();
|
||||
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;
|
||||
let x = data.borrow_mut().clone();
|
||||
x
|
||||
|
|
|
|||
|
|
@ -536,6 +536,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
vrr_mode: Cell::new(VrrMode::NEVER),
|
||||
vrr_cursor_hz: Default::default(),
|
||||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
brightness: Cell::new(None),
|
||||
});
|
||||
let connector = Rc::new(DummyOutput {
|
||||
id: state.connector_ids.next(),
|
||||
|
|
|
|||
|
|
@ -1126,6 +1126,16 @@ impl ConfigProxyHandler {
|
|||
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(
|
||||
&self,
|
||||
connector: Option<Connector>,
|
||||
|
|
@ -2023,6 +2033,12 @@ impl ConfigProxyHandler {
|
|||
} => self
|
||||
.handle_connector_set_colors(connector, color_space, transfer_function)
|
||||
.wrn("connector_set_colors")?,
|
||||
ClientMessage::ConnectorSetBrightness {
|
||||
connector,
|
||||
brightness,
|
||||
} => self
|
||||
.handle_connector_set_brightness(connector, brightness)
|
||||
.wrn("connector_set_brightness")?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ impl Global for JayCompositorGlobal {
|
|||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
15
|
||||
16
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ const TEARING_SINCE: Version = Version(3);
|
|||
const FORMAT_SINCE: Version = Version(8);
|
||||
const FLIP_MARGIN_SINCE: Version = Version(10);
|
||||
const COLORIMETRY_SINCE: Version = Version(15);
|
||||
const BRIGHTNESS_SINCE: Version = Version(16);
|
||||
|
||||
impl JayRandr {
|
||||
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
|
||||
|
|
@ -187,6 +188,22 @@ impl JayRandr {
|
|||
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) {
|
||||
|
|
@ -464,6 +481,26 @@ impl JayRandrRequestHandler for JayRandr {
|
|||
c.connector.set_colors(cs, tf);
|
||||
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! {
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ pub struct PersistentOutputState {
|
|||
pub vrr_mode: Cell<&'static VrrMode>,
|
||||
pub vrr_cursor_hz: Cell<Option<f64>>,
|
||||
pub tearing_mode: Cell<&'static TearingMode>,
|
||||
pub brightness: Cell<Option<f64>>,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash, Debug)]
|
||||
|
|
@ -332,9 +333,21 @@ impl WlOutputGlobal {
|
|||
pub fn update_color_description(&self) -> bool {
|
||||
let mut luminance = Luminance::SRGB;
|
||||
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 => {
|
||||
luminance = Luminance::ST2084_PQ;
|
||||
if let Some(brightness) = self.persistent.brightness.get() {
|
||||
luminance.white.0 = brightness;
|
||||
}
|
||||
TransferFunction::St2084Pq
|
||||
}
|
||||
};
|
||||
|
|
@ -368,6 +381,12 @@ impl WlOutputGlobal {
|
|||
self.linear_color_description.set(cd_linear.clone());
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ impl ConnectorHandler {
|
|||
vrr_mode: Cell::new(self.state.default_vrr_mode.get()),
|
||||
vrr_cursor_hz: Cell::new(self.state.default_vrr_cursor_hz.get()),
|
||||
tearing_mode: Cell::new(self.state.default_tearing_mode.get()),
|
||||
brightness: Cell::new(None),
|
||||
});
|
||||
self.state
|
||||
.persistent_output_states
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ impl ToolClient {
|
|||
self_id: s.registry,
|
||||
name: s.jay_compositor.0,
|
||||
interface: JayCompositor.name(),
|
||||
version: s.jay_compositor.1.min(15),
|
||||
version: s.jay_compositor.1.min(16),
|
||||
id: id.into(),
|
||||
});
|
||||
self.jay_compositor.set(Some(id));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue