1
0
Fork 0
forked from wry/wry

config: make the blend space configurable

This commit is contained in:
Julian Orth 2025-09-05 19:19:54 +02:00
parent 991b212120
commit 39c770f6e2
20 changed files with 257 additions and 15 deletions

View file

@ -13,6 +13,7 @@ use {
AcquireSync, BufferResv, GfxApiOpt, GfxRenderPass, GfxTexture, ReleaseSync, SyncFile,
create_render_pass,
},
ifs::wl_output::BlendSpace,
rect::Region,
theme::Color,
time::Time,
@ -201,7 +202,11 @@ impl MetalConnector {
let buffer = &buffers[next_buffer_idx];
let cd = node.global.color_description.get();
let blend_cd = self.state.color_manager.srgb_gamma22();
let linear_cd = node.global.linear_color_description.get();
let blend_cd = match node.global.persistent.blend_space.get() {
BlendSpace::Linear => &linear_cd,
BlendSpace::Srgb => self.state.color_manager.srgb_gamma22(),
};
if self.has_damage.get() > 0 || self.cursor_damage.get() {
node.schedule.commit_cursor();

View file

@ -3,6 +3,7 @@ use {
backend::{BackendColorSpace, BackendEotfs},
cli::GlobalArgs,
format::{Format, XRGB8888},
ifs::wl_output::BlendSpace,
scale::Scale,
tools::tool_client::{Handle, ToolClient, with_tool_client},
utils::{errorfmt::ErrorFmt, transform_ext::TransformExt},
@ -164,6 +165,8 @@ pub enum OutputCommand {
Colors(ColorsSettings),
/// Change the output brightness.
Brightness(BrightnessArgs),
/// Change the blend space.
BlendSpace(BlendSpaceArgs),
}
#[derive(ValueEnum, Debug, Clone)]
@ -407,6 +410,26 @@ fn parse_brightness(s: &str) -> Result<Brightness, ParseBrightnessError> {
.map_err(|_| ParseBrightnessError)
}
#[derive(Args, Debug, Clone)]
pub struct BlendSpaceArgs {
/// The space to blend translucent surfaces in.
#[clap(value_parser = PossibleValuesParser::new(blend_space_possible_values()))]
blend_space: String,
}
fn blend_space_possible_values() -> Vec<PossibleValue> {
let mut res = vec![];
for bs in BlendSpace::variants() {
use BlendSpace::*;
let help = match bs {
Linear => "Linear space, more accurate but brighter",
Srgb => "sRGB space, the classic desktop blend space",
};
res.push(PossibleValue::new(bs.name()).help(help));
}
res
}
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() });
@ -466,6 +489,7 @@ struct Output {
pub current_eotf: Option<String>,
pub brightness_range: Option<(f64, f64)>,
pub brightness: Option<f64>,
pub blend_space: Option<String>,
}
#[derive(Copy, Clone, Debug)]
@ -743,6 +767,16 @@ impl Randr {
}
}
}
OutputCommand::BlendSpace(a) => {
self.handle_error(randr, move |msg| {
eprintln!("Could not set the blend space: {}", msg);
});
tc.send(jay_randr::SetBlendSpace {
self_id: randr,
output: &args.output,
blend_space: &a.blend_space,
});
}
}
tc.round_trip().await;
}
@ -975,6 +1009,9 @@ impl Randr {
if let Some(lux) = o.brightness {
println!(" brightness: {:>10.4} cd/m^2", lux);
}
if let Some(bs) = &o.blend_space {
println!(" blend space: {bs}");
}
if o.modes.is_not_empty() && modes {
println!(" modes:");
for mode in &o.modes {
@ -1149,6 +1186,12 @@ impl Randr {
let output = c.output.as_mut().unwrap();
output.brightness = Some(msg.lux);
});
jay_randr::BlendSpace::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.blend_space = Some(msg.blend_space.to_string());
});
tc.round_trip().await;
data.borrow_mut().clone()
}

View file

@ -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::{OutputId, PersistentOutputState, WlOutputGlobal},
wl_output::{BlendSpace, OutputId, PersistentOutputState, WlOutputGlobal},
wl_seat::handle_position_hint_requests,
wl_surface::{
NoneSurfaceExt, xdg_surface::handle_xdg_surface_configure_events,
@ -636,6 +636,7 @@ fn create_dummy_output(state: &Rc<State>) {
vrr_cursor_hz: Default::default(),
tearing_mode: Cell::new(&TearingMode::Never),
brightness: Cell::new(None),
blend_space: Cell::new(BlendSpace::Srgb),
});
let mode = backend::Mode {
width: 0,

View file

@ -17,6 +17,7 @@ use {
},
format::config_formats,
ifs::{
wl_output::BlendSpace,
wl_seat::{SeatId, WlSeatGlobal},
wp_content_type_v1::ContentTypeExt,
},
@ -69,8 +70,9 @@ use {
theme::{colors::Colorable, sized::Resizable},
timer::Timer as JayTimer,
video::{
ColorSpace, Connector, DrmDevice, Eotf as ConfigEotf, Format as ConfigFormat, GfxApi,
TearingMode as ConfigTearingMode, Transform, VrrMode as ConfigVrrMode,
BlendSpace as ConfigBlendSpace, ColorSpace, Connector, DrmDevice, Eotf as ConfigEotf,
Format as ConfigFormat, GfxApi, TearingMode as ConfigTearingMode, Transform,
VrrMode as ConfigVrrMode,
},
window::{TileState, Window, WindowMatcher},
workspace::WorkspaceDisplayOrder,
@ -1306,6 +1308,21 @@ impl ConfigProxyHandler {
Ok(())
}
fn handle_connector_set_blend_space(
&self,
connector: Connector,
blend_space: ConfigBlendSpace,
) -> Result<(), CphError> {
let blend_space = match blend_space {
ConfigBlendSpace::SRGB => BlendSpace::Srgb,
ConfigBlendSpace::LINEAR => BlendSpace::Linear,
_ => return Err(CphError::UnknownBlendSpace(blend_space)),
};
let connector = self.get_output_node(connector)?;
connector.set_blend_space(blend_space);
Ok(())
}
fn handle_connector_set_brightness(
&self,
connector: Connector,
@ -3117,6 +3134,12 @@ impl ConfigProxyHandler {
ClientMessage::SeatCopyMark { seat, src, dst } => self
.handle_seat_copy_mark(seat, src, dst)
.wrn("seat_copy_mark")?,
ClientMessage::ConnectorSetBlendSpace {
connector,
blend_space,
} => self
.handle_connector_set_blend_space(connector, blend_space)
.wrn("connector_set_blend_space")?,
}
Ok(())
}
@ -3226,6 +3249,8 @@ enum CphError {
WindowMatcherDoesNotExist(WindowMatcher),
#[error("Could not modify the connector state")]
ModifyConnectorState(#[source] BackendConnectorTransactionError),
#[error("Unknown blend space {0:?}")]
UnknownBlendSpace(ConfigBlendSpace),
}
trait WithRequestName {

View file

@ -79,7 +79,7 @@ impl Global for JayCompositorGlobal {
}
fn version(&self) -> u32 {
20
21
}
fn required_caps(&self) -> ClientCaps {

View file

@ -4,6 +4,7 @@ use {
client::{Client, ClientError},
compositor::MAX_EXTENTS,
format::named_formats,
ifs::wl_output,
leaks::Tracker,
object::{Object, Version},
scale::Scale,
@ -34,6 +35,7 @@ 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);
const BLEND_SPACE_SINCE: Version = Version(21);
impl JayRandr {
pub fn new(id: JayRandrId, client: &Rc<Client>, version: Version) -> Self {
@ -207,6 +209,12 @@ impl JayRandr {
});
}
}
if self.version >= BLEND_SPACE_SINCE {
self.client.event(BlendSpace {
self_id: self.id,
blend_space: node.global.persistent.blend_space.get().name(),
});
}
}
fn send_error(&self, msg: &str) {
@ -526,6 +534,23 @@ impl JayRandrRequestHandler for JayRandr {
c.set_brightness(None);
Ok(())
}
fn set_blend_space(&self, req: SetBlendSpace<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let space = 'space: {
for space in wl_output::BlendSpace::variants() {
if space.name() == req.blend_space {
break 'space space;
}
}
self.send_error(&format!("Unknown blend space: {}", req.blend_space));
return Ok(());
};
let Some(c) = self.get_output_node(req.output) else {
return Ok(());
};
c.set_blend_space(space);
Ok(())
}
}
object_base! {

View file

@ -30,6 +30,7 @@ use {
},
ahash::AHashMap,
jay_config::video::Transform,
linearize::Linearize,
std::{
cell::{Cell, RefCell},
collections::hash_map::Entry,
@ -115,6 +116,21 @@ impl OutputGlobalOpt {
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Linearize)]
pub enum BlendSpace {
Linear,
Srgb,
}
impl BlendSpace {
pub fn name(self) -> &'static str {
match self {
BlendSpace::Linear => "linear",
BlendSpace::Srgb => "srgb",
}
}
}
pub struct PersistentOutputState {
pub transform: Cell<Transform>,
pub scale: Cell<crate::scale::Scale>,
@ -123,6 +139,7 @@ pub struct PersistentOutputState {
pub vrr_cursor_hz: Cell<Option<f64>>,
pub tearing_mode: Cell<&'static TearingMode>,
pub brightness: Cell<Option<f64>>,
pub blend_space: Cell<BlendSpace>,
}
impl Default for PersistentOutputState {
@ -135,6 +152,7 @@ impl Default for PersistentOutputState {
vrr_cursor_hz: Default::default(),
tearing_mode: Cell::new(&TearingMode::Never),
brightness: Default::default(),
blend_space: Cell::new(BlendSpace::Srgb),
}
}
}

View file

@ -9,7 +9,7 @@ use {
ifs::{
head_management::{HeadManagers, HeadState},
jay_tray_v1::JayTrayV1Global,
wl_output::{PersistentOutputState, WlOutputGlobal},
wl_output::{BlendSpace, PersistentOutputState, WlOutputGlobal},
},
output_schedule::OutputSchedule,
state::{ConnectorData, OutputData, State},
@ -183,6 +183,7 @@ impl ConnectorHandler {
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),
blend_space: Cell::new(BlendSpace::Srgb),
});
self.state
.persistent_output_states

View file

@ -335,7 +335,7 @@ impl ToolClient {
self_id: s.registry,
name: s.jay_compositor.0,
interface: JayCompositor.name(),
version: s.jay_compositor.1.min(20),
version: s.jay_compositor.1.min(21),
id: id.into(),
});
self.jay_compositor.set(Some(id));

View file

@ -13,7 +13,7 @@ use {
jay_output::JayOutput,
jay_screencast::JayScreencast,
wl_buffer::WlBufferStorage,
wl_output::WlOutputGlobal,
wl_output::{BlendSpace, WlOutputGlobal},
wl_seat::{
BTN_LEFT, NodeSeatState, SeatId, WlSeatGlobal, collect_kb_foci2,
tablet::{TabletTool, TabletToolChanges, TabletToolId},
@ -971,6 +971,12 @@ impl OutputNode {
}
}
pub fn set_blend_space(&self, blend_space: BlendSpace) {
let old = self.global.persistent.blend_space.replace(blend_space);
if old != blend_space {
self.state.damage(self.global.position());
}
}
fn find_stacked_at(
&self,
stack: &LinkedList<Rc<dyn StackedNode>>,