head-management: add infrastructure
This commit is contained in:
parent
078c59d730
commit
8356dd5d5c
28 changed files with 1791 additions and 21 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
|
@ -627,6 +627,7 @@ dependencies = [
|
|||
"tiny-skia",
|
||||
"tracy-client-sys",
|
||||
"uapi",
|
||||
"with_builtin_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1740,6 +1741,26 @@ dependencies = [
|
|||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "with_builtin_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24deb3cd6e530e7617b12b1f0f1ce160a3a71d92feb351c4db5156d1d10e398a"
|
||||
dependencies = [
|
||||
"with_builtin_macros-proc_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "with_builtin_macros-proc_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2259ae9b1285596f1ee52ce8f627013c65853d4d7f271cb10bfe2d048769804a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ tiny-skia = { version = "0.11.4", default-features = false, features = ["std"] }
|
|||
regex = "1.11.1"
|
||||
cfg-if = "1.0.0"
|
||||
opera = "1.0.1"
|
||||
with_builtin_macros = "0.1.0"
|
||||
|
||||
[build-dependencies]
|
||||
repc = "0.1.1"
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ bitflags! {
|
|||
CAP_INPUT_METHOD = 1 << 10,
|
||||
CAP_WORKSPACE = 1 << 11,
|
||||
CAP_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
|
||||
CAP_HEAD_MANAGER = 1 << 13,
|
||||
}
|
||||
|
||||
pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use {
|
|||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
ext_image_capture_source_v1::ExtImageCaptureSourceV1,
|
||||
ext_image_copy::ext_image_copy_capture_session_v1::ExtImageCopyCaptureSessionV1,
|
||||
head_management::jay_head_error_v1::JayHeadErrorV1,
|
||||
ipc::{
|
||||
data_control::{
|
||||
ext_data_control_source_v1::ExtDataControlSourceV1,
|
||||
|
|
@ -41,12 +42,13 @@ use {
|
|||
},
|
||||
wire::{
|
||||
ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
|
||||
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayOutputId,
|
||||
JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
|
||||
WpImageDescriptionV1Id, WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId,
|
||||
XdgSurfaceId, XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id,
|
||||
ZwpPrimarySelectionSourceV1Id, ZwpTabletToolV2Id,
|
||||
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayHeadErrorV1Id,
|
||||
JayOutputId, JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId,
|
||||
WlDataSourceId, WlOutputId, WlPointerId, WlRegionId, WlRegistryId, WlSeatId,
|
||||
WlSurfaceId, WpDrmLeaseConnectorV1Id, WpImageDescriptionV1Id,
|
||||
WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId, XdgSurfaceId,
|
||||
XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id,
|
||||
ZwpTabletToolV2Id,
|
||||
},
|
||||
},
|
||||
std::{cell::RefCell, rc::Rc},
|
||||
|
|
@ -87,6 +89,7 @@ pub struct Objects {
|
|||
pub ext_workspace_groups:
|
||||
CopyHashMap<ExtWorkspaceGroupHandleV1Id, Rc<ExtWorkspaceGroupHandleV1>>,
|
||||
pub wp_image_description: CopyHashMap<WpImageDescriptionV1Id, Rc<WpImageDescriptionV1>>,
|
||||
pub jay_head_errors: CopyHashMap<JayHeadErrorV1Id, Rc<JayHeadErrorV1>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +129,7 @@ impl Objects {
|
|||
ext_data_sources: Default::default(),
|
||||
ext_workspace_groups: Default::default(),
|
||||
wp_image_description: Default::default(),
|
||||
jay_head_errors: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
@ -168,6 +172,7 @@ impl Objects {
|
|||
self.ext_copy_sessions.clear();
|
||||
self.ext_data_sources.clear();
|
||||
self.ext_workspace_groups.clear();
|
||||
self.jay_head_errors.clear();
|
||||
}
|
||||
|
||||
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@ use {
|
|||
format::XRGB8888,
|
||||
globals::Globals,
|
||||
ifs::{
|
||||
head_management::{
|
||||
HeadManagers, HeadState, jay_head_manager_session_v1::handle_jay_head_manager_done,
|
||||
},
|
||||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||
wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
wl_seat::handle_position_hint_requests,
|
||||
|
|
@ -62,6 +65,7 @@ use {
|
|||
numcell::NumCell,
|
||||
oserror::OsError,
|
||||
queue::AsyncQueue,
|
||||
rc_eq::RcEq,
|
||||
refcounted::RefCounted,
|
||||
run_toplevel::RunToplevel,
|
||||
tri::Try,
|
||||
|
|
@ -339,6 +343,9 @@ fn start_compositor2(
|
|||
node_at_tree: Default::default(),
|
||||
position_hint_requests: Default::default(),
|
||||
backend_connector_state_serials: Default::default(),
|
||||
head_names: Default::default(),
|
||||
head_managers: Default::default(),
|
||||
head_managers_async: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -523,6 +530,11 @@ fn start_global_event_handlers(state: &Rc<State>) -> Vec<SpawnedFuture<()>> {
|
|||
"position hint requests",
|
||||
handle_position_hint_requests(state.clone()),
|
||||
),
|
||||
eng.spawn2(
|
||||
"jay head manager send done",
|
||||
Phase::Layout,
|
||||
handle_jay_head_manager_done(state.clone()),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -602,9 +614,6 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
brightness: Cell::new(None),
|
||||
});
|
||||
let connector = Rc::new(DummyOutput {
|
||||
id: state.connector_ids.next(),
|
||||
}) as Rc<dyn Connector>;
|
||||
let mode = backend::Mode {
|
||||
width: 0,
|
||||
height: 0,
|
||||
|
|
@ -622,11 +631,21 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
color_space: Default::default(),
|
||||
transfer_function: Default::default(),
|
||||
};
|
||||
let id = state.connector_ids.next();
|
||||
let connector = Rc::new(DummyOutput { id }) as Rc<dyn Connector>;
|
||||
let name = Rc::new("Dummy".to_string());
|
||||
let head_name = state.head_names.next();
|
||||
let head_state = HeadState {
|
||||
name: RcEq(name.clone()),
|
||||
wl_output: None,
|
||||
monitor_info: None,
|
||||
};
|
||||
let connector_data = Rc::new(ConnectorData {
|
||||
id,
|
||||
connector,
|
||||
handler: Cell::new(None),
|
||||
connected: Cell::new(true),
|
||||
name: "Dummy".to_string(),
|
||||
name,
|
||||
drm_dev: None,
|
||||
async_event: Default::default(),
|
||||
damaged: Cell::new(false),
|
||||
|
|
@ -634,6 +653,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
needs_vblank_emulation: Cell::new(false),
|
||||
damage_intersect: Default::default(),
|
||||
state: Cell::new(backend_state),
|
||||
head_managers: HeadManagers::new(head_name, head_state),
|
||||
});
|
||||
let schedule = Rc::new(OutputSchedule::new(
|
||||
&state.ring,
|
||||
|
|
|
|||
|
|
@ -1175,7 +1175,7 @@ impl ConfigProxyHandler {
|
|||
fn handle_connector_name(&self, connector: Connector) -> Result<(), CphError> {
|
||||
let connector = self.get_connector(connector)?;
|
||||
self.respond(Response::GetConnectorName {
|
||||
name: connector.name.clone(),
|
||||
name: connector.name.deref().clone(),
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use {
|
|||
ext_image_copy::ext_image_copy_capture_manager_v1::ExtImageCopyCaptureManagerV1Global,
|
||||
ext_output_image_capture_source_manager_v1::ExtOutputImageCaptureSourceManagerV1Global,
|
||||
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
|
||||
head_management::jay_head_manager_v1::JayHeadManagerV1Global,
|
||||
ipc::{
|
||||
data_control::{
|
||||
ext_data_control_manager_v1::ExtDataControlManagerV1Global,
|
||||
|
|
@ -225,6 +226,7 @@ impl Globals {
|
|||
add_singleton!(ExtWorkspaceManagerV1Global);
|
||||
add_singleton!(WpColorManagerV1Global);
|
||||
add_singleton!(XdgToplevelTagManagerV1Global);
|
||||
add_singleton!(JayHeadManagerV1Global);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ pub mod ext_image_copy;
|
|||
pub mod ext_output_image_capture_source_manager_v1;
|
||||
pub mod ext_session_lock_manager_v1;
|
||||
pub mod ext_session_lock_v1;
|
||||
pub mod head_management;
|
||||
pub mod ipc;
|
||||
pub mod jay_client_query;
|
||||
pub mod jay_color_management;
|
||||
|
|
|
|||
191
src/ifs/head_management.rs
Normal file
191
src/ifs/head_management.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
use {
|
||||
crate::{
|
||||
backend::{ConnectorId, MonitorInfo, transaction::BackendConnectorTransactionError},
|
||||
client::ClientId,
|
||||
globals::GlobalName,
|
||||
ifs::head_management::{
|
||||
head_management_macros::HeadExts, jay_head_manager_session_v1::JayHeadManagerSessionV1,
|
||||
jay_head_v1::JayHeadV1,
|
||||
},
|
||||
state::OutputData,
|
||||
utils::{copyhashmap::CopyHashMap, hash_map_ext::HashMapExt, rc_eq::RcEq},
|
||||
wire::JayHeadManagerSessionV1Id,
|
||||
},
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
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<JayHeadManagerSessionV1>,
|
||||
common: Rc<HeadCommon>,
|
||||
head: Rc<JayHeadV1>,
|
||||
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<HeadMgrCommon>,
|
||||
name: HeadName,
|
||||
id: ConnectorId,
|
||||
removed: Cell<bool>,
|
||||
|
||||
shared: Rc<RefCell<HeadState>>,
|
||||
snapshot_state: RefCell<HeadState>,
|
||||
transaction_state: RefCell<HeadState>,
|
||||
pending: RefCell<Vec<HeadOp>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct HeadState {
|
||||
pub name: RcEq<String>,
|
||||
pub wl_output: Option<GlobalName>,
|
||||
pub monitor_info: Option<RcEq<MonitorInfo>>,
|
||||
}
|
||||
|
||||
enum HeadOp {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
|
||||
enum HeadMgrState {
|
||||
#[default]
|
||||
Init,
|
||||
Started,
|
||||
StopScheduled,
|
||||
Stopped,
|
||||
}
|
||||
|
||||
struct HeadMgrCommon {
|
||||
state: Cell<HeadMgrState>,
|
||||
in_transaction: Cell<bool>,
|
||||
transaction_failed: Cell<bool>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
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 {
|
||||
name: HeadName,
|
||||
state: Rc<RefCell<HeadState>>,
|
||||
managers: CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc<Head>>,
|
||||
}
|
||||
|
||||
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, output: &OutputData) {
|
||||
let state = &mut *self.state.borrow_mut();
|
||||
state.monitor_info = Some(RcEq(output.monitor_info.clone()));
|
||||
if let Some(n) = &output.node {
|
||||
state.wl_output = Some(n.global.name);
|
||||
}
|
||||
for head in self.managers.lock().values() {
|
||||
skip_in_transaction!(head);
|
||||
if let Some(ext) = &head.ext.core_info_v1 {
|
||||
ext.send_wl_output(state);
|
||||
head.session.schedule_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_output_disconnected(&self) {
|
||||
let state = &mut *self.state.borrow_mut();
|
||||
state.wl_output = None;
|
||||
state.monitor_info = None;
|
||||
for head in self.managers.lock().values() {
|
||||
skip_in_transaction!(head);
|
||||
if let Some(ext) = &head.ext.core_info_v1 {
|
||||
ext.send_wl_output(state);
|
||||
head.session.schedule_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
392
src/ifs/head_management/head_management_macros.rs
Normal file
392
src/ifs/head_management/head_management_macros.rs
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
use {
|
||||
crate::{
|
||||
client::ClientError,
|
||||
ifs::head_management::{
|
||||
Head, HeadState,
|
||||
jay_head_manager_session_v1::{JayHeadManagerSessionV1, JayHeadManagerSessionV1Error},
|
||||
jay_head_manager_v1::JayHeadManagerV1,
|
||||
jay_head_v1::JayHeadV1,
|
||||
},
|
||||
object::{ObjectId, Version},
|
||||
state::ConnectorData,
|
||||
utils::clonecell::CloneCell,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
macro_rules! error_ {
|
||||
($error_name:ident, $($tt:tt)*) => {
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum $error_name {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<crate::client::ClientError>),
|
||||
#[error(transparent)]
|
||||
Common(#[from] super::super::HeadCommonError),
|
||||
$($tt)*
|
||||
}
|
||||
efrom!($error_name, ClientError, crate::client::ClientError);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_head_ext {
|
||||
(
|
||||
$snake:ident,
|
||||
$camel:ident,
|
||||
$macro_name:ident,
|
||||
$mgr_module:ident,
|
||||
$mgr_name:ident,
|
||||
$mgr_id_name:ident,
|
||||
$head_module:ident,
|
||||
$head_name:ident,
|
||||
$head_id_name:ident,
|
||||
$version:expr,
|
||||
$(filter = $filter:ident,)?
|
||||
$(after_announce = $after_announce:ident,)?
|
||||
$(after_transaction = $after_transaction:ident,)?
|
||||
) => {
|
||||
pub(in super::super) struct $mgr_name {
|
||||
pub(in super::super) id: crate::wire::$mgr_id_name,
|
||||
pub(in super::super) client: std::rc::Rc<crate::client::Client>,
|
||||
pub(in super::super) tracker: crate::leaks::Tracker<Self>,
|
||||
pub(in super::super) version: crate::object::Version,
|
||||
pub(in super::super) common: std::rc::Rc<super::super::HeadMgrCommon>,
|
||||
}
|
||||
|
||||
impl $mgr_name {
|
||||
pub(in super::super) const VERSION: crate::object::Version = crate::object::Version($version);
|
||||
pub(in super::super) const NAME: &str = concat!("jay_head_manager_ext_", stringify!($snake));
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = $mgr_name;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl crate::object::Object for $mgr_name {}
|
||||
|
||||
simple_add_obj!($mgr_name);
|
||||
|
||||
pub(in super::super) struct $head_name {
|
||||
pub(in super::super) id: crate::wire::$head_id_name,
|
||||
pub(in super::super) client: std::rc::Rc<crate::client::Client>,
|
||||
pub(in super::super) tracker: crate::leaks::Tracker<Self>,
|
||||
pub(in super::super) version: crate::object::Version,
|
||||
pub(in super::super) common: std::rc::Rc<super::super::HeadCommon>,
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = $head_name;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl crate::object::Object for $head_name {}
|
||||
|
||||
simple_add_obj!($head_name);
|
||||
|
||||
impl $mgr_name {
|
||||
pub(in super::super) fn announce(
|
||||
&self,
|
||||
#[allow(clippy::allow_attributes, unused_variables)]
|
||||
connector: &crate::state::ConnectorData,
|
||||
common: &std::rc::Rc<super::super::HeadCommon>,
|
||||
) -> Result<Option<std::rc::Rc<$head_name>>, crate::client::ClientError> {
|
||||
$(
|
||||
if !self.$filter(connector, common) {
|
||||
return Ok(None);
|
||||
}
|
||||
)?
|
||||
let obj = std::rc::Rc::new($head_name {
|
||||
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);
|
||||
self.send_head(&obj);
|
||||
Ok(Some(obj))
|
||||
}
|
||||
|
||||
fn send_head(&self, obj: &$head_name) {
|
||||
self.client.event(crate::wire::$mgr_module::Head {
|
||||
self_id: self.id,
|
||||
head: obj.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl $head_name {
|
||||
pub(in super::super) fn after_announce_wrapper(
|
||||
&self,
|
||||
#[allow(clippy::allow_attributes, unused_variables)]
|
||||
shared: &super::super::HeadState,
|
||||
) {
|
||||
$(
|
||||
self.$after_announce(shared);
|
||||
)?
|
||||
}
|
||||
|
||||
pub(in super::super) fn after_transaction_wrapper(
|
||||
&self,
|
||||
#[allow(clippy::allow_attributes, unused_variables)]
|
||||
shared: &super::super::HeadState,
|
||||
#[allow(clippy::allow_attributes, unused_variables)]
|
||||
tran: &super::super::HeadState,
|
||||
) {
|
||||
$(
|
||||
self.$after_transaction(shared, tran);
|
||||
)?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
macro_rules! declare_extension_type_macro {
|
||||
(
|
||||
($dollar:tt),
|
||||
$snake:ident,
|
||||
$camel:ident,
|
||||
$macro_name:ident,
|
||||
$macro_name_int:ident,
|
||||
$mgr_module:ident,
|
||||
$mgr_name:ident,
|
||||
$mgr_id_name:ident,
|
||||
$mgr_request_handler:ident,
|
||||
$head_module:ident,
|
||||
$head_name:ident,
|
||||
$head_id_name:ident,
|
||||
$head_request_handler:ident,
|
||||
$error_name:ident,
|
||||
) => {
|
||||
macro_rules! $macro_name {
|
||||
(
|
||||
version = $version:expr,
|
||||
$dollar(filter = $filter:ident,)?
|
||||
$dollar(after_announce = $after_announce:ident,)?
|
||||
$dollar(after_transaction = $after_transaction:ident,)?
|
||||
) => {
|
||||
$macro_name_int! {
|
||||
($),
|
||||
version = $version,
|
||||
$dollar(filter = $filter,)?
|
||||
$dollar(after_announce = $after_announce,)?
|
||||
$dollar(after_transaction = $after_transaction,)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! $macro_name_int {
|
||||
(
|
||||
($dollar2:tt),
|
||||
version = $version:expr,
|
||||
$dollar(filter = $filter:ident,)?
|
||||
$dollar(after_announce = $after_announce:ident,)?
|
||||
$dollar(after_transaction = $after_transaction:ident,)?
|
||||
) => {
|
||||
impl_head_ext!(
|
||||
$snake,
|
||||
$camel,
|
||||
$macro_name,
|
||||
$mgr_module,
|
||||
$mgr_name,
|
||||
$mgr_id_name,
|
||||
$head_module,
|
||||
$head_name,
|
||||
$head_id_name,
|
||||
$version,
|
||||
$dollar(filter = $filter,)?
|
||||
$dollar(after_announce = $after_announce,)?
|
||||
$dollar(after_transaction = $after_transaction,)?
|
||||
);
|
||||
|
||||
macro_rules! head_common_req {
|
||||
() => {
|
||||
head_common_req_!($snake);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! mgr_common_req {
|
||||
() => {
|
||||
mgr_common_req_!($snake);
|
||||
}
|
||||
}
|
||||
|
||||
type ErrorName = $error_name;
|
||||
type MgrName = $mgr_name;
|
||||
type HeadName = $head_name;
|
||||
|
||||
macro_rules! error {
|
||||
($dollar2($tt:tt)*) => {
|
||||
error_!($error_name, $dollar2($tt)*);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mgr_common_req_ {
|
||||
($snake:ident) => {
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
fn destroy(
|
||||
&self,
|
||||
_req: crate::wire::#{concat_idents!(jay_head_manager_ext_, $snake)}::Destroy,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.common.assert_stopped()?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! head_common_req_ {
|
||||
($snake:ident) => {
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
fn destroy(
|
||||
&self,
|
||||
_req: crate::wire::#{concat_idents!(jay_head_ext_, $snake)}::Destroy,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.common.assert_removed()?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! declare_extensions {
|
||||
($($snake:ident: $camel:ident,)*) => {
|
||||
#[derive(linearize::Linearize)]
|
||||
pub(super) enum HeadExtension {
|
||||
$($camel,)*
|
||||
}
|
||||
|
||||
pub(super) fn send_available_extensions(mgr: &JayHeadManagerV1) {
|
||||
use linearize::Linearize;
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
$(
|
||||
type $camel = super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadManagerExt, $camel)};
|
||||
mgr.send_extension(
|
||||
HeadExtension::$camel.linearize() as _,
|
||||
$camel::NAME,
|
||||
$camel::VERSION,
|
||||
);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn announce_head(
|
||||
session: &Rc<JayHeadManagerSessionV1>,
|
||||
head: &Rc<JayHeadV1>,
|
||||
connector: &ConnectorData,
|
||||
) -> Result<Rc<Head>, ClientError> {
|
||||
session.send_head_start(head, connector.head_managers.name);
|
||||
let head = super::Head {
|
||||
session: session.clone(),
|
||||
common: head.common.clone(),
|
||||
head: head.clone(),
|
||||
ext: HeadExts {
|
||||
$(
|
||||
$snake: match session.ext.$snake.get() {
|
||||
Some(f) => f.announce(connector, &head.common)?,
|
||||
_ => None,
|
||||
},
|
||||
)*
|
||||
},
|
||||
};
|
||||
session.send_head_complete();
|
||||
let shared = &*connector.head_managers.state.borrow();
|
||||
$(
|
||||
if let Some(ext) = &head.ext.$snake {
|
||||
ext.after_announce_wrapper(shared);
|
||||
}
|
||||
)*
|
||||
Ok(Rc::new(head))
|
||||
}
|
||||
|
||||
pub(super) fn bind_extension(session: &JayHeadManagerSessionV1, ext: HeadExtension, name: u32, version: u32, id: ObjectId) -> Result<(), JayHeadManagerSessionV1Error> {
|
||||
match ext {
|
||||
$(
|
||||
HeadExtension::$camel => {
|
||||
if session.ext.$snake.is_some() {
|
||||
return Err(JayHeadManagerSessionV1Error::AlreadyBound(name));
|
||||
}
|
||||
let version = Version(version);
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
type T = super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadManagerExt, $camel)};
|
||||
if version > T::VERSION {
|
||||
return Err(JayHeadManagerSessionV1Error::UnsupportedVersion(name, version));
|
||||
}
|
||||
let obj = Rc::new(T {
|
||||
id: id.into(),
|
||||
client: session.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
common: session.common.clone(),
|
||||
});
|
||||
}
|
||||
track!(session.client, obj);
|
||||
session.client.add_client_obj(&obj)?;
|
||||
session.ext.$snake.set(Some(obj));
|
||||
}
|
||||
)*
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
#[derive(Default)]
|
||||
pub(super) struct MgrExts {
|
||||
$(
|
||||
pub(super) $snake: CloneCell<Option<Rc<super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadManagerExt, $camel)}>>>,
|
||||
)*
|
||||
}
|
||||
|
||||
pub(super) struct HeadExts {
|
||||
$(
|
||||
pub(super) $snake: Option<Rc<super::jay_head_ext::#{concat_idents!(jay_head_ext_, $snake)}::#{concat_idents!(JayHeadExt, $camel)}>>,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl HeadExts {
|
||||
pub(super) fn after_transaction(&self, shared: &HeadState, tran: &HeadState) {
|
||||
$(
|
||||
if let Some(ext) = &self.$snake {
|
||||
ext.after_transaction_wrapper(shared, tran);
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
with_builtin_macros::with_eager_expansions! {
|
||||
$(
|
||||
declare_extension_type_macro!(
|
||||
($),
|
||||
$snake,
|
||||
$camel,
|
||||
#{concat_idents!(impl_, $snake)},
|
||||
#{concat_idents!(impl_, $snake, _int)},
|
||||
#{concat_idents!(jay_head_manager_ext_, $snake)},
|
||||
#{concat_idents!(JayHeadManagerExt, $camel)},
|
||||
#{concat_idents!(JayHeadManagerExt, $camel, Id)},
|
||||
#{concat_idents!(JayHeadManagerExt, $camel, RequestHandler)},
|
||||
#{concat_idents!(jay_head_ext_, $snake)},
|
||||
#{concat_idents!(JayHeadExt, $camel)},
|
||||
#{concat_idents!(JayHeadExt, $camel, Id)},
|
||||
#{concat_idents!(JayHeadExt, $camel, RequestHandler)},
|
||||
#{concat_idents!(JayHeadExt, $camel, Error)},
|
||||
);
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare_extensions! {
|
||||
core_info_v1: CoreInfoV1,
|
||||
}
|
||||
77
src/ifs/head_management/jay_head_error_v1.rs
Normal file
77
src/ifs/head_management/jay_head_error_v1.rs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::head_management::HeadTransactionError,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
utils::errorfmt::ErrorFmt,
|
||||
wire::{JayHeadErrorV1Id, jay_head_error_v1::*},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct JayHeadErrorV1 {
|
||||
pub(super) id: JayHeadErrorV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) error: Rc<HeadTransactionError>,
|
||||
}
|
||||
|
||||
impl JayHeadErrorV1 {
|
||||
pub fn send(&self) {
|
||||
let msg = ErrorFmt(&*self.error).to_string();
|
||||
self.send_message(&msg);
|
||||
match &*self.error {
|
||||
HeadTransactionError::HeadRemoved(_) | HeadTransactionError::MonitorChanged(_) => {
|
||||
self.send_out_of_date();
|
||||
}
|
||||
HeadTransactionError::AlreadyFailed => {
|
||||
self.send_already_failed();
|
||||
}
|
||||
HeadTransactionError::Backend(_) => {}
|
||||
}
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
fn send_message(&self, message: &str) {
|
||||
self.client.event(Message {
|
||||
self_id: self.id,
|
||||
msg: message,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_out_of_date(&self) {
|
||||
self.client.event(OutOfDate { self_id: self.id });
|
||||
}
|
||||
|
||||
fn send_already_failed(&self) {
|
||||
self.client.event(AlreadyFailed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl JayHeadErrorV1RequestHandler for JayHeadErrorV1 {
|
||||
type Error = JayHeadErrorV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayHeadErrorV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayHeadErrorV1 {}
|
||||
|
||||
dedicated_add_obj!(JayHeadErrorV1, JayHeadErrorV1Id, jay_head_errors);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayHeadErrorV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(JayHeadErrorV1Error, ClientError);
|
||||
1
src/ifs/head_management/jay_head_ext.rs
Normal file
1
src/ifs/head_management/jay_head_ext.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub(super) mod jay_head_ext_core_info_v1;
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
use {
|
||||
crate::{
|
||||
ifs::head_management::HeadState,
|
||||
wire::{jay_head_ext_core_info_v1::*, jay_head_manager_ext_core_info_v1::*},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
impl_core_info_v1! {
|
||||
version = 1,
|
||||
after_announce = after_announce,
|
||||
after_transaction = after_transaction,
|
||||
}
|
||||
|
||||
impl HeadName {
|
||||
fn after_announce(&self, shared: &HeadState) {
|
||||
self.send_name(shared);
|
||||
self.send_wl_output(shared);
|
||||
}
|
||||
|
||||
fn after_transaction(&self, shared: &HeadState, tran: &HeadState) {
|
||||
if shared.wl_output != tran.wl_output {
|
||||
self.send_wl_output(shared);
|
||||
}
|
||||
}
|
||||
|
||||
fn send_name(&self, state: &HeadState) {
|
||||
self.client.event(Name {
|
||||
self_id: self.id,
|
||||
name: Some(&**state.name),
|
||||
});
|
||||
}
|
||||
|
||||
pub(in super::super) fn send_wl_output(&self, state: &HeadState) {
|
||||
match state.wl_output {
|
||||
None => {
|
||||
self.client.event(NoWlOutput { self_id: self.id });
|
||||
}
|
||||
Some(name) => {
|
||||
self.client.event(WlOutput {
|
||||
self_id: self.id,
|
||||
global_name: name.raw(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl JayHeadManagerExtCoreInfoV1RequestHandler for MgrName {
|
||||
type Error = ErrorName;
|
||||
|
||||
mgr_common_req!();
|
||||
}
|
||||
|
||||
impl JayHeadExtCoreInfoV1RequestHandler for HeadName {
|
||||
type Error = ErrorName;
|
||||
|
||||
head_common_req!();
|
||||
}
|
||||
|
||||
error!();
|
||||
488
src/ifs/head_management/jay_head_manager_session_v1.rs
Normal file
488
src/ifs/head_management/jay_head_manager_session_v1.rs
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
use {
|
||||
super::HeadMgrCommon,
|
||||
crate::{
|
||||
backend::transaction::{ConnectorTransaction, PreparedConnectorTransaction},
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
head_management::{
|
||||
Head, HeadCommon, HeadCommonError, HeadMgrState, HeadName, 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,
|
||||
},
|
||||
wl_output::PersistentOutputState,
|
||||
},
|
||||
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<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.get();
|
||||
#[expect(unused_mut)]
|
||||
let mut new = old;
|
||||
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,
|
||||
}
|
||||
for head in self.heads.lock().values() {
|
||||
let pending = mem::take(&mut *head.common.pending.borrow_mut());
|
||||
#[expect(unused_variables)]
|
||||
let snapshot = &*head.common.snapshot_state.borrow();
|
||||
let state = &mut *head.common.transaction_state.borrow_mut();
|
||||
#[expect(unused_mut)]
|
||||
let mut to_send = ToSend::default();
|
||||
#[expect(clippy::never_loop)]
|
||||
for op in pending {
|
||||
match op {}
|
||||
}
|
||||
if to_send.contains(CORE_INFO)
|
||||
&& let Some(i) = &head.ext.core_info_v1
|
||||
{
|
||||
i.send_wl_output(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
|
||||
{
|
||||
let _ = node;
|
||||
} else if let Some(mi) = &desired.monitor_info {
|
||||
let pos = &self.client.state.persistent_output_states;
|
||||
let pos = match pos.get(&mi.output_id) {
|
||||
Some(ps) => ps,
|
||||
_ => {
|
||||
let ps = Rc::new(PersistentOutputState {
|
||||
transform: Default::default(),
|
||||
scale: Default::default(),
|
||||
pos: Default::default(),
|
||||
vrr_mode: Cell::new(&VrrMode::Never),
|
||||
vrr_cursor_hz: Default::default(),
|
||||
tearing_mode: Cell::new(&TearingMode::Never),
|
||||
brightness: Default::default(),
|
||||
});
|
||||
pos.set(mi.output_id.clone(), ps.clone());
|
||||
ps
|
||||
}
|
||||
};
|
||||
let _ = pos;
|
||||
}
|
||||
}
|
||||
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);
|
||||
158
src/ifs/head_management/jay_head_manager_v1.rs
Normal file
158
src/ifs/head_management/jay_head_manager_v1.rs
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{CAP_HEAD_MANAGER, Client, ClientCaps, ClientError},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::head_management::{
|
||||
HeadMgrCommon, head_management_macros::send_available_extensions,
|
||||
jay_head_manager_session_v1::JayHeadManagerSessionV1,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
utils::numcell::NumCell,
|
||||
wire::{
|
||||
JayHeadManagerV1Id,
|
||||
jay_head_manager_v1::{
|
||||
CreateSession, Destroy, Done, Extension, ExtensionsDone,
|
||||
JayHeadManagerV1RequestHandler,
|
||||
},
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct JayHeadManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
impl JayHeadManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: JayHeadManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), JayHeadManagerV1Error> {
|
||||
let mgr = Rc::new(JayHeadManagerV1 {
|
||||
id,
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
done_scheduled: Cell::new(false),
|
||||
sessions: Default::default(),
|
||||
destroyed: Default::default(),
|
||||
});
|
||||
track!(client, mgr);
|
||||
client.add_client_obj(&mgr)?;
|
||||
send_available_extensions(&mgr);
|
||||
mgr.send_extensions_done();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
JayHeadManagerV1Global,
|
||||
JayHeadManagerV1,
|
||||
JayHeadManagerV1Error
|
||||
);
|
||||
|
||||
simple_add_global!(JayHeadManagerV1Global);
|
||||
|
||||
impl Global for JayHeadManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_HEAD_MANAGER
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct JayHeadManagerV1 {
|
||||
pub(super) id: JayHeadManagerV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) done_scheduled: Cell<bool>,
|
||||
pub(super) sessions: NumCell<u32>,
|
||||
pub(super) destroyed: Cell<bool>,
|
||||
}
|
||||
|
||||
impl JayHeadManagerV1 {
|
||||
pub(super) fn send_extension(&self, name: u32, interface: &str, version: Version) {
|
||||
self.client.event(Extension {
|
||||
self_id: self.id,
|
||||
name,
|
||||
interface,
|
||||
version: version.0,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_extensions_done(&self) {
|
||||
self.client.event(ExtensionsDone { self_id: self.id });
|
||||
}
|
||||
|
||||
pub(super) fn send_done(&self) {
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl JayHeadManagerV1RequestHandler for JayHeadManagerV1 {
|
||||
type Error = JayHeadManagerV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if self.sessions.get() > 0 {
|
||||
return Err(JayHeadManagerV1Error::HasSessions);
|
||||
}
|
||||
self.destroyed.set(true);
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_session(&self, req: CreateSession, slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let obj = Rc::new(JayHeadManagerSessionV1 {
|
||||
id: req.session,
|
||||
mgr: slf.clone(),
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
common: Rc::new(HeadMgrCommon {
|
||||
state: Default::default(),
|
||||
in_transaction: Cell::new(false),
|
||||
transaction_failed: Cell::new(false),
|
||||
}),
|
||||
serial: Default::default(),
|
||||
heads: Default::default(),
|
||||
ext: Default::default(),
|
||||
});
|
||||
track!(self.client, obj);
|
||||
self.client.add_client_obj(&obj)?;
|
||||
self.sessions.fetch_add(1);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayHeadManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayHeadManagerV1 {}
|
||||
|
||||
simple_add_obj!(JayHeadManagerV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayHeadManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("Manager still has sessions")]
|
||||
HasSessions,
|
||||
}
|
||||
efrom!(JayHeadManagerV1Error, ClientError);
|
||||
82
src/ifs/head_management/jay_head_transaction_result_v1.rs
Normal file
82
src/ifs/head_management/jay_head_transaction_result_v1.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::head_management::{HeadTransactionError, jay_head_error_v1::JayHeadErrorV1},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{JayHeadTransactionResultV1Id, jay_head_transaction_result_v1::*},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub(super) struct JayHeadTransactionResultV1 {
|
||||
pub(super) id: JayHeadTransactionResultV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) error: Option<Rc<HeadTransactionError>>,
|
||||
pub(super) destroyed: Cell<bool>,
|
||||
}
|
||||
|
||||
impl JayHeadTransactionResultV1 {
|
||||
pub(super) fn send(&self) {
|
||||
match self.error {
|
||||
None => self.send_success(),
|
||||
_ => self.send_failed(),
|
||||
}
|
||||
}
|
||||
|
||||
fn send_success(&self) {
|
||||
self.client.event(Success { self_id: self.id });
|
||||
}
|
||||
|
||||
fn send_failed(&self) {
|
||||
self.client.event(Failed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl JayHeadTransactionResultV1RequestHandler for JayHeadTransactionResultV1 {
|
||||
type Error = JayHeadTransactionResultV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.client.remove_obj(self)?;
|
||||
self.destroyed.set(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_error(&self, req: GetError, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
let Some(err) = &self.error else {
|
||||
return Err(JayHeadTransactionResultV1Error::NoError);
|
||||
};
|
||||
let err = Rc::new(JayHeadErrorV1 {
|
||||
id: req.error,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
error: err.clone(),
|
||||
});
|
||||
track!(self.client, err);
|
||||
self.client.add_client_obj(&err)?;
|
||||
err.send();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayHeadTransactionResultV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayHeadTransactionResultV1 {}
|
||||
|
||||
simple_add_obj!(JayHeadTransactionResultV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayHeadTransactionResultV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error("The transaction result was success")]
|
||||
NoError,
|
||||
}
|
||||
efrom!(JayHeadTransactionResultV1Error, ClientError);
|
||||
55
src/ifs/head_management/jay_head_v1.rs
Normal file
55
src/ifs/head_management/jay_head_v1.rs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
use {
|
||||
super::HeadCommon,
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::head_management::HeadCommonError,
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
wire::{JayHeadV1Id, jay_head_v1::*},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub(super) struct JayHeadV1 {
|
||||
pub(super) id: JayHeadV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub(super) common: Rc<HeadCommon>,
|
||||
}
|
||||
|
||||
impl JayHeadV1 {
|
||||
pub(super) fn send_removed(&self) {
|
||||
self.common.removed.set(true);
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
}
|
||||
|
||||
impl JayHeadV1RequestHandler for JayHeadV1 {
|
||||
type Error = JayHeadV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.common.assert_removed()?;
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = JayHeadV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for JayHeadV1 {}
|
||||
|
||||
simple_add_obj!(JayHeadV1);
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum JayHeadV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
#[error(transparent)]
|
||||
Common(#[from] HeadCommonError),
|
||||
}
|
||||
efrom!(JayHeadV1Error, ClientError);
|
||||
21
src/state.rs
21
src/state.rs
|
|
@ -40,6 +40,10 @@ use {
|
|||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||
ext_idle_notification_v1::ExtIdleNotificationV1,
|
||||
ext_session_lock_v1::ExtSessionLockV1,
|
||||
head_management::{
|
||||
HeadManagers, HeadNames,
|
||||
jay_head_manager_session_v1::{HeadManagerEvent, JayHeadManagerSessionV1},
|
||||
},
|
||||
ipc::{
|
||||
DataOfferIds, DataSourceIds, data_control::DataControlDeviceIds,
|
||||
x_data_device::XIpcDeviceIds,
|
||||
|
|
@ -107,8 +111,9 @@ use {
|
|||
},
|
||||
wheel::Wheel,
|
||||
wire::{
|
||||
ExtForeignToplevelListV1Id, ExtIdleNotificationV1Id, JayRenderCtxId, JaySeatEventsId,
|
||||
JayWorkspaceWatcherId, ZwlrForeignToplevelManagerV1Id, ZwpLinuxDmabufFeedbackV1Id,
|
||||
ExtForeignToplevelListV1Id, ExtIdleNotificationV1Id, JayHeadManagerSessionV1Id,
|
||||
JayRenderCtxId, JaySeatEventsId, JayWorkspaceWatcherId, ZwlrForeignToplevelManagerV1Id,
|
||||
ZwpLinuxDmabufFeedbackV1Id,
|
||||
},
|
||||
xwayland::{self, XWaylandEvent},
|
||||
},
|
||||
|
|
@ -257,6 +262,10 @@ pub struct State {
|
|||
pub node_at_tree: RefCell<Vec<FoundNode>>,
|
||||
pub position_hint_requests: AsyncQueue<PositionHintRequest>,
|
||||
pub backend_connector_state_serials: BackendConnectorStateSerials,
|
||||
pub head_names: HeadNames,
|
||||
pub head_managers:
|
||||
CopyHashMap<(ClientId, JayHeadManagerSessionV1Id), Rc<JayHeadManagerSessionV1>>,
|
||||
pub head_managers_async: AsyncQueue<HeadManagerEvent>,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -368,10 +377,11 @@ pub struct DeviceHandlerData {
|
|||
}
|
||||
|
||||
pub struct ConnectorData {
|
||||
pub id: ConnectorId,
|
||||
pub connector: Rc<dyn Connector>,
|
||||
pub handler: Cell<Option<SpawnedFuture<()>>>,
|
||||
pub connected: Cell<bool>,
|
||||
pub name: String,
|
||||
pub name: Rc<String>,
|
||||
pub drm_dev: Option<Rc<DrmDevData>>,
|
||||
pub async_event: Rc<AsyncEvent>,
|
||||
pub damaged: Cell<bool>,
|
||||
|
|
@ -379,11 +389,12 @@ pub struct ConnectorData {
|
|||
pub needs_vblank_emulation: Cell<bool>,
|
||||
pub damage_intersect: Cell<Rect>,
|
||||
pub state: Cell<BackendConnectorState>,
|
||||
pub head_managers: HeadManagers,
|
||||
}
|
||||
|
||||
pub struct OutputData {
|
||||
pub connector: Rc<ConnectorData>,
|
||||
pub monitor_info: MonitorInfo,
|
||||
pub monitor_info: Rc<MonitorInfo>,
|
||||
pub node: Option<Rc<OutputNode>>,
|
||||
pub lease_connectors: Rc<Bindings<WpDrmLeaseConnectorV1>>,
|
||||
}
|
||||
|
|
@ -1017,6 +1028,8 @@ impl State {
|
|||
self.tl_matcher_manager.clear();
|
||||
self.node_at_tree.borrow_mut().clear();
|
||||
self.position_hint_requests.clear();
|
||||
self.head_managers.clear();
|
||||
self.head_managers_async.clear();
|
||||
}
|
||||
|
||||
pub fn remove_toplevel_id(&self, id: ToplevelIdentifier) {
|
||||
|
|
|
|||
|
|
@ -7,13 +7,16 @@ use {
|
|||
format::XRGB8888,
|
||||
globals::GlobalName,
|
||||
ifs::{
|
||||
head_management::{HeadManagers, HeadState},
|
||||
jay_tray_v1::JayTrayV1Global,
|
||||
wl_output::{PersistentOutputState, WlOutputGlobal},
|
||||
},
|
||||
output_schedule::OutputSchedule,
|
||||
state::{ConnectorData, OutputData, State},
|
||||
tree::{OutputNode, WsMoveConfig, move_ws_to_output},
|
||||
utils::{asyncevent::AsyncEvent, clonecell::CloneCell, hash_map_ext::HashMapExt},
|
||||
utils::{
|
||||
asyncevent::AsyncEvent, clonecell::CloneCell, hash_map_ext::HashMapExt, rc_eq::RcEq,
|
||||
},
|
||||
},
|
||||
std::{cell::Cell, collections::VecDeque, rc::Rc},
|
||||
};
|
||||
|
|
@ -39,11 +42,18 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
|||
transfer_function: Default::default(),
|
||||
};
|
||||
let id = connector.id();
|
||||
let name = Rc::new(connector.kernel_id().to_string());
|
||||
let head_state = HeadState {
|
||||
name: RcEq(name.clone()),
|
||||
wl_output: None,
|
||||
monitor_info: None,
|
||||
};
|
||||
let data = Rc::new(ConnectorData {
|
||||
id,
|
||||
connector: connector.clone(),
|
||||
handler: Default::default(),
|
||||
connected: Cell::new(false),
|
||||
name: connector.kernel_id().to_string(),
|
||||
name,
|
||||
drm_dev: drm_dev.clone(),
|
||||
async_event: Rc::new(AsyncEvent::default()),
|
||||
damaged: Cell::new(false),
|
||||
|
|
@ -51,6 +61,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
|||
needs_vblank_emulation: Cell::new(false),
|
||||
damage_intersect: Default::default(),
|
||||
state: Cell::new(backend_state),
|
||||
head_managers: HeadManagers::new(state.head_names.next(), head_state),
|
||||
});
|
||||
if let Some(dev) = drm_dev {
|
||||
dev.connectors.set(id, data.clone());
|
||||
|
|
@ -62,6 +73,9 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
|
|||
};
|
||||
let future = state.eng.spawn("connector handler", oh.handle());
|
||||
data.handler.set(Some(future));
|
||||
for mgr in state.head_managers.lock().values() {
|
||||
mgr.announce(&data);
|
||||
}
|
||||
if state.connectors.set(id, data).is_some() {
|
||||
panic!("Connector id has been reused");
|
||||
}
|
||||
|
|
@ -100,6 +114,7 @@ impl ConnectorHandler {
|
|||
}
|
||||
self.data.handler.set(None);
|
||||
self.state.connectors.remove(&self.id);
|
||||
self.data.head_managers.handle_removed();
|
||||
}
|
||||
|
||||
async fn handle_connected(&self, info: MonitorInfo) {
|
||||
|
|
@ -113,6 +128,7 @@ impl ConnectorHandler {
|
|||
self.handle_desktop_connected(info, name).await;
|
||||
}
|
||||
self.data.connected.set(false);
|
||||
self.data.head_managers.handle_output_disconnected();
|
||||
log::info!("Connector {} disconnected", self.data.connector.kernel_id());
|
||||
}
|
||||
|
||||
|
|
@ -218,11 +234,11 @@ impl ConnectorHandler {
|
|||
.add_output_scale(on.global.persistent.scale.get());
|
||||
let output_data = Rc::new(OutputData {
|
||||
connector: self.data.clone(),
|
||||
monitor_info: info,
|
||||
monitor_info: Rc::new(info),
|
||||
node: Some(on.clone()),
|
||||
lease_connectors: Default::default(),
|
||||
});
|
||||
self.state.outputs.set(self.id, output_data);
|
||||
self.state.outputs.set(self.id, output_data.clone());
|
||||
on.schedule_update_render_data();
|
||||
self.state.root.outputs.set(self.id, on.clone());
|
||||
self.state.output_extents_changed();
|
||||
|
|
@ -274,6 +290,9 @@ impl ConnectorHandler {
|
|||
self.state.tree_changed();
|
||||
on.update_presentation_type();
|
||||
self.state.workspace_managers.announce_output(&on);
|
||||
self.data
|
||||
.head_managers
|
||||
.handle_output_connected(&output_data);
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
|
|
@ -362,7 +381,7 @@ impl ConnectorHandler {
|
|||
async fn handle_non_desktop_connected(&self, monitor_info: MonitorInfo) {
|
||||
let output_data = Rc::new(OutputData {
|
||||
connector: self.data.clone(),
|
||||
monitor_info,
|
||||
monitor_info: Rc::new(monitor_info),
|
||||
node: None,
|
||||
lease_connectors: Default::default(),
|
||||
});
|
||||
|
|
@ -387,6 +406,9 @@ impl ConnectorHandler {
|
|||
if let Some(config) = self.state.config.get() {
|
||||
config.connector_connected(self.id);
|
||||
}
|
||||
self.data
|
||||
.head_managers
|
||||
.handle_output_connected(&output_data);
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
|
|
|
|||
|
|
@ -66,6 +66,14 @@ impl<K: Eq + Hash, V> CopyHashMap<K, V> {
|
|||
unsafe { self.map.get().deref().contains_key(k) }
|
||||
}
|
||||
|
||||
pub fn not_contains<Q>(&self, k: &Q) -> bool
|
||||
where
|
||||
Q: Hash + Eq + ?Sized,
|
||||
K: Borrow<Q>,
|
||||
{
|
||||
!self.contains(k)
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> Locked<'_, K, V> {
|
||||
Locked {
|
||||
source: self,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,29 @@
|
|||
use std::rc::Rc;
|
||||
use std::{ops::Deref, rc::Rc};
|
||||
|
||||
pub fn rc_eq<T: ?Sized>(a: &Rc<T>, b: &Rc<T>) -> bool {
|
||||
Rc::as_ptr(a) as *const u8 == Rc::as_ptr(b) as *const u8
|
||||
}
|
||||
|
||||
pub struct RcEq<T>(pub Rc<T>);
|
||||
|
||||
impl<T> Clone for RcEq<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for RcEq<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
rc_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for RcEq<T> {}
|
||||
|
||||
impl<T> Deref for RcEq<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
wire/jay_head_error_v1.txt
Normal file
23
wire/jay_head_error_v1.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event message {
|
||||
msg: str,
|
||||
}
|
||||
|
||||
event already_failed {
|
||||
|
||||
}
|
||||
|
||||
event out_of_date {
|
||||
|
||||
}
|
||||
|
||||
event extension {
|
||||
name: u32,
|
||||
}
|
||||
|
||||
event done {
|
||||
|
||||
}
|
||||
15
wire/jay_head_ext_core_info_v1.txt
Normal file
15
wire/jay_head_ext_core_info_v1.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event wl_output {
|
||||
global_name: u32,
|
||||
}
|
||||
|
||||
event no_wl_output {
|
||||
|
||||
}
|
||||
|
||||
event name {
|
||||
name: optstr,
|
||||
}
|
||||
7
wire/jay_head_manager_ext_core_info_v1.txt
Normal file
7
wire/jay_head_manager_ext_core_info_v1.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event head {
|
||||
head: id(jay_head_ext_core_info_v1) (new),
|
||||
}
|
||||
59
wire/jay_head_manager_session_v1.txt
Normal file
59
wire/jay_head_manager_session_v1.txt
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
request bind_extension {
|
||||
name: u32,
|
||||
interface: str,
|
||||
version: u32,
|
||||
id: id(object) (new),
|
||||
}
|
||||
|
||||
request stop {
|
||||
|
||||
}
|
||||
|
||||
event stopped {
|
||||
|
||||
}
|
||||
|
||||
request start {
|
||||
|
||||
}
|
||||
|
||||
event head_start {
|
||||
head: id(jay_head_v1) (new),
|
||||
name: u64,
|
||||
}
|
||||
|
||||
event head_complete {
|
||||
|
||||
}
|
||||
|
||||
request begin_transaction {
|
||||
|
||||
}
|
||||
|
||||
request rollback_transaction {
|
||||
|
||||
}
|
||||
|
||||
request apply_changes {
|
||||
result: id(jay_head_transaction_result_v1) (new),
|
||||
}
|
||||
|
||||
request test_transaction {
|
||||
result: id(jay_head_transaction_result_v1) (new),
|
||||
}
|
||||
|
||||
request commit_transaction {
|
||||
result: id(jay_head_transaction_result_v1) (new),
|
||||
}
|
||||
|
||||
event transaction_started {
|
||||
|
||||
}
|
||||
|
||||
event transaction_ended {
|
||||
|
||||
}
|
||||
21
wire/jay_head_manager_v1.txt
Normal file
21
wire/jay_head_manager_v1.txt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event extension {
|
||||
name: u32,
|
||||
interface: str,
|
||||
version: u32,
|
||||
}
|
||||
|
||||
event extensions_done {
|
||||
|
||||
}
|
||||
|
||||
request create_session {
|
||||
session: id(jay_head_manager_session_v1) (new),
|
||||
}
|
||||
|
||||
event done {
|
||||
|
||||
}
|
||||
15
wire/jay_head_transaction_result_v1.txt
Normal file
15
wire/jay_head_transaction_result_v1.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event success {
|
||||
|
||||
}
|
||||
|
||||
event failed {
|
||||
|
||||
}
|
||||
|
||||
request get_error {
|
||||
error: id(jay_head_error_v1) (new),
|
||||
}
|
||||
7
wire/jay_head_v1.txt
Normal file
7
wire/jay_head_v1.txt
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
request destroy (destructor) {
|
||||
|
||||
}
|
||||
|
||||
event removed {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue