1
0
Fork 0
forked from wry/wry

color-management-v1: initial implementation

This commit is contained in:
Julian Orth 2025-02-26 14:32:57 +01:00
parent 084006d64a
commit c66f5798b7
22 changed files with 1130 additions and 3 deletions

View file

@ -160,6 +160,7 @@ Jay supports the following wayland protocols:
| wl_shm | 2 | | | wl_shm | 2 | |
| wl_subcompositor | 1 | | | wl_subcompositor | 1 | |
| wp_alpha_modifier_v1 | 1 | | | wp_alpha_modifier_v1 | 1 | |
| wp_color_manager_v1 | 1[^color_mng] | |
| wp_commit_timing_manager_v1 | 1 | | | wp_commit_timing_manager_v1 | 1 | |
| wp_content_type_manager_v1 | 1 | | | wp_content_type_manager_v1 | 1 | |
| wp_cursor_shape_manager_v1 | 1 | | | wp_cursor_shape_manager_v1 | 1 | |
@ -195,3 +196,4 @@ Jay supports the following wayland protocols:
[^lsaccess]: Sandboxes can restrict access to this protocol. [^lsaccess]: Sandboxes can restrict access to this protocol.
[^ts_rejected]: Seat creation is always rejected. [^ts_rejected]: Seat creation is always rejected.
[^composited]: Cursors are always composited. [^composited]: Cursors are always composited.
[^color_mng]: Only SRGB is supported.

View file

@ -9,6 +9,7 @@
opacity on top of a red window will produce a perfectly yellow image instead of a muddy opacity on top of a red window will produce a perfectly yellow image instead of a muddy
yellow. The blend buffer is only used for those areas of the screen where blending is yellow. The blend buffer is only used for those areas of the screen where blending is
observable. This should have no impact on performance in the common case. observable. This should have no impact on performance in the common case.
- Implement color-management-v1.
# 1.9.1 (2025-02-13) # 1.9.1 (2025-02-13)

View file

@ -2,6 +2,7 @@ use {
crate::{ crate::{
client::{Client, ClientError}, client::{Client, ClientError},
ifs::{ ifs::{
color_management::wp_image_description_v1::WpImageDescriptionV1,
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1, ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
ext_image_capture_source_v1::ExtImageCaptureSourceV1, ext_image_capture_source_v1::ExtImageCaptureSourceV1,
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1, ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
@ -43,9 +44,9 @@ use {
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayOutputId, ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayOutputId,
JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id, WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId, XdgSurfaceId, WpImageDescriptionV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId,
XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id, XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
ZwpTabletToolV2Id, ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
}, },
}, },
std::{cell::RefCell, rc::Rc}, std::{cell::RefCell, rc::Rc},
@ -85,6 +86,7 @@ pub struct Objects {
pub ext_data_sources: CopyHashMap<ExtDataControlSourceV1Id, Rc<ExtDataControlSourceV1>>, pub ext_data_sources: CopyHashMap<ExtDataControlSourceV1Id, Rc<ExtDataControlSourceV1>>,
pub ext_workspace_groups: pub ext_workspace_groups:
CopyHashMap<ExtWorkspaceGroupHandleV1Id, Rc<ExtWorkspaceGroupHandleV1>>, CopyHashMap<ExtWorkspaceGroupHandleV1Id, Rc<ExtWorkspaceGroupHandleV1>>,
pub wp_image_description: CopyHashMap<WpImageDescriptionV1Id, Rc<WpImageDescriptionV1>>,
ids: RefCell<Vec<usize>>, ids: RefCell<Vec<usize>>,
} }
@ -123,6 +125,7 @@ impl Objects {
ext_copy_sessions: Default::default(), ext_copy_sessions: Default::default(),
ext_data_sources: Default::default(), ext_data_sources: Default::default(),
ext_workspace_groups: Default::default(), ext_workspace_groups: Default::default(),
wp_image_description: Default::default(),
ids: RefCell::new(vec![]), ids: RefCell::new(vec![]),
} }
} }

View file

@ -3,6 +3,7 @@ use {
backend::Backend, backend::Backend,
client::{Client, ClientCaps}, client::{Client, ClientCaps},
ifs::{ ifs::{
color_management::wp_color_manager_v1::WpColorManagerV1Global,
ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1Global, ext_foreign_toplevel_image_capture_source_manager_v1::ExtForeignToplevelImageCaptureSourceManagerV1Global,
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global, ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1Global,
ext_idle_notifier_v1::ExtIdleNotifierV1Global, ext_idle_notifier_v1::ExtIdleNotifierV1Global,
@ -215,6 +216,7 @@ impl Globals {
add_singleton!(ExtDataControlManagerV1Global); add_singleton!(ExtDataControlManagerV1Global);
add_singleton!(WlFixesGlobal); add_singleton!(WlFixesGlobal);
add_singleton!(ExtWorkspaceManagerV1Global); add_singleton!(ExtWorkspaceManagerV1Global);
add_singleton!(WpColorManagerV1Global);
} }
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) { pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {

View file

@ -1,3 +1,4 @@
pub mod color_management;
pub mod ext_foreign_toplevel_handle_v1; pub mod ext_foreign_toplevel_handle_v1;
pub mod ext_foreign_toplevel_image_capture_source_manager_v1; pub mod ext_foreign_toplevel_image_capture_source_manager_v1;
pub mod ext_foreign_toplevel_list_v1; pub mod ext_foreign_toplevel_list_v1;

View file

@ -0,0 +1,56 @@
pub mod wp_color_management_output_v1;
pub mod wp_color_management_surface_feedback_v1;
pub mod wp_color_management_surface_v1;
pub mod wp_color_manager_v1;
pub mod wp_image_description_creator_icc_v1;
pub mod wp_image_description_creator_params_v1;
pub mod wp_image_description_info_v1;
pub mod wp_image_description_v1;
#[expect(dead_code)]
mod consts {
pub(super) const RENDER_INTENT_PERCEPTUAL: u32 = 0;
pub(super) const RENDER_INTENT_RELATIVE: u32 = 1;
pub(super) const RENDER_INTENT_SATURATION: u32 = 2;
pub(super) const RENDER_INTENT_ABSOLUTE: u32 = 3;
pub(super) const RENDER_INTENT_RELATIVE_BPC: u32 = 4;
pub(super) const FEATURE_ICC_V2_V4: u32 = 0;
pub(super) const FEATURE_PARAMETRIC: u32 = 1;
pub(super) const FEATURE_SET_PRIMARIES: u32 = 2;
pub(super) const FEATURE_SET_TF_POWER: u32 = 3;
pub(super) const FEATURE_SET_LUMINANCES: u32 = 4;
pub(super) const FEATURE_SET_MASTERING_DISPLAY_PRIMARIES: u32 = 5;
pub(super) const FEATURE_EXTENDED_TARGET_VOLUME: u32 = 6;
pub(super) const FEATURE_WINDOWS_SCRGB: u32 = 7;
pub(super) const PRIMARIES_SRGB: u32 = 1;
pub(super) const PRIMARIES_PAL_M: u32 = 2;
pub(super) const PRIMARIES_PAL: u32 = 3;
pub(super) const PRIMARIES_NTSC: u32 = 4;
pub(super) const PRIMARIES_GENERIC_FILM: u32 = 5;
pub(super) const PRIMARIES_BT2020: u32 = 6;
pub(super) const PRIMARIES_CIE1931_XYZ: u32 = 7;
pub(super) const PRIMARIES_DCI_P3: u32 = 8;
pub(super) const PRIMARIES_DISPLAY_P3: u32 = 9;
pub(super) const PRIMARIES_ADOBE_RGB: u32 = 10;
pub(super) const TRANSFER_FUNCTION_BT1886: u32 = 1;
pub(super) const TRANSFER_FUNCTION_GAMMA22: u32 = 2;
pub(super) const TRANSFER_FUNCTION_GAMMA28: u32 = 3;
pub(super) const TRANSFER_FUNCTION_ST240: u32 = 4;
pub(super) const TRANSFER_FUNCTION_EXT_LINEAR: u32 = 5;
pub(super) const TRANSFER_FUNCTION_LOG_100: u32 = 6;
pub(super) const TRANSFER_FUNCTION_LOG_316: u32 = 7;
pub(super) const TRANSFER_FUNCTION_XVYCC: u32 = 8;
pub(super) const TRANSFER_FUNCTION_SRGB: u32 = 9;
pub(super) const TRANSFER_FUNCTION_EXT_SRGB: u32 = 10;
pub(super) const TRANSFER_FUNCTION_ST2084_PQ: u32 = 11;
pub(super) const TRANSFER_FUNCTION_ST428: u32 = 12;
pub(super) const TRANSFER_FUNCTION_HLG: u32 = 13;
pub(super) const CAUSE_LOW_VERSION: u32 = 0;
pub(super) const CAUSE_UNSUPPORTED: u32 = 1;
pub(super) const CAUSE_OPERATING_SYSTEM: u32 = 2;
pub(super) const CAUSE_NO_OUTPUT: u32 = 3;
}

View file

@ -0,0 +1,68 @@
use {
crate::{
client::{Client, ClientError},
ifs::color_management::wp_image_description_v1::WpImageDescriptionV1,
leaks::Tracker,
object::{Object, Version},
wire::{WpColorManagementOutputV1Id, wp_color_management_output_v1::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpColorManagementOutputV1 {
pub id: WpColorManagementOutputV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpColorManagementOutputV1 {
#[expect(dead_code)]
pub fn send_image_description_changed(&self) {
self.client
.event(ImageDescriptionChanged { self_id: self.id });
}
}
impl WpColorManagementOutputV1RequestHandler for WpColorManagementOutputV1 {
type Error = WpColorManagementOutputV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_image_description(
&self,
req: GetImageDescription,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let obj = Rc::new(WpImageDescriptionV1 {
id: req.image_description,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
obj.send_ready(0);
Ok(())
}
}
object_base! {
self = WpColorManagementOutputV1;
version = self.version;
}
impl Object for WpColorManagementOutputV1 {}
simple_add_obj!(WpColorManagementOutputV1);
#[derive(Debug, Error)]
pub enum WpColorManagementOutputV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(WpColorManagementOutputV1Error, ClientError);

View file

@ -0,0 +1,76 @@
use {
crate::{
client::{Client, ClientError},
ifs::color_management::wp_image_description_v1::WpImageDescriptionV1,
leaks::Tracker,
object::{Object, Version},
wire::{
WpColorManagementSurfaceFeedbackV1Id, WpImageDescriptionV1Id,
wp_color_management_surface_feedback_v1::*,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpColorManagementSurfaceFeedbackV1 {
pub id: WpColorManagementSurfaceFeedbackV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpColorManagementSurfaceFeedbackV1 {
fn get_description(
&self,
id: WpImageDescriptionV1Id,
) -> Result<(), WpColorManagementSurfaceFeedbackV1Error> {
let obj = Rc::new(WpImageDescriptionV1 {
id,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
obj.send_ready(0);
Ok(())
}
}
impl WpColorManagementSurfaceFeedbackV1RequestHandler for WpColorManagementSurfaceFeedbackV1 {
type Error = WpColorManagementSurfaceFeedbackV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_preferred(&self, req: GetPreferred, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.get_description(req.image_description)
}
fn get_preferred_parametric(
&self,
req: GetPreferredParametric,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.get_description(req.image_description)
}
}
object_base! {
self = WpColorManagementSurfaceFeedbackV1;
version = self.version;
}
impl Object for WpColorManagementSurfaceFeedbackV1 {}
simple_add_obj!(WpColorManagementSurfaceFeedbackV1);
#[derive(Debug, Error)]
pub enum WpColorManagementSurfaceFeedbackV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(WpColorManagementSurfaceFeedbackV1Error, ClientError);

View file

@ -0,0 +1,73 @@
use {
crate::{
client::{Client, ClientError},
ifs::color_management::consts::RENDER_INTENT_PERCEPTUAL,
leaks::Tracker,
object::{Object, Version},
wire::{
WpColorManagementSurfaceV1Id,
wp_color_management_surface_v1::{
Destroy, SetImageDescription, UnsetImageDescription,
WpColorManagementSurfaceV1RequestHandler,
},
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpColorManagementSurfaceV1 {
pub id: WpColorManagementSurfaceV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpColorManagementSurfaceV1RequestHandler for WpColorManagementSurfaceV1 {
type Error = WpColorManagementSurfaceV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn set_image_description(
&self,
req: SetImageDescription,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let _ = self.client.lookup(req.image_description)?;
if req.render_intent != RENDER_INTENT_PERCEPTUAL {
return Err(WpColorManagementSurfaceV1Error::UnsupportedRenderIntent(
req.render_intent,
));
}
Ok(())
}
fn unset_image_description(
&self,
_req: UnsetImageDescription,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Ok(())
}
}
object_base! {
self = WpColorManagementSurfaceV1;
version = self.version;
}
impl Object for WpColorManagementSurfaceV1 {}
simple_add_obj!(WpColorManagementSurfaceV1);
#[derive(Debug, Error)]
pub enum WpColorManagementSurfaceV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("{} is not a supported render intent", .0)]
UnsupportedRenderIntent(u32),
}
efrom!(WpColorManagementSurfaceV1Error, ClientError);

View file

@ -0,0 +1,223 @@
use {
crate::{
client::{Client, ClientError},
globals::{Global, GlobalName},
ifs::color_management::{
consts::{
FEATURE_PARAMETRIC, PRIMARIES_SRGB, RENDER_INTENT_PERCEPTUAL,
TRANSFER_FUNCTION_SRGB,
},
wp_color_management_output_v1::WpColorManagementOutputV1,
wp_color_management_surface_feedback_v1::WpColorManagementSurfaceFeedbackV1,
wp_color_management_surface_v1::WpColorManagementSurfaceV1,
wp_image_description_creator_params_v1::WpImageDescriptionCreatorParamsV1,
},
leaks::Tracker,
object::{Object, Version},
wire::{
WpColorManagerV1Id,
wp_color_manager_v1::{SupportedIntent, *},
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpColorManagerV1Global {
pub name: GlobalName,
}
impl WpColorManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: WpColorManagerV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), WpColorManagerV1Error> {
let obj = Rc::new(WpColorManagerV1 {
id,
client: client.clone(),
tracker: Default::default(),
version,
});
track!(client, obj);
client.add_client_obj(&obj)?;
obj.send_capabilities();
Ok(())
}
}
pub struct WpColorManagerV1 {
pub id: WpColorManagerV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpColorManagerV1 {
fn send_capabilities(&self) {
self.send_supported_intent(RENDER_INTENT_PERCEPTUAL);
self.send_supported_feature(FEATURE_PARAMETRIC);
self.send_supported_tf_named(TRANSFER_FUNCTION_SRGB);
self.send_supported_primaries_named(PRIMARIES_SRGB);
self.send_done();
}
fn send_supported_intent(&self, render_intent: u32) {
self.client.event(SupportedIntent {
self_id: self.id,
render_intent,
});
}
fn send_supported_feature(&self, feature: u32) {
self.client.event(SupportedFeature {
self_id: self.id,
feature,
});
}
fn send_supported_tf_named(&self, tf: u32) {
self.client.event(SupportedTfNamed {
self_id: self.id,
tf,
});
}
fn send_supported_primaries_named(&self, primaries: u32) {
self.client.event(SupportedPrimariesNamed {
self_id: self.id,
primaries,
});
}
fn send_done(&self) {
self.client.event(Done { self_id: self.id });
}
}
impl WpColorManagerV1RequestHandler for WpColorManagerV1 {
type Error = WpColorManagerV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_output(&self, req: GetOutput, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let _ = self.client.lookup(req.output)?;
let obj = Rc::new(WpColorManagementOutputV1 {
id: req.id,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
Ok(())
}
fn get_surface(&self, req: GetSurface, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let _ = self.client.lookup(req.surface)?;
let obj = Rc::new(WpColorManagementSurfaceV1 {
id: req.id,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
Ok(())
}
fn get_surface_feedback(
&self,
req: GetSurfaceFeedback,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let _ = self.client.lookup(req.surface)?;
let obj = Rc::new(WpColorManagementSurfaceFeedbackV1 {
id: req.id,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
Ok(())
}
fn create_icc_creator(
&self,
_req: CreateIccCreator,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Err(WpColorManagerV1Error::CreateIccCreatorNotSupported)
}
fn create_parametric_creator(
&self,
req: CreateParametricCreator,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let obj = Rc::new(WpImageDescriptionCreatorParamsV1 {
id: req.obj,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
Ok(())
}
fn create_windows_scrgb(
&self,
_req: CreateWindowsScrgb,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Err(WpColorManagerV1Error::CreateWindowsScrgbNotSupported)
}
}
global_base!(
WpColorManagerV1Global,
WpColorManagerV1,
WpColorManagerV1Error
);
impl Global for WpColorManagerV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
}
simple_add_global!(WpColorManagerV1Global);
object_base! {
self = WpColorManagerV1;
version = self.version;
}
impl Object for WpColorManagerV1 {}
simple_add_obj!(WpColorManagerV1);
#[derive(Debug, Error)]
pub enum WpColorManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("create_icc_creator is not supported")]
CreateIccCreatorNotSupported,
#[error("create_windows_scrgb is not supported")]
CreateWindowsScrgbNotSupported,
}
efrom!(WpColorManagerV1Error, ClientError);

View file

@ -0,0 +1,43 @@
use {
crate::{
client::Client,
leaks::Tracker,
object::{Object, Version},
wire::{
WpImageDescriptionCreatorIccV1Id,
wp_image_description_creator_icc_v1::{
Create, SetIccFile, WpImageDescriptionCreatorIccV1RequestHandler,
},
},
},
std::{convert::Infallible, rc::Rc},
};
#[expect(dead_code)]
pub struct WpImageDescriptionCreatorIccV1 {
pub id: WpImageDescriptionCreatorIccV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpImageDescriptionCreatorIccV1RequestHandler for WpImageDescriptionCreatorIccV1 {
type Error = Infallible;
fn create(&self, _req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
unreachable!()
}
fn set_icc_file(&self, _req: SetIccFile, _slf: &Rc<Self>) -> Result<(), Self::Error> {
unreachable!()
}
}
object_base! {
self = WpImageDescriptionCreatorIccV1;
version = self.version;
}
impl Object for WpImageDescriptionCreatorIccV1 {}
simple_add_obj!(WpImageDescriptionCreatorIccV1);

View file

@ -0,0 +1,134 @@
use {
crate::{
client::{Client, ClientError},
ifs::color_management::{
consts::{PRIMARIES_SRGB, TRANSFER_FUNCTION_SRGB},
wp_image_description_v1::WpImageDescriptionV1,
},
leaks::Tracker,
object::{Object, Version},
wire::{
WpImageDescriptionCreatorParamsV1Id,
wp_image_description_creator_params_v1::{
Create, SetLuminances, SetMasteringDisplayPrimaries, SetMasteringLuminance,
SetMaxCll, SetMaxFall, SetPrimaries, SetPrimariesNamed, SetTfNamed, SetTfPower,
WpImageDescriptionCreatorParamsV1RequestHandler,
},
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpImageDescriptionCreatorParamsV1 {
pub id: WpImageDescriptionCreatorParamsV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpImageDescriptionCreatorParamsV1RequestHandler for WpImageDescriptionCreatorParamsV1 {
type Error = WpImageDescriptionCreatorParamsV1Error;
fn create(&self, req: Create, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let obj = Rc::new(WpImageDescriptionV1 {
id: req.image_description,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
track!(self.client, obj);
self.client.add_client_obj(&obj)?;
obj.send_ready(0);
self.client.remove_obj(self)?;
Ok(())
}
fn set_tf_named(&self, req: SetTfNamed, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if req.tf != TRANSFER_FUNCTION_SRGB {
return Err(WpImageDescriptionCreatorParamsV1Error::UnsupportedTf(
req.tf,
));
}
Ok(())
}
fn set_tf_power(&self, _req: SetTfPower, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Err(WpImageDescriptionCreatorParamsV1Error::SetTfPowerNotSupported)
}
fn set_primaries_named(
&self,
req: SetPrimariesNamed,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
if req.primaries != PRIMARIES_SRGB {
return Err(
WpImageDescriptionCreatorParamsV1Error::UnsupportedPrimaries(req.primaries),
);
}
Ok(())
}
fn set_primaries(&self, _req: SetPrimaries, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Err(WpImageDescriptionCreatorParamsV1Error::SetPrimariesNotSupported)
}
fn set_luminances(&self, _req: SetLuminances, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Err(WpImageDescriptionCreatorParamsV1Error::SetLuminancesNotSupported)
}
fn set_mastering_display_primaries(
&self,
_req: SetMasteringDisplayPrimaries,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Err(WpImageDescriptionCreatorParamsV1Error::SetMasteringDisplayPrimariesNotSupported)
}
fn set_mastering_luminance(
&self,
_req: SetMasteringLuminance,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Err(WpImageDescriptionCreatorParamsV1Error::SetMasteringLuminanceNotSupported)
}
fn set_max_cll(&self, _req: SetMaxCll, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Ok(())
}
fn set_max_fall(&self, _req: SetMaxFall, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Ok(())
}
}
object_base! {
self = WpImageDescriptionCreatorParamsV1;
version = self.version;
}
impl Object for WpImageDescriptionCreatorParamsV1 {}
simple_add_obj!(WpImageDescriptionCreatorParamsV1);
#[derive(Debug, Error)]
pub enum WpImageDescriptionCreatorParamsV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error("set_mastering_luminance is not supported")]
SetMasteringLuminanceNotSupported,
#[error("set_mastering_display_primaries is not supported")]
SetMasteringDisplayPrimariesNotSupported,
#[error("set_luminances is not supported")]
SetLuminancesNotSupported,
#[error("set_primaries is not supported")]
SetPrimariesNotSupported,
#[error("{} is not a supported named primary", .0)]
UnsupportedPrimaries(u32),
#[error("set_tf_power is not supported")]
SetTfPowerNotSupported,
#[error("{} is not a supported named transfer function", .0)]
UnsupportedTf(u32),
}
efrom!(WpImageDescriptionCreatorParamsV1Error, ClientError);

View file

@ -0,0 +1,145 @@
use {
crate::{
client::Client,
ifs::color_management::consts::{PRIMARIES_SRGB, TRANSFER_FUNCTION_SRGB},
leaks::Tracker,
object::{Object, Version},
wire::{WpImageDescriptionInfoV1Id, wp_image_description_info_v1::*},
},
std::{convert::Infallible, rc::Rc},
uapi::OwnedFd,
};
pub struct WpImageDescriptionInfoV1 {
pub id: WpImageDescriptionInfoV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpImageDescriptionInfoV1 {
pub fn send_srgb(&self) {
let red = [0.64, 0.33];
let green = [0.3, 0.6];
let blue = [0.15, 0.06];
let white = [0.3127, 0.3290];
self.send_primaries(red, green, blue, white);
self.send_primaries_named(PRIMARIES_SRGB);
self.send_tf_named(TRANSFER_FUNCTION_SRGB);
self.send_luminances(0.2, 80.0, 80.0);
self.send_target_primaries(red, green, blue, white);
self.send_target_luminances(0.2, 80.0);
self.send_done();
}
pub fn send_done(&self) {
self.client.event(Done { self_id: self.id });
}
#[expect(dead_code)]
pub fn send_ic_file(&self, file: &Rc<OwnedFd>, size: usize) {
self.client.event(IccFile {
self_id: self.id,
icc: file.clone(),
icc_size: size as _,
});
}
pub fn send_primaries(&self, r: [f64; 2], g: [f64; 2], b: [f64; 2], w: [f64; 2]) {
let map = |c: f64| (c * 1_000_000.0) as i32;
self.client.event(Primaries {
self_id: self.id,
r_x: map(r[0]),
r_y: map(r[1]),
g_x: map(g[0]),
g_y: map(g[1]),
b_x: map(b[0]),
b_y: map(b[1]),
w_x: map(w[0]),
w_y: map(w[1]),
});
}
pub fn send_primaries_named(&self, primaries: u32) {
self.client.event(PrimariesNamed {
self_id: self.id,
primaries,
});
}
#[expect(dead_code)]
pub fn send_tf_power(&self, eexp: f64) {
self.client.event(TfPower {
self_id: self.id,
eexp: (eexp * 10_000.0) as u32,
});
}
pub fn send_tf_named(&self, tf: u32) {
self.client.event(TfNamed {
self_id: self.id,
tf,
});
}
pub fn send_luminances(&self, min_lum: f64, max_lum: f64, reference_lum: f64) {
self.client.event(Luminances {
self_id: self.id,
min_lum: (min_lum * 10_000.0) as u32,
max_lum: max_lum as _,
reference_lum: reference_lum as _,
});
}
pub fn send_target_primaries(&self, r: [f64; 2], g: [f64; 2], b: [f64; 2], w: [f64; 2]) {
let map = |c: f64| (c * 1_000_000.0) as i32;
self.client.event(TargetPrimaries {
self_id: self.id,
r_x: map(r[0]),
r_y: map(r[1]),
g_x: map(g[0]),
g_y: map(g[1]),
b_x: map(b[0]),
b_y: map(b[1]),
w_x: map(w[0]),
w_y: map(w[1]),
});
}
pub fn send_target_luminances(&self, min_lum: f64, max_lum: f64) {
self.client.event(TargetLuminance {
self_id: self.id,
min_lum: (min_lum * 10_000.0) as u32,
max_lum: max_lum as _,
});
}
#[expect(dead_code)]
pub fn send_target_max_cll(&self, max_cll: f64) {
self.client.event(TargetMaxCll {
self_id: self.id,
max_cll: max_cll as _,
});
}
#[expect(dead_code)]
pub fn send_target_max_fall(&self, max_fall: f64) {
self.client.event(TargetMaxFall {
self_id: self.id,
max_fall: max_fall as _,
});
}
}
impl WpImageDescriptionInfoV1RequestHandler for WpImageDescriptionInfoV1 {
type Error = Infallible;
}
object_base! {
self = WpImageDescriptionInfoV1;
version = self.version;
}
impl Object for WpImageDescriptionInfoV1 {}
simple_add_obj!(WpImageDescriptionInfoV1);

View file

@ -0,0 +1,79 @@
use {
crate::{
client::{Client, ClientError},
ifs::color_management::wp_image_description_info_v1::WpImageDescriptionInfoV1,
leaks::Tracker,
object::{Object, Version},
wire::{WpImageDescriptionV1Id, wp_image_description_v1::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct WpImageDescriptionV1 {
pub id: WpImageDescriptionV1Id,
pub client: Rc<Client>,
pub version: Version,
pub tracker: Tracker<Self>,
}
impl WpImageDescriptionV1 {
#[expect(dead_code)]
pub fn send_failed(&self, cause: u32, msg: &str) {
self.client.event(Failed {
self_id: self.id,
cause,
msg,
});
}
pub fn send_ready(&self, identity: u32) {
self.client.event(Ready {
self_id: self.id,
identity,
});
}
}
impl WpImageDescriptionV1RequestHandler for WpImageDescriptionV1 {
type Error = WpImageDescriptionV1Error;
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
fn get_information(&self, req: GetInformation, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let obj = Rc::new(WpImageDescriptionInfoV1 {
id: req.information,
client: self.client.clone(),
version: self.version,
tracker: Default::default(),
});
self.client.add_client_obj(&obj)?;
track!(self.client, obj);
obj.send_srgb();
self.client.remove_obj(&*obj)?;
Ok(())
}
}
object_base! {
self = WpImageDescriptionV1;
version = self.version;
}
impl Object for WpImageDescriptionV1 {}
dedicated_add_obj!(
WpImageDescriptionV1,
WpImageDescriptionV1Id,
wp_image_description
);
#[derive(Debug, Error)]
pub enum WpImageDescriptionV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(WpImageDescriptionV1Error, ClientError);

View file

@ -0,0 +1,9 @@
request destroy {
}
event image_description_changed {
}
request get_image_description {
image_description: id(wp_image_description_v1),
}

View file

@ -0,0 +1,14 @@
request destroy {
}
event preferred_changed {
identity: u32,
}
request get_preferred {
image_description: id(wp_image_description_v1),
}
request get_preferred_parametric {
image_description: id(wp_image_description_v1),
}

View file

@ -0,0 +1,10 @@
request destroy {
}
request set_image_description {
image_description: id(wp_image_description_v1),
render_intent: u32,
}
request unset_image_description {
}

View file

@ -0,0 +1,48 @@
request destroy {
}
request get_output {
id: id(wp_color_management_output_v1),
output: id(wl_output),
}
request get_surface {
id: id(wp_color_management_surface_v1),
surface: id(wl_surface),
}
request get_surface_feedback {
id: id(wp_color_management_surface_feedback_v1),
surface: id(wl_surface),
}
request create_icc_creator {
obj: id(wp_image_description_creator_icc_v1),
}
request create_parametric_creator {
obj: id(wp_image_description_creator_params_v1),
}
request create_windows_scrgb {
image_description: id(wp_image_description_v1),
}
event supported_intent {
render_intent: u32,
}
event supported_feature {
feature: u32,
}
event supported_tf_named {
tf: u32,
}
event supported_primaries_named {
primaries: u32,
}
event done {
}

View file

@ -0,0 +1,9 @@
request create {
image_description: id(wp_image_description_v1),
}
request set_icc_file {
icc_profile: fd,
offset: u32,
length: u32,
}

View file

@ -0,0 +1,56 @@
request create {
image_description: id(wp_image_description_v1),
}
request set_tf_named {
tf: u32,
}
request set_tf_power {
eexp: u32,
}
request set_primaries_named {
primaries: u32,
}
request set_primaries {
r_x: i32,
r_y: i32,
g_x: i32,
g_y: i32,
b_x: i32,
b_y: i32,
w_x: i32,
w_y: i32,
}
request set_luminances {
min_lum: u32,
max_lum: u32,
reference_lum: u32,
}
request set_mastering_display_primaries {
r_x: i32,
r_y: i32,
g_x: i32,
g_y: i32,
b_x: i32,
b_y: i32,
w_x: i32,
w_y: i32,
}
request set_mastering_luminance {
min_lum: u32,
max_lum: u32,
}
request set_max_cll {
max_cll: u32,
}
request set_max_fall {
max_fall: u32,
}

View file

@ -0,0 +1,60 @@
event done {
}
event icc_file {
icc: fd,
icc_size: u32,
}
event primaries {
r_x: i32,
r_y: i32,
g_x: i32,
g_y: i32,
b_x: i32,
b_y: i32,
w_x: i32,
w_y: i32,
}
event primaries_named {
primaries: u32,
}
event tf_power {
eexp: u32,
}
event tf_named {
tf: u32,
}
event luminances {
min_lum: u32,
max_lum: u32,
reference_lum: u32,
}
event target_primaries {
r_x: i32,
r_y: i32,
g_x: i32,
g_y: i32,
b_x: i32,
b_y: i32,
w_x: i32,
w_y: i32,
}
event target_luminance {
min_lum: u32,
max_lum: u32,
}
event target_max_cll {
max_cll: u32,
}
event target_max_fall {
max_fall: u32,
}

View file

@ -0,0 +1,15 @@
request destroy {
}
event failed {
cause: u32,
msg: str,
}
event ready {
identity: u32,
}
request get_information {
information: id(wp_image_description_info_v1),
}