1
0
Fork 0
forked from wry/wry

implement wlr-gamma-control-unstable-v1

This commit is contained in:
khyperia 2026-01-24 20:26:51 +01:00 committed by Julian Orth
parent 11b3f08514
commit b1db715a90
26 changed files with 475 additions and 21 deletions

View file

@ -196,6 +196,7 @@ Jay supports the following wayland protocols:
| xdg_wm_dialog_v1 | 1 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_foreign_toplevel_manager_v1 | 3 | Yes |
| zwlr_gamma_control_manager_v1 | 1 | Yes |
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
| zwlr_output_manager_v1 | 4 | Yes |
| zwlr_screencopy_manager_v1 | 3 | Yes |

View file

@ -225,5 +225,7 @@ bitflags! {
/// Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
/// globals.
pub const CC_HEAD_MANAGER = 1 << 13,
/// Grants access to the `zwlr_gamma_control_manager_v1` global.
pub const CC_GAMMA_CONTROL_MANAGER = 1 << 14,
}
}

View file

@ -37,7 +37,7 @@ use {
hash::Hash,
rc::Rc,
},
uapi::{OwnedFd, c},
uapi::{OwnedFd, Packed, Pod, c},
};
pub mod transaction;
@ -165,6 +165,9 @@ pub trait Connector: Any {
self.kernel_id(),
))
}
fn gamma_lut_size(&self) -> Option<u32> {
None
}
}
#[derive(Debug)]
@ -611,6 +614,42 @@ impl BackendColorSpace {
}
}
// kernel: struct drm_color_lut
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Hash)]
#[repr(C)]
pub struct BackendGammaLutElement {
pub red: u16,
pub green: u16,
pub blue: u16,
pub reserved: u16,
}
unsafe impl Pod for BackendGammaLutElement {}
unsafe impl Packed for BackendGammaLutElement {}
#[derive(Debug, Eq)]
pub struct BackendGammaLut {
id: [u8; 32],
pub gamma_lut: Vec<BackendGammaLutElement>,
}
impl BackendGammaLut {
pub fn new(mut gamma_lut: Vec<BackendGammaLutElement>) -> Self {
for element in &mut gamma_lut {
element.reserved = 0;
}
let gamma_lut_bytes = uapi::as_bytes(&gamma_lut as &[_]);
let id = *blake3::hash(gamma_lut_bytes).as_bytes();
Self { id, gamma_lut }
}
}
impl PartialEq for BackendGammaLut {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
linear_ids!(
BackendConnectorStateSerials,
BackendConnectorStateSerial,
@ -629,4 +668,5 @@ pub struct BackendConnectorState {
pub format: &'static Format,
pub color_space: BackendColorSpace,
pub eotf: BackendEotfs,
pub gamma_lut: Option<Rc<BackendGammaLut>>,
}

View file

@ -124,6 +124,8 @@ pub enum BackendConnectorTransactionError {
AtomicTestFailed(#[source] DrmError),
#[error("Commit failed")]
AtomicCommitFailed(#[source] DrmError),
#[error("Could not create a gamma lut blob")]
CreateGammaLutBlob(#[source] DrmError),
}
pub trait BackendConnectorTransaction {

View file

@ -2,7 +2,8 @@ use {
crate::{
allocator::BufferObject,
backend::{
BackendColorSpace, BackendConnectorState, BackendEotfs, Connector, ConnectorEvent,
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendGammaLut, Connector,
ConnectorEvent,
transaction::{
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
BackendConnectorTransactionError, BackendPreparedConnectorTransaction,
@ -57,6 +58,9 @@ pub struct DrmCrtcState {
pub mode_blob: Option<Rc<PropBlob>>,
pub vrr_enabled: bool,
pub assigned_connector: DrmConnector,
pub gamma_lut: Option<Rc<BackendGammaLut>>,
pub gamma_lut_blob_id: DrmBlob,
pub gamma_lut_blob: Option<Rc<PropBlob>>,
}
#[derive(Default, Clone, Debug)]
@ -420,6 +424,22 @@ impl MetalDeviceTransaction {
crtc.new.mode_blob = Some(Rc::new(blob));
mode.clone()
};
if crtc.new.gamma_lut != state.gamma_lut {
if let Some(gamma_lut) = &state.gamma_lut {
let blob = slf
.dev
.dev
.master
.create_blob(&gamma_lut.gamma_lut as &[_])
.map_err(BackendConnectorTransactionError::CreateGammaLutBlob)?;
crtc.new.gamma_lut_blob_id = blob.id();
crtc.new.gamma_lut_blob = Some(Rc::new(blob));
} else {
crtc.new.gamma_lut_blob_id = DrmBlob::NONE;
crtc.new.gamma_lut_blob = None;
}
crtc.new.gamma_lut = state.gamma_lut.clone();
}
for plane_id in [&mut crtc_planes.primary, &mut crtc_planes.cursor] {
if plane_id.is_none() {
continue;
@ -841,6 +861,12 @@ impl MetalDeviceTransactionWithDrmState {
log_change!(o, n, mode_blob_id);
c.change(crtc.obj.mode_id, n.mode_blob_id);
}
if let Some(gamma_lut) = crtc.obj.gamma_lut
&& n.gamma_lut_blob_id != o.gamma_lut_blob_id
{
log_change!(o, n, gamma_lut_blob_id);
c.change(gamma_lut, n.gamma_lut_blob_id);
}
reset_default_properties!(
c,
&*crtc.obj.untyped_properties.borrow(),

View file

@ -3,10 +3,10 @@ use {
async_engine::{Phase, SpawnedFuture},
backend::{
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLease,
BackendDrmLessee, BackendEotfs, BackendEvent, BackendLuminance, CONCAP_CONNECTOR,
CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY, Connector, ConnectorCaps, ConnectorEvent,
ConnectorId, ConnectorKernelId, DrmDeviceId, HardwareCursor, HardwareCursorUpdate,
Mode, MonitorInfo,
BackendDrmLessee, BackendEotfs, BackendEvent, BackendGammaLut, BackendGammaLutElement,
BackendLuminance, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
Connector, ConnectorCaps, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
HardwareCursor, HardwareCursorUpdate, Mode, MonitorInfo,
transaction::{
BackendConnectorTransaction, BackendConnectorTransactionError,
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
@ -905,6 +905,10 @@ impl Connector for MetalConnector {
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
self.create_transaction().map(|v| Box::new(v) as _)
}
fn gamma_lut_size(&self) -> Option<u32> {
self.crtc.get().and_then(|crtc| crtc.gamma_lut_size)
}
}
pub struct MetalCrtc {
@ -925,6 +929,8 @@ pub struct MetalCrtc {
pub mode_id: DrmProperty,
pub vrr_enabled: DrmProperty,
pub out_fence_ptr: DrmProperty,
pub gamma_lut: Option<DrmProperty>,
pub gamma_lut_size: Option<u32>,
pub drm_state: RefCell<DrmCrtcState>,
pub sequence: Cell<u64>,
@ -1327,6 +1333,7 @@ fn create_connector_display_data(
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
}),
});
dev.backend
@ -1499,7 +1506,6 @@ fn create_crtc(
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
("CTM", DefaultValue::Fixed(0)),
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
("GAMMA_LUT", DefaultValue::Fixed(0)),
("OUT_FENCE_PTR", DefaultValue::Fixed(0)),
],
);
@ -1507,6 +1513,14 @@ fn create_crtc(
let mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
let gamma_lut = props
.get("GAMMA_LUT")
.ok()
.map(|v| v.map(|v| DrmBlob(v as u32)));
let mut gamma_lut_size = None;
if gamma_lut.is_some() {
gamma_lut_size = props.get("GAMMA_LUT_SIZE").ok().map(|v| v.value as u32);
}
let mut mode = None;
if mode_id.value.is_some() {
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
@ -1523,6 +1537,9 @@ fn create_crtc(
mode_blob: None,
vrr_enabled: vrr_enabled.value,
assigned_connector: DrmConnector::NONE,
gamma_lut: None,
gamma_lut_blob_id: gamma_lut.map_or(DrmBlob::NONE, |v| v.value),
gamma_lut_blob: None,
};
Ok(MetalCrtc {
id: crtc,
@ -1539,6 +1556,8 @@ fn create_crtc(
mode_id: mode_id.id,
vrr_enabled: vrr_enabled.id,
out_fence_ptr: out_fence_ptr.id,
gamma_lut: gamma_lut.map(|v| v.id),
gamma_lut_size,
sequence: Cell::new(0),
have_queued_sequence: Cell::new(false),
needs_vblank_emulation: Cell::new(false),
@ -2187,6 +2206,25 @@ impl MetalCrtc {
}
}
}
if let Some(gamma_lut) = self.gamma_lut {
let id = DrmBlob(get(props, gamma_lut)? as _);
let old = state.gamma_lut_blob_id;
state.gamma_lut_blob_id = id;
if old != id {
state.gamma_lut = None;
state.gamma_lut_blob = None;
if id.is_some() {
match master.getblob_vec::<BackendGammaLutElement>(id) {
Ok(b) => {
state.gamma_lut = Some(Rc::new(BackendGammaLut::new(b)));
}
Err(e) => {
log::error!("Could not fetch gamma_lut: {}", ErrorFmt(e));
}
}
}
}
}
Ok(())
}
}

View file

@ -493,6 +493,7 @@ impl XBackend {
format: FORMAT,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
};
let output = Rc::new(XOutput {
id: self.state.connector_ids.next(),

View file

@ -65,6 +65,7 @@ bitflags! {
CAP_WORKSPACE = 1 << 11,
CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
CAP_HEAD_MANAGER = 1 << 13,
CAP_GAMMA_CONTROL_MANAGER = 1 << 14,
}
pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);

View file

@ -16,7 +16,7 @@ use {
},
thiserror::Error,
uapi::{
OwnedFd,
OwnedFd, Pod,
c::{self, raise},
ftruncate,
},
@ -30,6 +30,8 @@ pub enum ClientMemError {
Sigbus,
#[error("mmap failed")]
MmapFailed(#[source] crate::utils::oserror::OsError),
#[error("Length was not a multiple of the data element size")]
InvalidLength,
}
pub struct ClientMem {
@ -182,13 +184,17 @@ impl ClientMemOffset {
}
}
pub fn read(&self, dst: &mut Vec<u8>) -> Result<(), ClientMemError> {
pub fn read<T: Pod>(&self, dst: &mut Vec<T>) -> Result<(), ClientMemError> {
if self.data.len().checked_rem(std::mem::size_of::<T>()) != Some(0) {
return Err(ClientMemError::InvalidLength);
}
self.access(|v| {
dst.reserve(v.len());
let (_, unused) = dst.split_at_spare_mut_ext();
let len_elements = v.len() / std::mem::size_of::<T>();
dst.reserve(len_elements);
let (_, unused) = dst.split_at_spare_mut_bytes_ext();
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
unsafe {
dst.set_len(dst.len() + v.len());
dst.set_len(dst.len() + len_elements);
}
})
}

View file

@ -658,6 +658,7 @@ fn create_dummy_output(state: &Rc<State>) {
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
};
let id = state.connector_ids.next();
let connector = Rc::new(DummyOutput { id }) as Rc<dyn Connector>;
@ -771,6 +772,7 @@ fn create_dummy_output(state: &Rc<State>) {
ext_workspace_groups: Default::default(),
pinned: Default::default(),
tearing: Default::default(),
active_zwlr_gamma_control: Default::default(),
});
let dummy_workspace = WorkspaceNode::new(&dummy_output, "dummy", true);
dummy_workspace.may_capture.set(false);

View file

@ -63,6 +63,7 @@ use {
xdg_wm_base::XdgWmBaseGlobal,
xdg_wm_dialog_v1::XdgWmDialogV1Global,
zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1Global,
zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1Global,
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global,
@ -233,6 +234,7 @@ impl Globals {
add_singleton!(JayHeadManagerV1Global);
add_singleton!(WpPointerWarpV1Global);
add_singleton!(JayPopupExtManagerV1Global);
add_singleton!(ZwlrGammaControlManagerV1Global);
}
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {

View file

@ -86,6 +86,8 @@ pub mod xdg_wm_base;
pub mod xdg_wm_dialog_v1;
pub mod zwlr_foreign_toplevel_handle_v1;
pub mod zwlr_foreign_toplevel_manager_v1;
pub mod zwlr_gamma_control_manager_v1;
pub mod zwlr_gamma_control_v1;
pub mod zwlr_layer_shell_v1;
pub mod zwlr_screencopy_frame_v1;
pub mod zwlr_screencopy_manager_v1;

View file

@ -0,0 +1,115 @@
use {
crate::{
client::{CAP_GAMMA_CONTROL_MANAGER, Client, ClientCaps, ClientError},
globals::{Global, GlobalName},
ifs::zwlr_gamma_control_v1::*,
leaks::Tracker,
object::{Object, Version},
wire::{ZwlrGammaControlManagerV1Id, zwlr_gamma_control_manager_v1::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwlrGammaControlManagerV1Global {
name: GlobalName,
}
impl ZwlrGammaControlManagerV1Global {
pub fn new(name: GlobalName) -> Self {
Self { name }
}
fn bind_(
self: Rc<Self>,
id: ZwlrGammaControlManagerV1Id,
client: &Rc<Client>,
version: Version,
) -> Result<(), ZwlrGammaControlManagerV1Error> {
let obj = Rc::new(ZwlrGammaControlManagerV1 {
id,
client: client.clone(),
tracker: Default::default(),
version,
});
track!(client, obj);
client.add_client_obj(&obj)?;
Ok(())
}
}
global_base!(
ZwlrGammaControlManagerV1Global,
ZwlrGammaControlManagerV1,
ZwlrGammaControlManagerV1Error
);
simple_add_global!(ZwlrGammaControlManagerV1Global);
impl Global for ZwlrGammaControlManagerV1Global {
fn singleton(&self) -> bool {
true
}
fn version(&self) -> u32 {
1
}
fn required_caps(&self) -> ClientCaps {
CAP_GAMMA_CONTROL_MANAGER
}
}
pub struct ZwlrGammaControlManagerV1 {
pub id: ZwlrGammaControlManagerV1Id,
pub client: Rc<Client>,
pub tracker: Tracker<Self>,
pub version: Version,
}
impl ZwlrGammaControlManagerV1RequestHandler for ZwlrGammaControlManagerV1 {
type Error = ZwlrGammaControlManagerV1Error;
fn get_gamma_control(&self, req: GetGammaControl, slf: &Rc<Self>) -> Result<(), Self::Error> {
let output = self.client.lookup(req.output)?.global.clone();
let p = Rc::new(ZwlrGammaControlV1::new(req.id, slf, output.clone()));
track!(self.client, p);
self.client.add_client_obj(&p)?;
let Some(size) = p.gamma_lut_size() else {
p.send_failed();
return Ok(());
};
let Some(node) = output.node() else {
p.send_failed();
return Ok(());
};
if node.active_zwlr_gamma_control.is_some() {
p.send_failed();
return Ok(());
}
p.send_gamma_size(size);
node.active_zwlr_gamma_control.set(Some(p));
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = ZwlrGammaControlManagerV1;
version = self.version;
}
impl Object for ZwlrGammaControlManagerV1 {}
simple_add_obj!(ZwlrGammaControlManagerV1);
#[derive(Debug, Error)]
pub enum ZwlrGammaControlManagerV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
}
efrom!(ZwlrGammaControlManagerV1Error, ClientError);

View file

@ -0,0 +1,166 @@
use {
crate::{
backend::{BackendGammaLut, BackendGammaLutElement},
client::{Client, ClientError, ClientId},
clientmem::{ClientMem, ClientMemError},
ifs::{
wl_output::OutputGlobalOpt, zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1,
},
leaks::Tracker,
object::{Object, Version},
wire::{ZwlrGammaControlV1Id, zwlr_gamma_control_v1::*},
},
std::rc::Rc,
thiserror::Error,
};
pub struct ZwlrGammaControlV1 {
id: ZwlrGammaControlV1Id,
client: Rc<Client>,
version: Version,
output: Rc<OutputGlobalOpt>,
pub tracker: Tracker<Self>,
}
impl ZwlrGammaControlV1 {
pub fn new(
id: ZwlrGammaControlV1Id,
manager: &Rc<ZwlrGammaControlManagerV1>,
output: Rc<OutputGlobalOpt>,
) -> Self {
Self {
id,
client: manager.client.clone(),
version: manager.version,
output,
tracker: Default::default(),
}
}
pub fn id(&self) -> (ClientId, ZwlrGammaControlV1Id) {
(self.client.id, self.id)
}
pub fn send_gamma_size(&self, size: u32) {
self.client.event(GammaSize {
self_id: self.id,
size,
});
}
pub fn send_failed(&self) {
self.client.event(Failed { self_id: self.id });
}
pub fn gamma_lut_size(&self) -> Option<u32> {
self.output
.node()
.and_then(|node| node.global.connector.connector.gamma_lut_size())
}
fn detach(&self) {
if let Some(node) = self.output.node()
&& let Some(active_zwlr_gamma_control) = node.active_zwlr_gamma_control.get()
&& active_zwlr_gamma_control.id() == self.id()
{
node.active_zwlr_gamma_control.set(None);
let _ = node.set_gamma_lut(None);
}
}
}
// Wayland's LUT is ([red], [green], [blue]). DRM's LUT is [(red, green, blue, _)]. Both are u16.
fn wayland_gamma_lut_to_drm_gamma_lut(data: &[u16]) -> Vec<BackendGammaLutElement> {
let elem_count = data.len() / 3;
let (red, rest) = data.split_at(elem_count);
let (green, blue) = rest.split_at(elem_count);
red.iter()
.copied()
.zip(green.iter().copied())
.zip(blue.iter().copied())
.map(|((red, green), blue)| BackendGammaLutElement {
red,
green,
blue,
reserved: 0,
})
.collect()
}
impl ZwlrGammaControlV1RequestHandler for ZwlrGammaControlV1 {
type Error = ZwlrGammaControlV1Error;
fn set_gamma(&self, req: SetGamma, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let fail = || {
self.detach();
self.send_failed();
};
let Some(node) = self.output.node() else {
return Ok(());
};
// if the active gamma control isn't us, that implies we are not valid, and have already
// sent a failed event
if node.active_zwlr_gamma_control.get().map(|v| v.id()) != Some(self.id()) {
return Ok(());
}
let Some(gamma_lut_size) = self.gamma_lut_size() else {
fail();
return Ok(());
};
// 3 color channels
let data_size = gamma_lut_size * 3;
let mut gamma_lut = vec![];
Rc::new(ClientMem::new_private(
&req.fd,
(2 * data_size) as _,
true,
Some(&self.client),
None,
)?)
.offset(0)
.read(&mut gamma_lut)?;
let gamma_lut = &gamma_lut[..data_size as _];
let gamma_lut = wayland_gamma_lut_to_drm_gamma_lut(gamma_lut);
let gamma_lut = Rc::new(BackendGammaLut::new(gamma_lut));
if node.set_gamma_lut(Some(gamma_lut)).is_err() {
fail();
return Ok(());
}
Ok(())
}
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.detach();
self.client.remove_obj(self)?;
Ok(())
}
}
object_base! {
self = ZwlrGammaControlV1;
version = self.version;
}
impl Object for ZwlrGammaControlV1 {
fn break_loops(&self) {
self.detach();
}
}
simple_add_obj!(ZwlrGammaControlV1);
#[derive(Debug, Error)]
pub enum ZwlrGammaControlV1Error {
#[error(transparent)]
ClientError(Box<ClientError>),
#[error(transparent)]
CLientMemError(#[from] ClientMemError),
}
efrom!(ZwlrGammaControlV1Error, ClientError);

View file

@ -153,6 +153,7 @@ impl TestBackend {
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
},
};
Self {

View file

@ -66,6 +66,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: Default::default(),
},
};
run.backend

View file

@ -46,6 +46,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
format: XRGB8888,
color_space: Default::default(),
eotf: Default::default(),
gamma_lut: None,
};
let id = connector.id();
let name = Rc::new(connector.kernel_id().to_string());
@ -270,6 +271,7 @@ impl ConnectorHandler {
ext_workspace_groups: Default::default(),
pinned: Default::default(),
tearing: Default::default(),
active_zwlr_gamma_control: Default::default(),
});
on.update_visible();
on.update_rects();
@ -421,6 +423,9 @@ impl ConnectorHandler {
}
self.state
.remove_output_scale(on.global.persistent.scale.get());
if let Some(zwlr_gamma_control) = on.active_zwlr_gamma_control.take() {
zwlr_gamma_control.send_failed();
}
on.clear();
let _ = self.state.remove_global(&global);
let _ = self.state.remove_global(&tray);

View file

@ -1,8 +1,8 @@
use {
crate::{
backend::{
BackendColorSpace, BackendConnectorState, BackendEotfs, ButtonState, HardwareCursor,
Mode,
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendGammaLut, ButtonState,
HardwareCursor, Mode, transaction::BackendConnectorTransactionError,
},
client::ClientId,
cmm::cmm_description::ColorDescription,
@ -33,6 +33,7 @@ use {
},
wp_content_type_v1::ContentType,
wp_presentation_feedback::KIND_VSYNC,
zwlr_gamma_control_v1::ZwlrGammaControlV1,
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
},
@ -127,6 +128,7 @@ pub struct OutputNode {
pub ext_workspace_groups: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceGroupHandleV1>>,
pub pinned: LinkedList<Rc<dyn PinnedNode>>,
pub tearing: Cell<bool>,
pub active_zwlr_gamma_control: CloneCell<Option<Rc<ZwlrGammaControlV1>>>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
@ -1517,6 +1519,18 @@ impl OutputNode {
}
}
}
pub fn set_gamma_lut(
&self,
gamma_lut: Option<Rc<BackendGammaLut>>,
) -> Result<(), BackendConnectorTransactionError> {
self.global
.connector
.modify_state(&self.state, |s| s.gamma_lut = gamma_lut)
.inspect_err(|e| {
log::error!("Could not set gamma_lut: {}", ErrorFmt(e));
})
}
}
pub struct OutputTitle {

View file

@ -380,7 +380,7 @@ impl DrmMaster {
res
}
pub fn create_blob<T>(self: &Rc<Self>, t: &T) -> Result<PropBlob, DrmError> {
pub fn create_blob<T: ?Sized>(self: &Rc<Self>, t: &T) -> Result<PropBlob, DrmError> {
match mode_create_blob(self.raw(), t) {
Ok(b) => Ok(PropBlob {
master: self.clone(),

View file

@ -917,9 +917,9 @@ struct drm_mode_create_blob {
const DRM_IOCTL_MODE_CREATEPROPBLOB: u64 = drm_iowr::<drm_mode_create_blob>(0xbd);
pub fn mode_create_blob<T>(fd: c::c_int, t: &T) -> Result<DrmBlob, OsError> {
pub fn mode_create_blob<T: ?Sized>(fd: c::c_int, t: &T) -> Result<DrmBlob, OsError> {
let mut res = drm_mode_create_blob {
data: t as *const T as _,
data: t as *const T as *const () as _,
length: size_of_val(t) as _,
blob_id: 0,
};

View file

@ -8,9 +8,9 @@ use {
},
jay_config::client::{
CC_DATA_CONTROL, CC_DRM_LEASE, CC_FOREIGN_TOPLEVEL_LIST, CC_FOREIGN_TOPLEVEL_MANAGER,
CC_HEAD_MANAGER, CC_IDLE_NOTIFIER, CC_INPUT_METHOD, CC_LAYER_SHELL, CC_SCREENCOPY,
CC_SEAT_MANAGER, CC_SESSION_LOCK, CC_VIRTUAL_KEYBOARD, CC_WORKSPACE_MANAGER,
ClientCapabilities,
CC_GAMMA_CONTROL_MANAGER, CC_HEAD_MANAGER, CC_IDLE_NOTIFIER, CC_INPUT_METHOD,
CC_LAYER_SHELL, CC_SCREENCOPY, CC_SEAT_MANAGER, CC_SESSION_LOCK, CC_VIRTUAL_KEYBOARD,
CC_WORKSPACE_MANAGER, ClientCapabilities,
},
thiserror::Error,
};
@ -47,6 +47,7 @@ impl Parser for CapabilitiesParser {
"workspace-manager" => CC_WORKSPACE_MANAGER,
"foreign-toplevel-manager" => CC_FOREIGN_TOPLEVEL_MANAGER,
"head-manager" => CC_HEAD_MANAGER,
"gamma-control-manager" => CC_GAMMA_CONTROL_MANAGER,
_ => {
return Err(
CapabilitiesParserError::UnknownCapability(string.to_owned()).spanned(span),

View file

@ -641,7 +641,8 @@
"input-method",
"workspace-manager",
"foreign-toplevel-manager",
"head-manager"
"head-manager",
"gamma-control-manager"
]
},
{

View file

@ -1013,6 +1013,10 @@ The string should have one of the following values:
Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
globals.
- `gamma-control-manager`:
Grants access to the `zwlr_gamma_control_manager_v1` global.
#### An array

View file

@ -4360,6 +4360,9 @@ ClientCapabilities:
description: |
Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
globals.
- value: gamma-control-manager
description: |
Grants access to the `zwlr_gamma_control_manager_v1` global.
- kind: array
description: An array of masks that are OR'd.
items:

View file

@ -0,0 +1,7 @@
request get_gamma_control {
id: id(zwlr_gamma_control_v1) (new),
output: id(wl_output),
}
request destroy (destructor) {
}

View file

@ -0,0 +1,13 @@
event gamma_size {
size: u32,
}
request set_gamma {
fd: fd,
}
event failed {
}
request destroy (destructor) {
}