1
0
Fork 0
forked from wry/wry
wry/src/ifs/head_management/jay_head_manager_session_v1.rs
2026-02-25 12:06:04 +01:00

613 lines
22 KiB
Rust

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},
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<JayHeadManagerSessionV1>,
ty: HeadManagerEventType,
}
pub(super) enum HeadManagerEventType {
Done,
TransactionStarted,
TransactionEnded,
TransactionResult(Rc<JayHeadTransactionResultV1>),
Stopped,
}
pub async fn handle_jay_head_manager_done(state: Rc<State>) {
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<JayHeadManagerV1>,
pub(super) client: Rc<Client>,
pub(super) tracker: Tracker<Self>,
pub(super) version: Version,
pub(super) common: Rc<HeadMgrCommon>,
pub(super) serial: NumCell<u64>,
pub(super) heads: CopyHashMap<HeadName, Rc<Head>>,
pub(super) ext: MgrExts,
}
impl JayHeadManagerSessionV1 {
pub fn announce(self: &Rc<Self>, 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<Self>, 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<Self>, ty: HeadManagerEventType) {
self.client
.state
.head_managers_async
.push(HeadManagerEvent {
mgr: self.clone(),
ty,
});
}
pub(super) fn schedule_done(self: &Rc<Self>) {
if !self.mgr.done_scheduled.replace(true) {
self.serial.fetch_add(1);
self.schedule_event(HeadManagerEventType::Done);
}
}
fn schedule_transaction_started(self: &Rc<Self>) {
self.mgr.done_scheduled.set(true);
self.schedule_event(HeadManagerEventType::TransactionStarted);
}
fn schedule_transaction_ended(self: &Rc<Self>) {
self.mgr.done_scheduled.set(true);
self.schedule_event(HeadManagerEventType::TransactionEnded);
}
fn schedule_stopped(self: &Rc<Self>) {
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<Self>,
id: JayHeadTransactionResultV1Id,
error: Option<HeadTransactionError>,
) -> 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<Self>) {
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<PreparedConnectorTransaction, HeadTransactionError> {
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.borrow().clone();
let mut new = old.clone();
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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>,
) -> 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<Self>) -> 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<Self>) -> 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<Self>,
) -> 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(&desired.vrr_mode);
node.set_tearing_mode(&desired.tearing_mode);
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(desired.vrr_mode);
pos.tearing_mode.set(desired.tearing_mode);
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<ClientError>),
#[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);