Merge pull request #713 from khyperia/wlr-gamma-control
implement wlr-gamma-control-unstable-v1
This commit is contained in:
commit
f0c78c3fe6
33 changed files with 520 additions and 53 deletions
|
|
@ -196,6 +196,7 @@ Jay supports the following wayland protocols:
|
||||||
| xdg_wm_dialog_v1 | 1 | |
|
| xdg_wm_dialog_v1 | 1 | |
|
||||||
| zwlr_data_control_manager_v1 | 2 | Yes |
|
| zwlr_data_control_manager_v1 | 2 | Yes |
|
||||||
| zwlr_foreign_toplevel_manager_v1 | 3 | Yes |
|
| zwlr_foreign_toplevel_manager_v1 | 3 | Yes |
|
||||||
|
| zwlr_gamma_control_manager_v1 | 1 | Yes |
|
||||||
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
|
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
|
||||||
| zwlr_output_manager_v1 | 4 | Yes |
|
| zwlr_output_manager_v1 | 4 | Yes |
|
||||||
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
| zwlr_screencopy_manager_v1 | 3 | Yes |
|
||||||
|
|
|
||||||
|
|
@ -225,5 +225,7 @@ bitflags! {
|
||||||
/// Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
|
/// Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
|
||||||
/// globals.
|
/// globals.
|
||||||
pub const CC_HEAD_MANAGER = 1 << 13,
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ use {
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
},
|
},
|
||||||
uapi::{OwnedFd, c},
|
uapi::{OwnedFd, Packed, Pod, c},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
@ -165,6 +165,9 @@ pub trait Connector: Any {
|
||||||
self.kernel_id(),
|
self.kernel_id(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
fn gamma_lut_size(&self) -> Option<u32> {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -611,13 +614,49 @@ 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!(
|
linear_ids!(
|
||||||
BackendConnectorStateSerials,
|
BackendConnectorStateSerials,
|
||||||
BackendConnectorStateSerial,
|
BackendConnectorStateSerial,
|
||||||
u64
|
u64
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct BackendConnectorState {
|
pub struct BackendConnectorState {
|
||||||
pub serial: BackendConnectorStateSerial,
|
pub serial: BackendConnectorStateSerial,
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
|
@ -629,4 +668,5 @@ pub struct BackendConnectorState {
|
||||||
pub format: &'static Format,
|
pub format: &'static Format,
|
||||||
pub color_space: BackendColorSpace,
|
pub color_space: BackendColorSpace,
|
||||||
pub eotf: BackendEotfs,
|
pub eotf: BackendEotfs,
|
||||||
|
pub gamma_lut: Option<Rc<BackendGammaLut>>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,8 @@ pub enum BackendConnectorTransactionError {
|
||||||
AtomicTestFailed(#[source] DrmError),
|
AtomicTestFailed(#[source] DrmError),
|
||||||
#[error("Commit failed")]
|
#[error("Commit failed")]
|
||||||
AtomicCommitFailed(#[source] DrmError),
|
AtomicCommitFailed(#[source] DrmError),
|
||||||
|
#[error("Could not create a gamma lut blob")]
|
||||||
|
CreateGammaLutBlob(#[source] DrmError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BackendConnectorTransaction {
|
pub trait BackendConnectorTransaction {
|
||||||
|
|
@ -193,7 +195,7 @@ impl ConnectorTransaction {
|
||||||
Entry::Occupied(v) => v.into_mut(),
|
Entry::Occupied(v) => v.into_mut(),
|
||||||
Entry::Vacant(v) => v.insert(connector.create_transaction()?),
|
Entry::Vacant(v) => v.insert(connector.create_transaction()?),
|
||||||
};
|
};
|
||||||
tran.add(connector, state)?;
|
tran.add(connector, state.clone())?;
|
||||||
self.common.states.insert(connector.id(), state);
|
self.common.states.insert(connector.id(), state);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ use {
|
||||||
crate::{
|
crate::{
|
||||||
allocator::BufferObject,
|
allocator::BufferObject,
|
||||||
backend::{
|
backend::{
|
||||||
BackendColorSpace, BackendConnectorState, BackendEotfs, Connector, ConnectorEvent,
|
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendGammaLut, Connector,
|
||||||
|
ConnectorEvent,
|
||||||
transaction::{
|
transaction::{
|
||||||
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
BackendAppliedConnectorTransaction, BackendConnectorTransaction,
|
||||||
BackendConnectorTransactionError, BackendPreparedConnectorTransaction,
|
BackendConnectorTransactionError, BackendPreparedConnectorTransaction,
|
||||||
|
|
@ -57,6 +58,9 @@ pub struct DrmCrtcState {
|
||||||
pub mode_blob: Option<Rc<PropBlob>>,
|
pub mode_blob: Option<Rc<PropBlob>>,
|
||||||
pub vrr_enabled: bool,
|
pub vrr_enabled: bool,
|
||||||
pub assigned_connector: DrmConnector,
|
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)]
|
#[derive(Default, Clone, Debug)]
|
||||||
|
|
@ -189,7 +193,7 @@ impl MetalDrmDeviceData {
|
||||||
ConnectorConfig {
|
ConnectorConfig {
|
||||||
obj: connector.clone(),
|
obj: connector.clone(),
|
||||||
new: dd.drm_state.clone(),
|
new: dd.drm_state.clone(),
|
||||||
state: *dd.persistent.state.borrow(),
|
state: dd.persistent.state.borrow().clone(),
|
||||||
requested: false,
|
requested: false,
|
||||||
changed: Default::default(),
|
changed: Default::default(),
|
||||||
},
|
},
|
||||||
|
|
@ -420,6 +424,22 @@ impl MetalDeviceTransaction {
|
||||||
crtc.new.mode_blob = Some(Rc::new(blob));
|
crtc.new.mode_blob = Some(Rc::new(blob));
|
||||||
mode.clone()
|
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] {
|
for plane_id in [&mut crtc_planes.primary, &mut crtc_planes.cursor] {
|
||||||
if plane_id.is_none() {
|
if plane_id.is_none() {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -841,6 +861,12 @@ impl MetalDeviceTransactionWithDrmState {
|
||||||
log_change!(o, n, mode_blob_id);
|
log_change!(o, n, mode_blob_id);
|
||||||
c.change(crtc.obj.mode_id, 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!(
|
reset_default_properties!(
|
||||||
c,
|
c,
|
||||||
&*crtc.obj.untyped_properties.borrow(),
|
&*crtc.obj.untyped_properties.borrow(),
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ use {
|
||||||
async_engine::{Phase, SpawnedFuture},
|
async_engine::{Phase, SpawnedFuture},
|
||||||
backend::{
|
backend::{
|
||||||
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLease,
|
BackendColorSpace, BackendConnectorState, BackendDrmDevice, BackendDrmLease,
|
||||||
BackendDrmLessee, BackendEotfs, BackendEvent, BackendLuminance, CONCAP_CONNECTOR,
|
BackendDrmLessee, BackendEotfs, BackendEvent, BackendGammaLut, BackendGammaLutElement,
|
||||||
CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY, Connector, ConnectorCaps, ConnectorEvent,
|
BackendLuminance, CONCAP_CONNECTOR, CONCAP_MODE_SETTING, CONCAP_PHYSICAL_DISPLAY,
|
||||||
ConnectorId, ConnectorKernelId, DrmDeviceId, HardwareCursor, HardwareCursorUpdate,
|
Connector, ConnectorCaps, ConnectorEvent, ConnectorId, ConnectorKernelId, DrmDeviceId,
|
||||||
Mode, MonitorInfo,
|
HardwareCursor, HardwareCursorUpdate, Mode, MonitorInfo,
|
||||||
transaction::{
|
transaction::{
|
||||||
BackendConnectorTransaction, BackendConnectorTransactionError,
|
BackendConnectorTransaction, BackendConnectorTransactionError,
|
||||||
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
BackendConnectorTransactionType, BackendConnectorTransactionTypeDyn,
|
||||||
|
|
@ -637,7 +637,7 @@ impl MetalConnector {
|
||||||
| FrontState::Connected { non_desktop: true } => return,
|
| FrontState::Connected { non_desktop: true } => return,
|
||||||
FrontState::Connected { non_desktop: false } => {}
|
FrontState::Connected { non_desktop: false } => {}
|
||||||
}
|
}
|
||||||
let mut state = *self.display.borrow().persistent.state.borrow();
|
let mut state = self.display.borrow().persistent.state.borrow().clone();
|
||||||
state.serial = self.state.backend_connector_state_serials.next();
|
state.serial = self.state.backend_connector_state_serials.next();
|
||||||
self.send_event(ConnectorEvent::State(state));
|
self.send_event(ConnectorEvent::State(state));
|
||||||
}
|
}
|
||||||
|
|
@ -905,6 +905,10 @@ impl Connector for MetalConnector {
|
||||||
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
) -> Result<Box<dyn BackendConnectorTransaction>, BackendConnectorTransactionError> {
|
||||||
self.create_transaction().map(|v| Box::new(v) as _)
|
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 {
|
pub struct MetalCrtc {
|
||||||
|
|
@ -925,6 +929,8 @@ pub struct MetalCrtc {
|
||||||
pub mode_id: DrmProperty,
|
pub mode_id: DrmProperty,
|
||||||
pub vrr_enabled: DrmProperty,
|
pub vrr_enabled: DrmProperty,
|
||||||
pub out_fence_ptr: DrmProperty,
|
pub out_fence_ptr: DrmProperty,
|
||||||
|
pub gamma_lut: Option<DrmProperty>,
|
||||||
|
pub gamma_lut_size: Option<u32>,
|
||||||
pub drm_state: RefCell<DrmCrtcState>,
|
pub drm_state: RefCell<DrmCrtcState>,
|
||||||
|
|
||||||
pub sequence: Cell<u64>,
|
pub sequence: Cell<u64>,
|
||||||
|
|
@ -1327,6 +1333,7 @@ fn create_connector_display_data(
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: Default::default(),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
dev.backend
|
dev.backend
|
||||||
|
|
@ -1499,7 +1506,6 @@ fn create_crtc(
|
||||||
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
|
("AMD_CRTC_REGAMMA_TF", DefaultValue::Enum("Default")),
|
||||||
("CTM", DefaultValue::Fixed(0)),
|
("CTM", DefaultValue::Fixed(0)),
|
||||||
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
|
("DEGAMMA_LUT", DefaultValue::Fixed(0)),
|
||||||
("GAMMA_LUT", DefaultValue::Fixed(0)),
|
|
||||||
("OUT_FENCE_PTR", 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 mode_id = props.get("MODE_ID")?.map(|v| DrmBlob(v as u32));
|
||||||
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
|
let vrr_enabled = props.get("VRR_ENABLED")?.map(|v| v == 1);
|
||||||
let out_fence_ptr = props.get("OUT_FENCE_PTR")?;
|
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;
|
let mut mode = None;
|
||||||
if mode_id.value.is_some() {
|
if mode_id.value.is_some() {
|
||||||
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
|
match master.getblob::<drm_mode_modeinfo>(mode_id.value) {
|
||||||
|
|
@ -1523,6 +1537,9 @@ fn create_crtc(
|
||||||
mode_blob: None,
|
mode_blob: None,
|
||||||
vrr_enabled: vrr_enabled.value,
|
vrr_enabled: vrr_enabled.value,
|
||||||
assigned_connector: DrmConnector::NONE,
|
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 {
|
Ok(MetalCrtc {
|
||||||
id: crtc,
|
id: crtc,
|
||||||
|
|
@ -1539,6 +1556,8 @@ fn create_crtc(
|
||||||
mode_id: mode_id.id,
|
mode_id: mode_id.id,
|
||||||
vrr_enabled: vrr_enabled.id,
|
vrr_enabled: vrr_enabled.id,
|
||||||
out_fence_ptr: out_fence_ptr.id,
|
out_fence_ptr: out_fence_ptr.id,
|
||||||
|
gamma_lut: gamma_lut.map(|v| v.id),
|
||||||
|
gamma_lut_size,
|
||||||
sequence: Cell::new(0),
|
sequence: Cell::new(0),
|
||||||
have_queued_sequence: Cell::new(false),
|
have_queued_sequence: Cell::new(false),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
|
|
@ -1928,7 +1947,7 @@ impl MetalBackend {
|
||||||
if dd.supports_bt2020 {
|
if dd.supports_bt2020 {
|
||||||
color_spaces.push(BackendColorSpace::Bt2020);
|
color_spaces.push(BackendColorSpace::Bt2020);
|
||||||
}
|
}
|
||||||
let mut state = *dd.persistent.state.borrow();
|
let mut state = dd.persistent.state.borrow().clone();
|
||||||
state.serial = self.state.backend_connector_state_serials.next();
|
state.serial = self.state.backend_connector_state_serials.next();
|
||||||
connector.send_event(ConnectorEvent::Connected(MonitorInfo {
|
connector.send_event(ConnectorEvent::Connected(MonitorInfo {
|
||||||
modes,
|
modes,
|
||||||
|
|
@ -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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2573,7 +2611,7 @@ impl MetalBackend {
|
||||||
let mut tran = dev.create_transaction();
|
let mut tran = dev.create_transaction();
|
||||||
for c in dev.connectors.lock().values() {
|
for c in dev.connectors.lock().values() {
|
||||||
let dd = &*c.display.borrow();
|
let dd = &*c.display.borrow();
|
||||||
let mut state = *dd.persistent.state.borrow();
|
let mut state = dd.persistent.state.borrow().clone();
|
||||||
let mut changed_any = false;
|
let mut changed_any = false;
|
||||||
if disable_non_default_format && state.format != XRGB8888 {
|
if disable_non_default_format && state.format != XRGB8888 {
|
||||||
state.format = XRGB8888;
|
state.format = XRGB8888;
|
||||||
|
|
|
||||||
|
|
@ -493,6 +493,7 @@ impl XBackend {
|
||||||
format: FORMAT,
|
format: FORMAT,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: Default::default(),
|
||||||
};
|
};
|
||||||
let output = Rc::new(XOutput {
|
let output = Rc::new(XOutput {
|
||||||
id: self.state.connector_ids.next(),
|
id: self.state.connector_ids.next(),
|
||||||
|
|
@ -506,7 +507,7 @@ impl XBackend {
|
||||||
next_image: Default::default(),
|
next_image: Default::default(),
|
||||||
cb: CloneCell::new(None),
|
cb: CloneCell::new(None),
|
||||||
images,
|
images,
|
||||||
state: Cell::new(state),
|
state: RefCell::new(state),
|
||||||
});
|
});
|
||||||
{
|
{
|
||||||
let class = "jay\0jay\0";
|
let class = "jay\0jay\0";
|
||||||
|
|
@ -604,7 +605,7 @@ impl XBackend {
|
||||||
color_spaces: vec![],
|
color_spaces: vec![],
|
||||||
primaries: Primaries::SRGB,
|
primaries: Primaries::SRGB,
|
||||||
luminance: None,
|
luminance: None,
|
||||||
state: output.state.get(),
|
state: output.state.borrow().clone(),
|
||||||
}));
|
}));
|
||||||
output.changed();
|
output.changed();
|
||||||
self.present(output).await;
|
self.present(output).await;
|
||||||
|
|
@ -983,11 +984,11 @@ impl XBackend {
|
||||||
old.tex.set(new.tex.get());
|
old.tex.set(new.tex.get());
|
||||||
old.pixmap.set(new.pixmap.get());
|
old.pixmap.set(new.pixmap.get());
|
||||||
}
|
}
|
||||||
let mut state = output.state.get();
|
let mut state = output.state.borrow().clone();
|
||||||
state.serial = self.state.backend_connector_state_serials.next();
|
state.serial = self.state.backend_connector_state_serials.next();
|
||||||
state.mode.width = width;
|
state.mode.width = width;
|
||||||
state.mode.height = height;
|
state.mode.height = height;
|
||||||
output.state.set(state);
|
*output.state.borrow_mut() = state.clone();
|
||||||
output.events.push(ConnectorEvent::State(state));
|
output.events.push(ConnectorEvent::State(state));
|
||||||
output.changed();
|
output.changed();
|
||||||
}
|
}
|
||||||
|
|
@ -1057,7 +1058,7 @@ struct XOutput {
|
||||||
next_image: NumCell<usize>,
|
next_image: NumCell<usize>,
|
||||||
images: [XImage; 2],
|
images: [XImage; 2],
|
||||||
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
cb: CloneCell<Option<Rc<dyn Fn()>>>,
|
||||||
state: Cell<BackendConnectorState>,
|
state: RefCell<BackendConnectorState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct XImage {
|
struct XImage {
|
||||||
|
|
@ -1133,7 +1134,7 @@ struct XTransaction {
|
||||||
impl XTransaction {
|
impl XTransaction {
|
||||||
fn send_state(&self) {
|
fn send_state(&self) {
|
||||||
for con in self.connectors.values() {
|
for con in self.connectors.values() {
|
||||||
let mut state = con.state.get();
|
let mut state = con.state.borrow().clone();
|
||||||
state.serial = con.backend.state.backend_connector_state_serials.next();
|
state.serial = con.backend.state.backend_connector_state_serials.next();
|
||||||
con.events.push(ConnectorEvent::State(state));
|
con.events.push(ConnectorEvent::State(state));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ bitflags! {
|
||||||
CAP_WORKSPACE = 1 << 11,
|
CAP_WORKSPACE = 1 << 11,
|
||||||
CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
|
CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
|
||||||
CAP_HEAD_MANAGER = 1 << 13,
|
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);
|
pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use {
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::{
|
uapi::{
|
||||||
OwnedFd,
|
OwnedFd, Pod,
|
||||||
c::{self, raise},
|
c::{self, raise},
|
||||||
ftruncate,
|
ftruncate,
|
||||||
},
|
},
|
||||||
|
|
@ -30,6 +30,8 @@ pub enum ClientMemError {
|
||||||
Sigbus,
|
Sigbus,
|
||||||
#[error("mmap failed")]
|
#[error("mmap failed")]
|
||||||
MmapFailed(#[source] crate::utils::oserror::OsError),
|
MmapFailed(#[source] crate::utils::oserror::OsError),
|
||||||
|
#[error("Length was not a multiple of the data element size")]
|
||||||
|
InvalidLength,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ClientMem {
|
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| {
|
self.access(|v| {
|
||||||
dst.reserve(v.len());
|
let len_elements = v.len() / std::mem::size_of::<T>();
|
||||||
let (_, unused) = dst.split_at_spare_mut_ext();
|
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));
|
unused[..v.len()].copy_from_slice(uapi::as_maybe_uninit_bytes(v));
|
||||||
unsafe {
|
unsafe {
|
||||||
dst.set_len(dst.len() + v.len());
|
dst.set_len(dst.len() + len_elements);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,15 @@ use {
|
||||||
video::{GfxApi, Transform},
|
video::{GfxApi, Transform},
|
||||||
workspace::WorkspaceDisplayOrder,
|
workspace::WorkspaceDisplayOrder,
|
||||||
},
|
},
|
||||||
std::{cell::Cell, env, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration},
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
env,
|
||||||
|
future::Future,
|
||||||
|
ops::Deref,
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
uapi::c,
|
uapi::c,
|
||||||
};
|
};
|
||||||
|
|
@ -650,6 +658,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: Default::default(),
|
||||||
};
|
};
|
||||||
let id = state.connector_ids.next();
|
let id = state.connector_ids.next();
|
||||||
let connector = Rc::new(DummyOutput { id }) as Rc<dyn Connector>;
|
let connector = Rc::new(DummyOutput { id }) as Rc<dyn Connector>;
|
||||||
|
|
@ -694,7 +703,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
damage: Default::default(),
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
damage_intersect: Default::default(),
|
damage_intersect: Default::default(),
|
||||||
state: Cell::new(backend_state),
|
state: RefCell::new(backend_state),
|
||||||
head_managers: HeadManagers::new(head_name, head_state),
|
head_managers: HeadManagers::new(head_name, head_state),
|
||||||
wlr_output_heads: Default::default(),
|
wlr_output_heads: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
@ -763,6 +772,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
||||||
ext_workspace_groups: Default::default(),
|
ext_workspace_groups: Default::default(),
|
||||||
pinned: Default::default(),
|
pinned: Default::default(),
|
||||||
tearing: Default::default(),
|
tearing: Default::default(),
|
||||||
|
active_zwlr_gamma_control: Default::default(),
|
||||||
});
|
});
|
||||||
let dummy_workspace = WorkspaceNode::new(&dummy_output, "dummy", true);
|
let dummy_workspace = WorkspaceNode::new(&dummy_output, "dummy", true);
|
||||||
dummy_workspace.may_capture.set(false);
|
dummy_workspace.may_capture.set(false);
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ use {
|
||||||
xdg_wm_base::XdgWmBaseGlobal,
|
xdg_wm_base::XdgWmBaseGlobal,
|
||||||
xdg_wm_dialog_v1::XdgWmDialogV1Global,
|
xdg_wm_dialog_v1::XdgWmDialogV1Global,
|
||||||
zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1Global,
|
zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1Global,
|
||||||
|
zwlr_gamma_control_manager_v1::ZwlrGammaControlManagerV1Global,
|
||||||
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
|
zwlr_layer_shell_v1::ZwlrLayerShellV1Global,
|
||||||
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
|
zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1Global,
|
||||||
zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global,
|
zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1Global,
|
||||||
|
|
@ -233,6 +234,7 @@ impl Globals {
|
||||||
add_singleton!(JayHeadManagerV1Global);
|
add_singleton!(JayHeadManagerV1Global);
|
||||||
add_singleton!(WpPointerWarpV1Global);
|
add_singleton!(WpPointerWarpV1Global);
|
||||||
add_singleton!(JayPopupExtManagerV1Global);
|
add_singleton!(JayPopupExtManagerV1Global);
|
||||||
|
add_singleton!(ZwlrGammaControlManagerV1Global);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,8 @@ pub mod xdg_wm_base;
|
||||||
pub mod xdg_wm_dialog_v1;
|
pub mod xdg_wm_dialog_v1;
|
||||||
pub mod zwlr_foreign_toplevel_handle_v1;
|
pub mod zwlr_foreign_toplevel_handle_v1;
|
||||||
pub mod zwlr_foreign_toplevel_manager_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_layer_shell_v1;
|
||||||
pub mod zwlr_screencopy_frame_v1;
|
pub mod zwlr_screencopy_frame_v1;
|
||||||
pub mod zwlr_screencopy_manager_v1;
|
pub mod zwlr_screencopy_manager_v1;
|
||||||
|
|
|
||||||
|
|
@ -258,8 +258,8 @@ impl JayHeadManagerSessionV1 {
|
||||||
let Some(connector) = self.client.state.connectors.get(&head.common.id) else {
|
let Some(connector) = self.client.state.connectors.get(&head.common.id) else {
|
||||||
return Err(HeadTransactionError::HeadRemoved(head.common.id));
|
return Err(HeadTransactionError::HeadRemoved(head.common.id));
|
||||||
};
|
};
|
||||||
let old = connector.state.get();
|
let old = connector.state.borrow().clone();
|
||||||
let mut new = old;
|
let mut new = old.clone();
|
||||||
new.enabled = desired.connector_enabled;
|
new.enabled = desired.connector_enabled;
|
||||||
new.mode = desired.mode;
|
new.mode = desired.mode;
|
||||||
new.non_desktop_override = desired.override_non_desktop;
|
new.non_desktop_override = desired.override_non_desktop;
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ impl JayRandr {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_connector(&self, data: &ConnectorData) {
|
fn send_connector(&self, data: &ConnectorData) {
|
||||||
let state = data.state.get();
|
let state_enabled = data.state.borrow().enabled;
|
||||||
self.client.event(Connector {
|
self.client.event(Connector {
|
||||||
self_id: self.id,
|
self_id: self.id,
|
||||||
id: data.connector.id().raw() as _,
|
id: data.connector.id().raw() as _,
|
||||||
|
|
@ -81,7 +81,7 @@ impl JayRandr {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|d| d.dev.id().raw() as _)
|
.map(|d| d.dev.id().raw() as _)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
enabled: state.enabled as _,
|
enabled: state_enabled as _,
|
||||||
name: &data.name,
|
name: &data.name,
|
||||||
});
|
});
|
||||||
let Some(output) = self.client.state.outputs.get(&data.connector.id()) else {
|
let Some(output) = self.client.state.outputs.get(&data.connector.id()) else {
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ impl WlOutputGlobal {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (x, y) = persistent_state.pos.get();
|
let (x, y) = persistent_state.pos.get();
|
||||||
let scale = persistent_state.scale.get();
|
let scale = persistent_state.scale.get();
|
||||||
let connector_state = connector.state.get();
|
let connector_state = connector.state.borrow();
|
||||||
let (width, height) = calculate_logical_size(
|
let (width, height) = calculate_logical_size(
|
||||||
(connector_state.mode.width, connector_state.mode.height),
|
(connector_state.mode.width, connector_state.mode.height),
|
||||||
persistent_state.transform.get(),
|
persistent_state.transform.get(),
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ impl ZwlrOutputConfigurationV1 {
|
||||||
}
|
}
|
||||||
let mut tran = ConnectorTransaction::new(&self.client.state);
|
let mut tran = ConnectorTransaction::new(&self.client.state);
|
||||||
for output in self.client.state.outputs.lock().values() {
|
for output in self.client.state.outputs.lock().values() {
|
||||||
let mut state = output.connector.state.get();
|
let mut state = output.connector.state.borrow().clone();
|
||||||
match self.enabled_outputs.get(&output.connector.id) {
|
match self.enabled_outputs.get(&output.connector.id) {
|
||||||
None => {
|
None => {
|
||||||
if self.configured_outputs.not_contains(&output.connector.id) {
|
if self.configured_outputs.not_contains(&output.connector.id) {
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ impl ZwlrOutputManagerV1 {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mi = &output.monitor_info;
|
let mi = &output.monitor_info;
|
||||||
let state = output.connector.state.get();
|
let state_mode = output.connector.state.borrow().mode;
|
||||||
let head_id = self.client.state.wlr_output_managers.head_ids.next();
|
let head_id = self.client.state.wlr_output_managers.head_ids.next();
|
||||||
let mut modes_list = vec![];
|
let mut modes_list = vec![];
|
||||||
let mut modes = AHashMap::new();
|
let mut modes = AHashMap::new();
|
||||||
|
|
@ -140,7 +140,7 @@ impl ZwlrOutputManagerV1 {
|
||||||
if modes.contains_key(mode) {
|
if modes.contains_key(mode) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let current = !have_current && *mode == state.mode;
|
let current = !have_current && *mode == state_mode;
|
||||||
if current {
|
if current {
|
||||||
have_current = true;
|
have_current = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
115
src/ifs/zwlr_gamma_control_manager_v1.rs
Normal file
115
src/ifs/zwlr_gamma_control_manager_v1.rs
Normal 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);
|
||||||
166
src/ifs/zwlr_gamma_control_v1.rs
Normal file
166
src/ifs/zwlr_gamma_control_v1.rs
Normal 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);
|
||||||
|
|
@ -153,6 +153,7 @@ impl TestBackend {
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: Default::default(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ async fn test(run: Rc<TestRun>) -> TestResult {
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: Default::default(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
run.backend
|
run.backend
|
||||||
|
|
|
||||||
12
src/state.rs
12
src/state.rs
|
|
@ -420,7 +420,7 @@ pub struct ConnectorData {
|
||||||
pub damage: RefCell<Vec<Rect>>,
|
pub damage: RefCell<Vec<Rect>>,
|
||||||
pub needs_vblank_emulation: Cell<bool>,
|
pub needs_vblank_emulation: Cell<bool>,
|
||||||
pub damage_intersect: Cell<Rect>,
|
pub damage_intersect: Cell<Rect>,
|
||||||
pub state: Cell<BackendConnectorState>,
|
pub state: RefCell<BackendConnectorState>,
|
||||||
pub head_managers: HeadManagers,
|
pub head_managers: HeadManagers,
|
||||||
pub wlr_output_heads: CopyHashMap<WlrOutputManagerId, Rc<ZwlrOutputHeadV1>>,
|
pub wlr_output_heads: CopyHashMap<WlrOutputManagerId, Rc<ZwlrOutputHeadV1>>,
|
||||||
}
|
}
|
||||||
|
|
@ -456,26 +456,26 @@ impl ConnectorData {
|
||||||
state: &State,
|
state: &State,
|
||||||
f: impl FnOnce(&mut BackendConnectorState),
|
f: impl FnOnce(&mut BackendConnectorState),
|
||||||
) -> Result<(), BackendConnectorTransactionError> {
|
) -> Result<(), BackendConnectorTransactionError> {
|
||||||
let old = self.state.get();
|
let old = self.state.borrow().clone();
|
||||||
let mut s = old;
|
let mut s = old.clone();
|
||||||
f(&mut s);
|
f(&mut s);
|
||||||
if old == s {
|
if old == s {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
s.serial = state.backend_connector_state_serials.next();
|
s.serial = state.backend_connector_state_serials.next();
|
||||||
let mut tran = self.connector.create_transaction()?;
|
let mut tran = self.connector.create_transaction()?;
|
||||||
tran.add(&self.connector, s)?;
|
tran.add(&self.connector, s.clone())?;
|
||||||
tran.prepare()?.apply()?.commit();
|
tran.prepare()?.apply()?.commit();
|
||||||
self.set_state(state, s);
|
self.set_state(state, s);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_state(&self, state: &State, s: BackendConnectorState) {
|
pub fn set_state(&self, state: &State, s: BackendConnectorState) {
|
||||||
let old = self.state.get();
|
let old = self.state.borrow().clone();
|
||||||
if old.serial >= s.serial {
|
if old.serial >= s.serial {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.state.set(s);
|
*self.state.borrow_mut() = s.clone();
|
||||||
if old.enabled != s.enabled {
|
if old.enabled != s.enabled {
|
||||||
self.head_managers.handle_enabled_change(s.enabled);
|
self.head_managers.handle_enabled_change(s.enabled);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,12 @@ use {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
jay_config::video::Transform,
|
jay_config::video::Transform,
|
||||||
std::{cell::Cell, collections::VecDeque, fmt, rc::Rc},
|
std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
collections::VecDeque,
|
||||||
|
fmt,
|
||||||
|
rc::Rc,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
|
|
@ -41,6 +46,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
format: XRGB8888,
|
format: XRGB8888,
|
||||||
color_space: Default::default(),
|
color_space: Default::default(),
|
||||||
eotf: Default::default(),
|
eotf: Default::default(),
|
||||||
|
gamma_lut: None,
|
||||||
};
|
};
|
||||||
let id = connector.id();
|
let id = connector.id();
|
||||||
let name = Rc::new(connector.kernel_id().to_string());
|
let name = Rc::new(connector.kernel_id().to_string());
|
||||||
|
|
@ -83,7 +89,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
||||||
damage: Default::default(),
|
damage: Default::default(),
|
||||||
needs_vblank_emulation: Cell::new(false),
|
needs_vblank_emulation: Cell::new(false),
|
||||||
damage_intersect: Default::default(),
|
damage_intersect: Default::default(),
|
||||||
state: Cell::new(backend_state),
|
state: RefCell::new(backend_state),
|
||||||
head_managers: HeadManagers::new(state.head_names.next(), head_state),
|
head_managers: HeadManagers::new(state.head_names.next(), head_state),
|
||||||
wlr_output_heads: Default::default(),
|
wlr_output_heads: Default::default(),
|
||||||
});
|
});
|
||||||
|
|
@ -144,7 +150,7 @@ impl ConnectorHandler {
|
||||||
async fn handle_connected(&self, info: MonitorInfo) {
|
async fn handle_connected(&self, info: MonitorInfo) {
|
||||||
log::info!("Connector {} connected", self.data.connector.kernel_id());
|
log::info!("Connector {} connected", self.data.connector.kernel_id());
|
||||||
self.data.connected.set(true);
|
self.data.connected.set(true);
|
||||||
self.data.set_state(&self.state, info.state);
|
self.data.set_state(&self.state, info.state.clone());
|
||||||
*self.data.description.borrow_mut() = create_description(&info);
|
*self.data.description.borrow_mut() = create_description(&info);
|
||||||
let name = self.state.globals.name();
|
let name = self.state.globals.name();
|
||||||
if info.non_desktop_effective {
|
if info.non_desktop_effective {
|
||||||
|
|
@ -265,6 +271,7 @@ impl ConnectorHandler {
|
||||||
ext_workspace_groups: Default::default(),
|
ext_workspace_groups: Default::default(),
|
||||||
pinned: Default::default(),
|
pinned: Default::default(),
|
||||||
tearing: Default::default(),
|
tearing: Default::default(),
|
||||||
|
active_zwlr_gamma_control: Default::default(),
|
||||||
});
|
});
|
||||||
on.update_visible();
|
on.update_visible();
|
||||||
on.update_rects();
|
on.update_rects();
|
||||||
|
|
@ -416,6 +423,9 @@ impl ConnectorHandler {
|
||||||
}
|
}
|
||||||
self.state
|
self.state
|
||||||
.remove_output_scale(on.global.persistent.scale.get());
|
.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();
|
on.clear();
|
||||||
let _ = self.state.remove_global(&global);
|
let _ = self.state.remove_global(&global);
|
||||||
let _ = self.state.remove_global(&tray);
|
let _ = self.state.remove_global(&tray);
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ impl Idle {
|
||||||
fn try_set_idle(&self, idle: bool) -> Result<(), BackendConnectorTransactionError> {
|
fn try_set_idle(&self, idle: bool) -> Result<(), BackendConnectorTransactionError> {
|
||||||
let mut tran = ConnectorTransaction::new(&self.state);
|
let mut tran = ConnectorTransaction::new(&self.state);
|
||||||
for connector in self.state.connectors.lock().values() {
|
for connector in self.state.connectors.lock().values() {
|
||||||
let mut state = connector.state.get();
|
let mut state = connector.state.borrow().clone();
|
||||||
state.active = !idle;
|
state.active = !idle;
|
||||||
tran.add(&connector.connector, state)?;
|
tran.add(&connector.connector, state)?;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
backend::{
|
backend::{
|
||||||
BackendColorSpace, BackendConnectorState, BackendEotfs, ButtonState, HardwareCursor,
|
BackendColorSpace, BackendConnectorState, BackendEotfs, BackendGammaLut, ButtonState,
|
||||||
Mode,
|
HardwareCursor, Mode, transaction::BackendConnectorTransactionError,
|
||||||
},
|
},
|
||||||
client::ClientId,
|
client::ClientId,
|
||||||
cmm::cmm_description::ColorDescription,
|
cmm::cmm_description::ColorDescription,
|
||||||
|
|
@ -33,6 +33,7 @@ use {
|
||||||
},
|
},
|
||||||
wp_content_type_v1::ContentType,
|
wp_content_type_v1::ContentType,
|
||||||
wp_presentation_feedback::KIND_VSYNC,
|
wp_presentation_feedback::KIND_VSYNC,
|
||||||
|
zwlr_gamma_control_v1::ZwlrGammaControlV1,
|
||||||
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
||||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||||
},
|
},
|
||||||
|
|
@ -127,6 +128,7 @@ pub struct OutputNode {
|
||||||
pub ext_workspace_groups: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceGroupHandleV1>>,
|
pub ext_workspace_groups: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceGroupHandleV1>>,
|
||||||
pub pinned: LinkedList<Rc<dyn PinnedNode>>,
|
pub pinned: LinkedList<Rc<dyn PinnedNode>>,
|
||||||
pub tearing: Cell<bool>,
|
pub tearing: Cell<bool>,
|
||||||
|
pub active_zwlr_gamma_control: CloneCell<Option<Rc<ZwlrGammaControlV1>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[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 {
|
pub struct OutputTitle {
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,7 @@ impl DrmMaster {
|
||||||
res
|
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) {
|
match mode_create_blob(self.raw(), t) {
|
||||||
Ok(b) => Ok(PropBlob {
|
Ok(b) => Ok(PropBlob {
|
||||||
master: self.clone(),
|
master: self.clone(),
|
||||||
|
|
|
||||||
|
|
@ -917,9 +917,9 @@ struct drm_mode_create_blob {
|
||||||
|
|
||||||
const DRM_IOCTL_MODE_CREATEPROPBLOB: u64 = drm_iowr::<drm_mode_create_blob>(0xbd);
|
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 {
|
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 _,
|
length: size_of_val(t) as _,
|
||||||
blob_id: 0,
|
blob_id: 0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ use {
|
||||||
},
|
},
|
||||||
jay_config::client::{
|
jay_config::client::{
|
||||||
CC_DATA_CONTROL, CC_DRM_LEASE, CC_FOREIGN_TOPLEVEL_LIST, CC_FOREIGN_TOPLEVEL_MANAGER,
|
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_GAMMA_CONTROL_MANAGER, CC_HEAD_MANAGER, CC_IDLE_NOTIFIER, CC_INPUT_METHOD,
|
||||||
CC_SEAT_MANAGER, CC_SESSION_LOCK, CC_VIRTUAL_KEYBOARD, CC_WORKSPACE_MANAGER,
|
CC_LAYER_SHELL, CC_SCREENCOPY, CC_SEAT_MANAGER, CC_SESSION_LOCK, CC_VIRTUAL_KEYBOARD,
|
||||||
ClientCapabilities,
|
CC_WORKSPACE_MANAGER, ClientCapabilities,
|
||||||
},
|
},
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
};
|
};
|
||||||
|
|
@ -47,6 +47,7 @@ impl Parser for CapabilitiesParser {
|
||||||
"workspace-manager" => CC_WORKSPACE_MANAGER,
|
"workspace-manager" => CC_WORKSPACE_MANAGER,
|
||||||
"foreign-toplevel-manager" => CC_FOREIGN_TOPLEVEL_MANAGER,
|
"foreign-toplevel-manager" => CC_FOREIGN_TOPLEVEL_MANAGER,
|
||||||
"head-manager" => CC_HEAD_MANAGER,
|
"head-manager" => CC_HEAD_MANAGER,
|
||||||
|
"gamma-control-manager" => CC_GAMMA_CONTROL_MANAGER,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(
|
return Err(
|
||||||
CapabilitiesParserError::UnknownCapability(string.to_owned()).spanned(span),
|
CapabilitiesParserError::UnknownCapability(string.to_owned()).spanned(span),
|
||||||
|
|
|
||||||
|
|
@ -641,7 +641,8 @@
|
||||||
"input-method",
|
"input-method",
|
||||||
"workspace-manager",
|
"workspace-manager",
|
||||||
"foreign-toplevel-manager",
|
"foreign-toplevel-manager",
|
||||||
"head-manager"
|
"head-manager",
|
||||||
|
"gamma-control-manager"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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`
|
Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
|
||||||
globals.
|
globals.
|
||||||
|
|
||||||
|
- `gamma-control-manager`:
|
||||||
|
|
||||||
|
Grants access to the `zwlr_gamma_control_manager_v1` global.
|
||||||
|
|
||||||
|
|
||||||
#### An array
|
#### An array
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4360,6 +4360,9 @@ ClientCapabilities:
|
||||||
description: |
|
description: |
|
||||||
Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
|
Grants access to the `jay_head_manager_v1` and `zwlr_output_manager_v1`
|
||||||
globals.
|
globals.
|
||||||
|
- value: gamma-control-manager
|
||||||
|
description: |
|
||||||
|
Grants access to the `zwlr_gamma_control_manager_v1` global.
|
||||||
- kind: array
|
- kind: array
|
||||||
description: An array of masks that are OR'd.
|
description: An array of masks that are OR'd.
|
||||||
items:
|
items:
|
||||||
|
|
|
||||||
7
wire/zwlr_gamma_control_manager_v1.txt
Normal file
7
wire/zwlr_gamma_control_manager_v1.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
request get_gamma_control {
|
||||||
|
id: id(zwlr_gamma_control_v1) (new),
|
||||||
|
output: id(wl_output),
|
||||||
|
}
|
||||||
|
|
||||||
|
request destroy (destructor) {
|
||||||
|
}
|
||||||
13
wire/zwlr_gamma_control_v1.txt
Normal file
13
wire/zwlr_gamma_control_v1.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
event gamma_size {
|
||||||
|
size: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
request set_gamma {
|
||||||
|
fd: fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
event failed {
|
||||||
|
}
|
||||||
|
|
||||||
|
request destroy (destructor) {
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue