use { crate::{ backend::{ BackendColorSpace, BackendEotfs, ConnectorId, Mode, MonitorInfo, transaction::BackendConnectorTransactionError, }, client::ClientId, format::Format, globals::GlobalName, ifs::{ head_management::{ head_management_macros::HeadExts, jay_head_manager_session_v1::JayHeadManagerSessionV1, jay_head_v1::JayHeadV1, }, wl_output::{BlendSpace, PersistentOutputState}, }, scale::Scale, state::{OutputData, State}, tree::{OutputNode, TearingMode, Transform, VrrMode}, utils::{copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, rc_eq::RcEq}, wire::JayHeadManagerSessionV1Id, }, std::{ cell::{Cell, RefCell}, collections::hash_map::Entry, rc::Rc, }, thiserror::Error, }; #[macro_use] mod head_management_macros; pub mod jay_head_error_v1; mod jay_head_ext; pub mod jay_head_manager_session_v1; pub mod jay_head_manager_v1; mod jay_head_transaction_result_v1; mod jay_head_v1; linear_ids!(HeadNames, HeadName, u64); struct Head { session: Rc, common: Rc, head: Rc, ext: HeadExts, } #[derive(Error, Debug)] enum HeadTransactionError { #[error("The connector {} has been removed", .0)] HeadRemoved(ConnectorId), #[error("The display connected to connector {} has changed", .0)] MonitorChanged(ConnectorId), #[error("The transaction has already failed")] AlreadyFailed, #[error(transparent)] Backend(#[from] BackendConnectorTransactionError), } struct HeadCommon { mgr: Rc, name: HeadName, id: ConnectorId, removed: Cell, shared: Rc>, snapshot_state: RefCell, transaction_state: RefCell, pending: RefCell>, } #[derive(Clone, PartialEq)] pub struct HeadState { pub connector_id: ConnectorId, pub name: RcEq, pub wl_output: Option, pub connector_enabled: bool, pub active: bool, pub connected: bool, pub in_compositor_space: bool, pub position: (i32, i32), pub size: (i32, i32), pub mode: Mode, pub transform: Transform, pub scale: Scale, pub monitor_info: Option>, pub inherent_non_desktop: bool, pub override_non_desktop: Option, pub vrr: bool, pub vrr_mode: VrrMode, pub tearing_enabled: bool, pub tearing_active: bool, pub tearing_mode: TearingMode, pub format: &'static Format, pub color_space: BackendColorSpace, pub eotf: BackendEotfs, pub supported_formats: RcEq>, pub brightness: Option, pub blend_space: BlendSpace, pub use_native_gamut: bool, pub vrr_cursor_hz: Option, pub persistent_state: Option>, } impl HeadState { pub fn update_in_compositor_space(&mut self, state: &State, wl_output: Option) { self.in_compositor_space = false; self.wl_output = None; if !self.connector_enabled { return; } let Some(mi) = &self.monitor_info else { return; }; if mi.non_desktop { return; } if self.override_non_desktop == Some(true) { return; } self.in_compositor_space = true; self.wl_output = wl_output; if self.persistent_state.is_none() { let ds = state .persistent_output_states .get(&mi.output_id) .unwrap_or_else(|| state.new_persistent_output_state()); self.position = ds.pos.get(); self.transform = ds.transform.get(); self.vrr_mode = ds.vrr_mode.get(); self.tearing_mode = ds.tearing_mode.get(); self.brightness = ds.brightness.get(); self.blend_space = ds.blend_space.get(); self.use_native_gamut = ds.use_native_gamut.get(); self.vrr_cursor_hz = ds.vrr_cursor_hz.get(); self.scale = ds.scale.get(); self.persistent_state = Some(RcEq(ds)); if let Some(c) = state.connectors.get(&self.connector_id) { self.mode = c.state.borrow().mode; } self.update_size(); } } pub fn update_size(&mut self) { self.size = OutputNode::calculate_extents_(self.mode, self.transform, self.scale, self.position) .size(); } pub fn flush_persistent_state(&self, state: &State) { if let Some(mi) = &self.monitor_info && let Some(ds) = &self.persistent_state && let Entry::Vacant(v) = state .persistent_output_states .lock() .entry(mi.output_id.clone()) { v.insert(ds.0.clone()); } } } enum HeadOp { SetPosition(i32, i32), SetConnectorEnabled(bool), SetTransform(Transform), SetScale(Scale), SetMode(usize), SetNonDesktopOverride(Option), SetVrrMode(VrrMode), SetTearingMode(TearingMode), SetFormat(&'static Format), SetEotf(BackendEotfs), SetColorSpace(BackendColorSpace), SetBrightness(Option), } #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] enum HeadMgrState { #[default] Init, Started, StopScheduled, Stopped, } struct HeadMgrCommon { state: Cell, in_transaction: Cell, transaction_failed: Cell, } impl HeadCommon { fn assert_removed(&self) -> Result<(), HeadCommonError> { if self.removed.get() { Ok(()) } else { Err(HeadCommonError::NotYetRemoved) } } fn assert_in_transaction(&self) -> Result<(), HeadCommonError> { if self.mgr.in_transaction.get() { Ok(()) } else { Err(HeadCommonError::NotInTransaction) } } fn push_op(&self, op: HeadOp) -> Result<(), HeadCommonError> { self.assert_in_transaction()?; self.pending.borrow_mut().push(op); Ok(()) } } impl HeadMgrCommon { fn assert_stopped(&self) -> Result<(), HeadCommonError> { if self.state.get() == HeadMgrState::Stopped { Ok(()) } else { Err(HeadCommonError::NotYetStopped) } } } #[derive(Debug, Error)] pub enum HeadCommonError { #[error("Head has not yet been removed")] NotYetRemoved, #[error("Manager is not inside a transaction")] NotInTransaction, #[error("Manager has not yet been stopped")] NotYetStopped, } pub struct HeadManagers { pub name: HeadName, state: Rc>, managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc>, } macro_rules! skip_in_transaction { ($mgr:expr) => { if $mgr.common.mgr.in_transaction.get() { continue; } }; } impl HeadManagers { pub fn new(name: HeadName, state: HeadState) -> Self { Self { name, state: Rc::new(RefCell::new(state)), managers: Default::default(), } } pub fn handle_removed(&self) { for head in self.managers.lock().drain_values() { skip_in_transaction!(head); head.session.heads.remove(&self.name); head.head.send_removed(); head.session.schedule_done(); } } pub fn handle_output_connected(&self, s: &State, output: &OutputData) { let state = &mut *self.state.borrow_mut(); state.connected = true; state.monitor_info = Some(RcEq(output.monitor_info.clone())); state.persistent_state = None; state.inherent_non_desktop = output.monitor_info.non_desktop; state.update_in_compositor_space(s, output.node.as_ref().map(|n| n.global.name)); for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.connector_info_v1 { ext.send_connected(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.physical_display_info_v1 { ext.send_info(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.mode_setter_v1 { ext.send_modes(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_inside_outside(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.core_info_v1 { ext.send_wl_output(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.non_desktop_info_v1 { ext.send_state(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.vrr_state_v1 { ext.send_state(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.jay_vrr_mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.jay_tearing_mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.drm_color_space_setter_v1 { ext.send_supported(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.brightness_info_v1 { ext.send_implied_default_brightness(state); ext.send_brightness(state); head.session.schedule_done(); } } } pub fn handle_output_disconnected(&self, s: &State) { let state = &mut *self.state.borrow_mut(); state.connected = false; state.monitor_info = None; state.persistent_state = None; state.update_in_compositor_space(s, None); for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_inside_outside(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.connector_info_v1 { ext.send_connected(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.core_info_v1 { ext.send_wl_output(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.physical_display_info_v1 { ext.send_info(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.vrr_state_v1 { ext.send_state(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.drm_color_space_setter_v1 { ext.send_supported(state); head.session.schedule_done(); } } } pub fn handle_position_size_change(&self, node: &OutputNode) { let state = &mut *self.state.borrow_mut(); let pos = node.global.pos.get(); state.position = pos.position(); state.size = pos.size(); for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_position(state); ext.send_size(state); head.session.schedule_done(); } } } pub fn handle_mode_change(&self, mode: Mode) { let state = &mut *self.state.borrow_mut(); state.mode = mode; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } } } pub fn handle_transform_change(&self, transform: Transform) { let state = &mut *self.state.borrow_mut(); state.transform = transform; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_transform(state); head.session.schedule_done(); } } } pub fn handle_scale_change(&self, scale: Scale) { let state = &mut *self.state.borrow_mut(); state.scale = scale; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_scale(state); head.session.schedule_done(); } } } pub fn handle_enabled_change(&self, s: &State, enabled: bool) { let state = &mut *self.state.borrow_mut(); state.connector_enabled = enabled; state.update_in_compositor_space(s, state.wl_output); for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.compositor_space_info_v1 { ext.send_enabled(state); ext.send_inside_outside(state); head.session.schedule_done(); } } } pub fn handle_active_change(&self, active: bool) { let state = &mut *self.state.borrow_mut(); state.active = active; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.connector_info_v1 { ext.send_active(state); head.session.schedule_done(); } } } pub fn handle_non_desktop_override_changed(&self, overrd: Option) { let state = &mut *self.state.borrow_mut(); state.override_non_desktop = overrd; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.non_desktop_info_v1 { ext.send_state(state); head.session.schedule_done(); } } } pub fn handle_vrr_change(&self, vrr: bool) { let state = &mut *self.state.borrow_mut(); state.vrr = vrr; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.vrr_state_v1 { ext.send_state(state); head.session.schedule_done(); } } } pub fn handle_vrr_mode_change(&self, vrr_mode: &VrrMode) { let state = &mut *self.state.borrow_mut(); state.vrr_mode = *vrr_mode; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.jay_vrr_mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } } } pub fn handle_tearing_enabled_change(&self, enabled: bool) { let state = &mut *self.state.borrow_mut(); state.tearing_enabled = enabled; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.tearing_state_v1 { ext.send_enabled(state); head.session.schedule_done(); } } } pub fn handle_tearing_active_change(&self, active: bool) { let state = &mut *self.state.borrow_mut(); state.tearing_active = active; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.tearing_state_v1 { ext.send_active(state); head.session.schedule_done(); } } } pub fn handle_tearing_mode_change(&self, tearing_mode: &TearingMode) { let state = &mut *self.state.borrow_mut(); state.tearing_mode = *tearing_mode; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.jay_tearing_mode_info_v1 { ext.send_mode(state); head.session.schedule_done(); } } } pub fn handle_format_change(&self, format: &'static Format) { let state = &mut *self.state.borrow_mut(); state.format = format; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.format_info_v1 { ext.send_format(state); head.session.schedule_done(); } } } pub fn handle_colors_change(&self, color_space: BackendColorSpace, eotf: BackendEotfs) { let state = &mut *self.state.borrow_mut(); state.color_space = color_space; state.eotf = eotf; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.drm_color_space_info_v1 { ext.send_state(state); head.session.schedule_done(); } if let Some(ext) = &head.ext.brightness_info_v1 { ext.send_implied_default_brightness(state); head.session.schedule_done(); } } } pub fn handle_formats_change(&self, formats: &Rc>) { let state = &mut *self.state.borrow_mut(); state.supported_formats.0 = formats.clone(); for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.format_setter_v1 { ext.send_supported_formats(state); head.session.schedule_done(); } } } pub fn handle_brightness_change(&self, brightness: Option) { let state = &mut *self.state.borrow_mut(); state.brightness = brightness; for head in self.managers.lock().values() { skip_in_transaction!(head); if let Some(ext) = &head.ext.brightness_info_v1 { ext.send_brightness(state); head.session.schedule_done(); } } } pub fn handle_blend_space_change(&self, blend_space: BlendSpace) { let state = &mut *self.state.borrow_mut(); state.blend_space = blend_space; } pub fn handle_use_native_gamut_change(&self, use_native_gamut: bool) { let state = &mut *self.state.borrow_mut(); state.use_native_gamut = use_native_gamut; } pub fn handle_cursor_hz_change(&self, cursor_hz: Option) { let state = &mut *self.state.borrow_mut(); state.vrr_cursor_hz = cursor_hz; } }