use { super::HeadMgrCommon, crate::{ backend::transaction::{ConnectorTransaction, PreparedConnectorTransaction}, client::{Client, ClientError}, ifs::head_management::{ Head, HeadCommon, HeadCommonError, HeadMgrState, HeadName, HeadOp, HeadTransactionError, head_management_macros::{HeadExtension, MgrExts, announce_head, bind_extension}, jay_head_manager_v1::JayHeadManagerV1, jay_head_transaction_result_v1::JayHeadTransactionResultV1, jay_head_v1::JayHeadV1, }, leaks::Tracker, object::{Object, Version}, state::{ConnectorData, State}, tree::{TearingMode, VrrMode}, utils::{copyhashmap::CopyHashMap, numcell::NumCell}, wire::{ JayHeadManagerSessionV1Id, JayHeadTransactionResultV1Id, jay_head_manager_session_v1::{ ApplyChanges, BeginTransaction, BindExtension, CommitTransaction, Destroy, HeadComplete, HeadStart, JayHeadManagerSessionV1RequestHandler, RollbackTransaction, Start, Stop, Stopped, TestTransaction, TransactionEnded, TransactionStarted, }, }, }, linearize::LinearizeExt, std::{cell::Cell, mem, ops::Deref, rc::Rc}, thiserror::Error, }; pub struct HeadManagerEvent { mgr: Rc, ty: HeadManagerEventType, } pub(super) enum HeadManagerEventType { Done, TransactionStarted, TransactionEnded, TransactionResult(Rc), Stopped, } pub async fn handle_jay_head_manager_done(state: Rc) { loop { let ev = state.head_managers_async.pop().await; let session = ev.mgr; if session.mgr.destroyed.get() { continue; } if session.mgr.done_scheduled.take() { session.mgr.send_done(); } if session.common.state.get() == HeadMgrState::Stopped { continue; } match ev.ty { HeadManagerEventType::Done => {} HeadManagerEventType::TransactionStarted => { session.send_transaction_started(); } HeadManagerEventType::TransactionEnded => { session.send_transaction_ended(); } HeadManagerEventType::Stopped => { session.common.state.set(HeadMgrState::Stopped); session.send_stopped(); } HeadManagerEventType::TransactionResult(res) => { if !res.destroyed.get() { res.send(); } } } } } pub struct JayHeadManagerSessionV1 { pub(super) id: JayHeadManagerSessionV1Id, pub(super) mgr: Rc, pub(super) client: Rc, pub(super) tracker: Tracker, pub(super) version: Version, pub(super) common: Rc, pub(super) serial: NumCell, pub(super) heads: CopyHashMap>, pub(super) ext: MgrExts, } impl JayHeadManagerSessionV1 { pub fn announce(self: &Rc, connector: &ConnectorData) { if self.common.in_transaction.get() { return; } if let Err(e) = self.try_announce(connector) { self.client.error(e); } } fn try_announce(self: &Rc, connector: &ConnectorData) -> Result<(), ClientError> { let shared = connector.head_managers.state.clone(); let common = Rc::new(HeadCommon { mgr: self.common.clone(), name: connector.head_managers.name, id: connector.id, removed: Cell::new(false), shared: shared.clone(), snapshot_state: shared.deref().clone(), transaction_state: shared.deref().clone(), pending: Default::default(), }); let obj = Rc::new(JayHeadV1 { id: self.client.new_id()?, client: self.client.clone(), tracker: Default::default(), version: self.version, common: common.clone(), }); track!(self.client, obj); self.client.add_server_obj(&obj); let head = announce_head(self, &obj, connector)?; connector .head_managers .managers .set((self.client.id, self.id), head.clone()); self.heads.set(common.name, head); Ok(()) } fn schedule_event(self: &Rc, ty: HeadManagerEventType) { self.client .state .head_managers_async .push(HeadManagerEvent { mgr: self.clone(), ty, }); } pub(super) fn schedule_done(self: &Rc) { if !self.mgr.done_scheduled.replace(true) { self.serial.fetch_add(1); self.schedule_event(HeadManagerEventType::Done); } } fn schedule_transaction_started(self: &Rc) { self.mgr.done_scheduled.set(true); self.schedule_event(HeadManagerEventType::TransactionStarted); } fn schedule_transaction_ended(self: &Rc) { self.mgr.done_scheduled.set(true); self.schedule_event(HeadManagerEventType::TransactionEnded); } fn schedule_stopped(self: &Rc) { self.mgr.done_scheduled.set(true); self.schedule_event(HeadManagerEventType::Stopped); } fn send_transaction_started(&self) { self.client.event(TransactionStarted { self_id: self.id }); } fn send_transaction_ended(&self) { self.client.event(TransactionEnded { self_id: self.id }); } fn send_stopped(&self) { self.client.event(Stopped { self_id: self.id }); } pub(super) fn send_head_start(&self, head: &JayHeadV1, name: HeadName) { self.client.event(HeadStart { self_id: self.id, head: head.id, name: name.0, }); } pub(super) fn send_head_complete(&self) { self.client.event(HeadComplete { self_id: self.id }); } fn detach(&self, send_removed: bool) { let id = (self.client.id, self.id); for data in self.client.state.connectors.lock().values() { if let Some(head) = data.head_managers.managers.remove(&id) { if send_removed { head.head.send_removed(); } } } self.client.state.head_managers.remove(&id); self.heads.clear(); } fn schedule_transaction_result( self: &Rc, id: JayHeadTransactionResultV1Id, error: Option, ) -> Result<(), JayHeadManagerSessionV1Error> { if error.is_some() { self.common.transaction_failed.set(true); } self.mgr.done_scheduled.set(true); let res = Rc::new(JayHeadTransactionResultV1 { id, client: self.client.clone(), tracker: Default::default(), version: self.version, error: error.map(Rc::new), destroyed: Cell::new(false), }); track!(self.client, res); self.client.add_client_obj(&res)?; self.schedule_event(HeadManagerEventType::TransactionResult(res)); Ok(()) } fn after_transaction_ended(self: &Rc) { let mut to_remove = vec![]; for head in self.heads.lock().values() { if self.client.state.connectors.not_contains(&head.common.id) { head.head.send_removed(); to_remove.push(head.common.name); continue; } let _ = head.common.pending.take(); let tran = &*head.common.transaction_state.borrow(); let shared = &*head.common.shared.borrow(); head.ext.after_transaction(shared, tran); } for name in to_remove { self.heads.remove(&name); } for connector in self.client.state.connectors.lock().values() { if !self.heads.contains(&connector.head_managers.name) { self.announce(connector); } } self.schedule_transaction_ended(); } fn prepare_transaction(&self) -> Result { let mut tran = ConnectorTransaction::new(&self.client.state); for head in self.heads.lock().values() { let current = &*head.common.shared.borrow(); let snapshot = &*head.common.snapshot_state.borrow(); let desired = &*head.common.transaction_state.borrow(); if desired == current || desired == snapshot { continue; } let Some(connector) = self.client.state.connectors.get(&head.common.id) else { return Err(HeadTransactionError::HeadRemoved(head.common.id)); }; let old = connector.state.get(); let mut new = old; new.enabled = desired.connector_enabled; new.mode = desired.mode; new.non_desktop_override = desired.override_non_desktop; new.format = desired.format; new.color_space = desired.color_space; new.eotf = desired.eotf; if old == new { continue; } if current.monitor_info != desired.monitor_info { return Err(HeadTransactionError::MonitorChanged(head.common.id)); } tran.add(&connector.connector, new)?; } Ok(tran.prepare()?) } fn commit_transaction(&self) -> Result<(), HeadTransactionError> { self.prepare_transaction()?.apply()?.commit(); Ok(()) } } impl JayHeadManagerSessionV1RequestHandler for JayHeadManagerSessionV1 { type Error = JayHeadManagerSessionV1Error; fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { self.common.assert_stopped()?; self.mgr.sessions.fetch_sub(1); self.client.remove_obj(self)?; Ok(()) } fn bind_extension(&self, req: BindExtension<'_>, _slf: &Rc) -> Result<(), Self::Error> { if self.common.state.get() != HeadMgrState::Init { return Err(JayHeadManagerSessionV1Error::AlreadyStarted); } let Some(ext) = HeadExtension::from_linear(req.name as usize) else { return Err(JayHeadManagerSessionV1Error::UnknownExtension(req.name)); }; bind_extension(self, ext, req.name, req.version, req.id)?; Ok(()) } fn stop(&self, _req: Stop, slf: &Rc) -> Result<(), Self::Error> { if self.common.in_transaction.get() { return Err(JayHeadManagerSessionV1Error::InTransaction); } match self.common.state.get() { HeadMgrState::Init | HeadMgrState::Started => {} HeadMgrState::StopScheduled | HeadMgrState::Stopped => return Ok(()), } self.common.state.set(HeadMgrState::StopScheduled); self.detach(true); self.serial.fetch_add(1); slf.schedule_stopped(); Ok(()) } fn start(&self, _req: Start, slf: &Rc) -> Result<(), Self::Error> { if self.common.state.get() != HeadMgrState::Init { return Err(JayHeadManagerSessionV1Error::AlreadyStarted); } self.common.state.set(HeadMgrState::Started); self.client .state .head_managers .set((self.client.id, self.id), slf.clone()); for connector in self.client.state.connectors.lock().values() { slf.announce(connector); } slf.schedule_done(); Ok(()) } fn begin_transaction(&self, _req: BeginTransaction, slf: &Rc) -> Result<(), Self::Error> { if self.common.in_transaction.replace(true) { return Err(JayHeadManagerSessionV1Error::AlreadyInTransaction); } for head in self.heads.lock().values() { let snapshot = head.common.shared.borrow().clone(); *head.common.transaction_state.borrow_mut() = snapshot.clone(); *head.common.snapshot_state.borrow_mut() = snapshot; head.common.pending.borrow_mut().clear(); } self.common.transaction_failed.set(false); slf.schedule_transaction_started(); Ok(()) } fn rollback_transaction( &self, _req: RollbackTransaction, slf: &Rc, ) -> Result<(), Self::Error> { if !self.common.in_transaction.replace(false) { return Err(JayHeadManagerSessionV1Error::NotInTransaction); } slf.after_transaction_ended(); Ok(()) } fn apply_changes(&self, req: ApplyChanges, slf: &Rc) -> Result<(), Self::Error> { if !self.common.in_transaction.get() { return Err(JayHeadManagerSessionV1Error::NotInTransaction); } macro_rules! schedule_result { ($res:expr) => { slf.schedule_transaction_result(req.result, Some($res))?; return Ok(()); }; } if self.common.transaction_failed.get() { schedule_result!(HeadTransactionError::AlreadyFailed); } bitflags! { ToSend: u32; CORE_INFO = 1 << 0, COMPOSITOR_SPACE_INFO_FULL = 1 << 1, COMPOSITOR_SPACE_INFO_POS = 1 << 2, COMPOSITOR_SPACE_INFO_SIZE = 1 << 3, COMPOSITOR_SPACE_INFO_TRANSFORM = 1 << 4, COMPOSITOR_SPACE_INFO_SCALE = 1 << 5, MODE_INFO = 1 << 6, NON_DESKTOP_INFO = 1 << 7, VRR_MODE_INFO = 1 << 8, TEARING_MODE_INFO = 1 << 9, FORMAT_INFO = 1 << 10, DRM_COLOR_SPACE_INFO = 1 << 11, BRIGHTNESS_INFO = 1 << 12, COMPOSITOR_SPACE_INFO_ENABLED = 1 << 13, } for head in self.heads.lock().values() { let pending = mem::take(&mut *head.common.pending.borrow_mut()); let snapshot = &*head.common.snapshot_state.borrow(); let state = &mut *head.common.transaction_state.borrow_mut(); let mut to_send = ToSend::default(); for op in pending { match op { HeadOp::SetPosition(x, y) => { state.position = (x, y); to_send |= COMPOSITOR_SPACE_INFO_POS; } HeadOp::SetConnectorEnabled(enabled) => { state.connector_enabled = enabled; state.update_in_compositor_space(snapshot.wl_output); to_send |= COMPOSITOR_SPACE_INFO_FULL; to_send |= COMPOSITOR_SPACE_INFO_ENABLED; to_send |= CORE_INFO; } HeadOp::SetTransform(t) => { state.transform = t; state.update_size(); to_send |= COMPOSITOR_SPACE_INFO_TRANSFORM; to_send |= COMPOSITOR_SPACE_INFO_SIZE; } HeadOp::SetScale(s) => { state.scale = s; state.update_size(); to_send |= COMPOSITOR_SPACE_INFO_SCALE; to_send |= COMPOSITOR_SPACE_INFO_SIZE; } HeadOp::SetMode(i) => { state.mode = snapshot.monitor_info.as_deref().unwrap().modes[i]; state.update_size(); to_send |= MODE_INFO; to_send |= COMPOSITOR_SPACE_INFO_SIZE; } HeadOp::SetNonDesktopOverride(m) => { state.override_non_desktop = m; state.update_in_compositor_space(snapshot.wl_output); to_send |= COMPOSITOR_SPACE_INFO_FULL; to_send |= CORE_INFO; to_send |= NON_DESKTOP_INFO; } HeadOp::SetVrrMode(m) => { state.vrr_mode = m; to_send |= VRR_MODE_INFO; } HeadOp::SetTearingMode(m) => { state.tearing_mode = m; to_send |= TEARING_MODE_INFO; } HeadOp::SetFormat(f) => { state.format = f; to_send |= FORMAT_INFO; } HeadOp::SetEotf(e) => { state.eotf = e; to_send |= DRM_COLOR_SPACE_INFO; to_send |= BRIGHTNESS_INFO; } HeadOp::SetColorSpace(c) => { state.color_space = c; to_send |= DRM_COLOR_SPACE_INFO; } HeadOp::SetBrightness(b) => { state.brightness = b; to_send |= BRIGHTNESS_INFO; } } } if to_send.contains(CORE_INFO) && let Some(i) = &head.ext.core_info_v1 { i.send_wl_output(state); } if let Some(i) = &head.ext.compositor_space_info_v1 { if to_send.contains(COMPOSITOR_SPACE_INFO_ENABLED) { i.send_enabled(state); } if to_send.contains(COMPOSITOR_SPACE_INFO_FULL) { i.send_inside_outside(state); } else { if to_send.contains(COMPOSITOR_SPACE_INFO_POS) { i.send_position(state); } if to_send.contains(COMPOSITOR_SPACE_INFO_SIZE) { i.send_size(state); } if to_send.contains(COMPOSITOR_SPACE_INFO_TRANSFORM) { i.send_transform(state); } if to_send.contains(COMPOSITOR_SPACE_INFO_SCALE) { i.send_scale(state); } } } if to_send.contains(MODE_INFO) && let Some(i) = &head.ext.mode_info_v1 { i.send_mode(state); } if to_send.contains(NON_DESKTOP_INFO) && let Some(i) = &head.ext.non_desktop_info_v1 { i.send_state(state); } if to_send.contains(VRR_MODE_INFO) && let Some(i) = &head.ext.jay_vrr_mode_info_v1 { i.send_mode(state); } if to_send.contains(TEARING_MODE_INFO) && let Some(i) = &head.ext.jay_tearing_mode_info_v1 { i.send_mode(state); } if to_send.contains(FORMAT_INFO) && let Some(i) = &head.ext.format_info_v1 { i.send_format(state); } if to_send.contains(DRM_COLOR_SPACE_INFO) && let Some(i) = &head.ext.drm_color_space_info_v1 { i.send_state(state); } if to_send.contains(BRIGHTNESS_INFO) && let Some(i) = &head.ext.brightness_info_v1 { i.send_brightness(state); } } slf.schedule_transaction_result(req.result, None)?; Ok(()) } fn test_transaction(&self, req: TestTransaction, slf: &Rc) -> Result<(), Self::Error> { if !self.common.in_transaction.get() { return Err(JayHeadManagerSessionV1Error::NotInTransaction); } let res = self.prepare_transaction().err(); slf.schedule_transaction_result(req.result, res)?; Ok(()) } fn commit_transaction( &self, req: CommitTransaction, slf: &Rc, ) -> Result<(), Self::Error> { if !self.common.in_transaction.replace(false) { return Err(JayHeadManagerSessionV1Error::NotInTransaction); } slf.after_transaction_ended(); if let Err(e) = self.commit_transaction() { slf.schedule_transaction_result(req.result, Some(e))?; return Ok(()); } for head in self.heads.lock().values() { let desired = &*head.common.transaction_state.borrow(); if let Some(output) = self.client.state.outputs.get(&head.common.id) && let Some(node) = &output.node { node.set_position(desired.position.0, desired.position.1); node.set_preferred_scale(desired.scale); node.update_transform(desired.transform); node.set_vrr_mode(VrrMode::from_config(desired.vrr_mode).unwrap()); node.set_tearing_mode(TearingMode::from_config(desired.tearing_mode).unwrap()); node.set_brightness(desired.brightness); } else if let Some(mi) = &desired.monitor_info { let pos = &self.client.state.persistent_output_states; let pos = pos.lock().entry(mi.output_id.clone()).or_default().clone(); pos.pos.set(desired.position); pos.scale.set(desired.scale); pos.transform.set(desired.transform); pos.vrr_mode .set(VrrMode::from_config(desired.vrr_mode).unwrap()); pos.tearing_mode .set(TearingMode::from_config(desired.tearing_mode).unwrap()); pos.brightness.set(desired.brightness); } } slf.schedule_transaction_result(req.result, None)?; Ok(()) } } object_base! { self = JayHeadManagerSessionV1; version = self.version; } impl Object for JayHeadManagerSessionV1 { fn break_loops(&self) { self.detach(false); } } simple_add_obj!(JayHeadManagerSessionV1); #[derive(Debug, Error)] pub enum JayHeadManagerSessionV1Error { #[error(transparent)] ClientError(Box), #[error(transparent)] Common(#[from] HeadCommonError), #[error("Manager was already started")] AlreadyStarted, #[error("There is no extension with name {}", .0)] UnknownExtension(u32), #[error("The extension with name {} is already bound", .0)] AlreadyBound(u32), #[error("The extension with name {} does not support version {}", .0, .1 .0)] UnsupportedVersion(u32, Version), #[error("There already is an active transaction")] AlreadyInTransaction, #[error("There is no active transaction")] NotInTransaction, #[error("There is an active transaction")] InTransaction, } efrom!(JayHeadManagerSessionV1Error, ClientError);