ext-workspace: implement v1
This commit is contained in:
parent
2b76083d6e
commit
a4e197d92a
22 changed files with 948 additions and 4 deletions
|
|
@ -13,7 +13,7 @@ use {
|
|||
state::{ConnectorData, State},
|
||||
tree::{calculate_logical_size, OutputNode, TearingMode, VrrMode},
|
||||
utils::{
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap,
|
||||
cell_ext::CellExt, clonecell::CloneCell, copyhashmap::CopyHashMap, rc_eq::rc_eq,
|
||||
transform_ext::TransformExt,
|
||||
},
|
||||
wire::{wl_output::*, WlOutputId, ZxdgOutputV1Id},
|
||||
|
|
@ -238,6 +238,11 @@ impl WlOutputGlobal {
|
|||
if obj.version >= SEND_DONE_SINCE {
|
||||
obj.send_done();
|
||||
}
|
||||
for group in client.objects.ext_workspace_groups.lock().values() {
|
||||
if rc_eq(&group.output, &self.opt) {
|
||||
group.handle_new_output(&obj);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
64
src/ifs/workspace_manager.rs
Normal file
64
src/ifs/workspace_manager.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use {
|
||||
crate::{
|
||||
client::Client,
|
||||
ifs::workspace_manager::{
|
||||
ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1,
|
||||
ext_workspace_manager_v1::{
|
||||
ExtWorkspaceManagerV1, WorkspaceManagerId, WorkspaceManagerIds,
|
||||
},
|
||||
},
|
||||
state::State,
|
||||
tree::{OutputNode, WorkspaceNode},
|
||||
utils::{copyhashmap::CopyHashMap, opt::Opt, queue::AsyncQueue},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub mod ext_workspace_group_handle_v1;
|
||||
pub mod ext_workspace_handle_v1;
|
||||
pub mod ext_workspace_manager_v1;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WorkspaceManagerState {
|
||||
queue: AsyncQueue<Rc<Opt<ExtWorkspaceManagerV1>>>,
|
||||
dangling_group: Rc<Opt<ExtWorkspaceGroupHandleV1>>,
|
||||
ids: WorkspaceManagerIds,
|
||||
managers: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceManagerV1>>,
|
||||
}
|
||||
|
||||
impl WorkspaceManagerState {
|
||||
pub fn clear(&self) {
|
||||
self.managers.clear();
|
||||
self.queue.clear();
|
||||
}
|
||||
|
||||
pub fn announce_output(&self, on: &OutputNode) {
|
||||
for manager in self.managers.lock().values() {
|
||||
manager.announce_output(on);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announce_workspace(&self, output: &OutputNode, ws: &WorkspaceNode) {
|
||||
for manager in self.managers.lock().values() {
|
||||
manager.announce_workspace(output, ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn workspace_manager_done(state: Rc<State>) {
|
||||
loop {
|
||||
let manager = state.workspace_managers.queue.pop().await;
|
||||
if let Some(manager) = manager.get() {
|
||||
manager.send_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn group_or_dangling(
|
||||
client: &Client,
|
||||
group: Option<&ExtWorkspaceGroupHandleV1>,
|
||||
) -> Rc<Opt<ExtWorkspaceGroupHandleV1>> {
|
||||
group
|
||||
.map(|g| g.opt.clone())
|
||||
.unwrap_or_else(|| client.state.workspace_managers.dangling_group.clone())
|
||||
}
|
||||
164
src/ifs/workspace_manager/ext_workspace_group_handle_v1.rs
Normal file
164
src/ifs/workspace_manager/ext_workspace_group_handle_v1.rs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::{
|
||||
wl_output::{OutputGlobalOpt, WlOutput},
|
||||
workspace_manager::{
|
||||
ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||
ext_workspace_manager_v1::{
|
||||
ExtWorkspaceManagerV1, WorkspaceChange, WorkspaceManagerId,
|
||||
},
|
||||
},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
utils::opt::Opt,
|
||||
wire::{ext_workspace_group_handle_v1::*, ExtWorkspaceGroupHandleV1Id},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
pub struct ExtWorkspaceGroupHandleV1 {
|
||||
pub(super) id: ExtWorkspaceGroupHandleV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub(super) version: Version,
|
||||
pub output: Rc<OutputGlobalOpt>,
|
||||
pub(super) manager_id: WorkspaceManagerId,
|
||||
pub(super) manager: Rc<Opt<ExtWorkspaceManagerV1>>,
|
||||
pub(super) opt: Rc<Opt<ExtWorkspaceGroupHandleV1>>,
|
||||
}
|
||||
|
||||
const CAP_CREATE_WORKSPACE: u32 = 1;
|
||||
|
||||
impl ExtWorkspaceGroupHandleV1 {
|
||||
fn detach(&self) {
|
||||
self.opt.set(None);
|
||||
if let Some(node) = self.output.node() {
|
||||
node.ext_workspace_groups.remove(&self.manager_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn send_capabilities(&self) {
|
||||
let capabilities = CAP_CREATE_WORKSPACE;
|
||||
self.client.event(Capabilities {
|
||||
self_id: self.id,
|
||||
capabilities,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_output_enter(&self, output: &WlOutput) {
|
||||
self.client.event(OutputEnter {
|
||||
self_id: self.id,
|
||||
output: output.id,
|
||||
});
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
fn send_output_leave(&self, output: &WlOutput) {
|
||||
self.client.event(OutputLeave {
|
||||
self_id: self.id,
|
||||
output: output.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_workspace_enter(&self, workspace: &ExtWorkspaceHandleV1) {
|
||||
self.client.event(WorkspaceEnter {
|
||||
self_id: self.id,
|
||||
workspace: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_workspace_leave(&self, workspace: &ExtWorkspaceHandleV1) {
|
||||
self.client.event(WorkspaceLeave {
|
||||
self_id: self.id,
|
||||
workspace: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_removed(&self) {
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn handle_destroyed(&self) {
|
||||
self.detach();
|
||||
if let Some(manager) = self.manager.get() {
|
||||
self.send_removed();
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_new_output(&self, output: &WlOutput) {
|
||||
if let Some(manager) = self.manager.get() {
|
||||
self.send_output_enter(output);
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtWorkspaceGroupHandleV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtWorkspaceGroupHandleV1 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
dedicated_add_obj!(
|
||||
ExtWorkspaceGroupHandleV1,
|
||||
ExtWorkspaceGroupHandleV1Id,
|
||||
ext_workspace_groups
|
||||
);
|
||||
|
||||
impl ExtWorkspaceGroupHandleV1RequestHandler for ExtWorkspaceGroupHandleV1 {
|
||||
type Error = ExtWorkspaceGroupHandleV1Error;
|
||||
|
||||
fn create_workspace(
|
||||
&self,
|
||||
req: CreateWorkspace<'_>,
|
||||
_slf: &Rc<Self>,
|
||||
) -> Result<(), Self::Error> {
|
||||
if self.opt.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(manager) = self.manager.get() else {
|
||||
return Ok(());
|
||||
};
|
||||
manager.pending.push(WorkspaceChange::CreateWorkspace(
|
||||
req.workspace.to_string(),
|
||||
self.output.clone(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if let Some(manager) = self.manager.get() {
|
||||
if let Some(node) = self.output.node() {
|
||||
let mut sent_any = false;
|
||||
for ws in node.workspaces.iter() {
|
||||
if let Some(ws) = ws.ext_workspaces.get(&self.manager_id) {
|
||||
self.send_workspace_leave(&ws);
|
||||
sent_any = true;
|
||||
}
|
||||
}
|
||||
if sent_any {
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtWorkspaceGroupHandleV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtWorkspaceGroupHandleV1Error, ClientError);
|
||||
215
src/ifs/workspace_manager/ext_workspace_handle_v1.rs
Normal file
215
src/ifs/workspace_manager/ext_workspace_handle_v1.rs
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientError},
|
||||
ifs::workspace_manager::{
|
||||
ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1,
|
||||
ext_workspace_manager_v1::{
|
||||
ExtWorkspaceManagerV1, WorkspaceChange, WorkspaceManagerId,
|
||||
},
|
||||
group_or_dangling,
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::{OutputNode, WorkspaceNode},
|
||||
utils::{clonecell::CloneCell, opt::Opt},
|
||||
wire::{ext_workspace_handle_v1::*, ExtWorkspaceHandleV1Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
const STATE_ACTIVE: u32 = 1;
|
||||
const STATE_URGENT: u32 = 2;
|
||||
#[expect(dead_code)]
|
||||
const STATE_HIDDEN: u32 = 4;
|
||||
|
||||
const CAP_ACTIVATE: u32 = 1;
|
||||
#[expect(dead_code)]
|
||||
const CAP_DEACTIVATE: u32 = 2;
|
||||
#[expect(dead_code)]
|
||||
const CAP_REMOVE: u32 = 4;
|
||||
const CAP_ASSIGN: u32 = 8;
|
||||
|
||||
pub struct ExtWorkspaceHandleV1 {
|
||||
pub(super) id: ExtWorkspaceHandleV1Id,
|
||||
pub(super) client: Rc<Client>,
|
||||
pub(super) tracker: Tracker<Self>,
|
||||
pub version: Version,
|
||||
pub(super) group: CloneCell<Rc<Opt<ExtWorkspaceGroupHandleV1>>>,
|
||||
pub(super) workspace: Rc<Opt<WorkspaceNode>>,
|
||||
pub(super) manager_id: WorkspaceManagerId,
|
||||
pub(super) manager: Rc<Opt<ExtWorkspaceManagerV1>>,
|
||||
pub(super) destroyed: Cell<bool>,
|
||||
}
|
||||
|
||||
impl ExtWorkspaceHandleV1 {
|
||||
fn detach(&self) {
|
||||
if let Some(ws) = self.workspace.get() {
|
||||
ws.ext_workspaces.remove(&self.manager_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn send_id(&self, id: &str) {
|
||||
self.client.event(Id {
|
||||
self_id: self.id,
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_name(&self, name: &str) {
|
||||
self.client.event(Name {
|
||||
self_id: self.id,
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
fn send_coordinates(&self, coordinates: &[u32]) {
|
||||
self.client.event(Coordinates {
|
||||
self_id: self.id,
|
||||
coordinates,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_current_state(&self) {
|
||||
let Some(ws) = self.workspace.get() else {
|
||||
return;
|
||||
};
|
||||
let mut state = 0;
|
||||
let output = ws.output.get();
|
||||
if let Some(active) = output.workspace.get() {
|
||||
if active.id == ws.id {
|
||||
state |= STATE_ACTIVE;
|
||||
}
|
||||
}
|
||||
if ws.attention_requests.active() {
|
||||
state |= STATE_URGENT;
|
||||
}
|
||||
self.send_state(state);
|
||||
}
|
||||
|
||||
fn send_state(&self, state: u32) {
|
||||
self.client.event(State {
|
||||
self_id: self.id,
|
||||
state,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_capabilities(&self) {
|
||||
let capabilities = CAP_ACTIVATE | CAP_ASSIGN;
|
||||
self.client.event(Capabilities {
|
||||
self_id: self.id,
|
||||
capabilities,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_removed(&self) {
|
||||
self.client.event(Removed { self_id: self.id });
|
||||
}
|
||||
|
||||
pub fn handle_destroyed(&self) {
|
||||
self.destroyed.set(true);
|
||||
if let Some(manager) = self.manager.get() {
|
||||
if let Some(group) = self.group.get().get() {
|
||||
group.send_workspace_leave(self);
|
||||
}
|
||||
self.group
|
||||
.set(self.client.state.workspace_managers.dangling_group.clone());
|
||||
self.send_state(0);
|
||||
self.send_removed();
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_new_output(&self, output: &OutputNode) {
|
||||
let new = output.ext_workspace_groups.get(&self.manager_id);
|
||||
let new = group_or_dangling(&self.client, new.as_deref());
|
||||
let old = self.group.set(new.clone());
|
||||
if let Some(manager) = self.manager.get() {
|
||||
if let Some(old) = old.get() {
|
||||
old.send_workspace_leave(self);
|
||||
}
|
||||
if let Some(new) = new.get() {
|
||||
new.send_workspace_enter(self);
|
||||
}
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_visibility_changed(&self) {
|
||||
if let Some(manager) = self.manager.get() {
|
||||
self.send_current_state();
|
||||
manager.schedule_done();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_urgent_changed(&self) {
|
||||
self.handle_visibility_changed();
|
||||
}
|
||||
}
|
||||
|
||||
object_base! {
|
||||
self = ExtWorkspaceHandleV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtWorkspaceHandleV1 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ExtWorkspaceHandleV1);
|
||||
|
||||
impl ExtWorkspaceHandleV1RequestHandler for ExtWorkspaceHandleV1 {
|
||||
type Error = ExtWorkspaceHandleV1Error;
|
||||
|
||||
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn activate(&self, _req: Activate, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if self.destroyed.get() {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(manager) = self.manager.get() else {
|
||||
return Ok(());
|
||||
};
|
||||
manager
|
||||
.pending
|
||||
.push(WorkspaceChange::ActivateWorkspace(self.workspace.clone()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deactivate(&self, _req: Deactivate, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign(&self, req: Assign, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
if self.destroyed.get() {
|
||||
return Ok(());
|
||||
}
|
||||
let group = self.client.lookup(req.workspace_group)?;
|
||||
let Some(manager) = self.manager.get() else {
|
||||
return Ok(());
|
||||
};
|
||||
manager.pending.push(WorkspaceChange::AssignWorkspace(
|
||||
self.workspace.clone(),
|
||||
group.output.clone(),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove(&self, _req: Remove, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtWorkspaceHandleV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtWorkspaceHandleV1Error, ClientError);
|
||||
306
src/ifs/workspace_manager/ext_workspace_manager_v1.rs
Normal file
306
src/ifs/workspace_manager/ext_workspace_manager_v1.rs
Normal file
|
|
@ -0,0 +1,306 @@
|
|||
use {
|
||||
crate::{
|
||||
client::{Client, ClientCaps, ClientError, CAP_WORKSPACE},
|
||||
globals::{Global, GlobalName},
|
||||
ifs::{
|
||||
wl_output::OutputGlobalOpt,
|
||||
workspace_manager::{
|
||||
ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1,
|
||||
ext_workspace_handle_v1::ExtWorkspaceHandleV1, group_or_dangling,
|
||||
},
|
||||
},
|
||||
leaks::Tracker,
|
||||
object::{Object, Version},
|
||||
tree::{move_ws_to_output, OutputNode, WorkspaceNode, WsMoveConfig},
|
||||
utils::{clonecell::CloneCell, opt::Opt, syncqueue::SyncQueue},
|
||||
wire::{ext_workspace_manager_v1::*, ExtWorkspaceManagerV1Id},
|
||||
},
|
||||
std::{cell::Cell, rc::Rc},
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
linear_ids!(WorkspaceManagerIds, WorkspaceManagerId, u64);
|
||||
|
||||
pub struct ExtWorkspaceManagerV1Global {
|
||||
pub name: GlobalName,
|
||||
}
|
||||
|
||||
pub struct ExtWorkspaceManagerV1 {
|
||||
id: ExtWorkspaceManagerV1Id,
|
||||
pub(super) manager_id: WorkspaceManagerId,
|
||||
client: Rc<Client>,
|
||||
tracker: Tracker<Self>,
|
||||
version: Version,
|
||||
pub(super) pending: SyncQueue<WorkspaceChange>,
|
||||
opt: Rc<Opt<ExtWorkspaceManagerV1>>,
|
||||
done_scheduled: Cell<bool>,
|
||||
}
|
||||
|
||||
pub(super) enum WorkspaceChange {
|
||||
CreateWorkspace(String, Rc<OutputGlobalOpt>),
|
||||
ActivateWorkspace(Rc<Opt<WorkspaceNode>>),
|
||||
AssignWorkspace(Rc<Opt<WorkspaceNode>>, Rc<OutputGlobalOpt>),
|
||||
}
|
||||
|
||||
impl ExtWorkspaceManagerV1Global {
|
||||
pub fn new(name: GlobalName) -> Self {
|
||||
Self { name }
|
||||
}
|
||||
|
||||
fn bind_(
|
||||
self: Rc<Self>,
|
||||
id: ExtWorkspaceManagerV1Id,
|
||||
client: &Rc<Client>,
|
||||
version: Version,
|
||||
) -> Result<(), ExtWorkspaceManagerV1Error> {
|
||||
let obj = Rc::new(ExtWorkspaceManagerV1 {
|
||||
id,
|
||||
manager_id: client.state.workspace_managers.ids.next(),
|
||||
client: client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
pending: Default::default(),
|
||||
opt: Default::default(),
|
||||
done_scheduled: Cell::new(false),
|
||||
});
|
||||
track!(client, obj);
|
||||
client.add_client_obj(&obj)?;
|
||||
obj.opt.set(Some(obj.clone()));
|
||||
client
|
||||
.state
|
||||
.workspace_managers
|
||||
.managers
|
||||
.set(obj.manager_id, obj.clone());
|
||||
let dummy_output = client.state.dummy_output.get().unwrap();
|
||||
for ws in dummy_output.workspaces.iter() {
|
||||
if !ws.is_dummy {
|
||||
obj.announce_workspace(&dummy_output, &ws);
|
||||
}
|
||||
}
|
||||
for output in client.state.root.outputs.lock().values() {
|
||||
obj.announce_output(output);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtWorkspaceManagerV1 {
|
||||
pub(super) fn announce_output(&self, node: &OutputNode) {
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let group = Rc::new(ExtWorkspaceGroupHandleV1 {
|
||||
id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
output: node.global.opt.clone(),
|
||||
manager_id: self.manager_id,
|
||||
manager: self.opt.clone(),
|
||||
opt: Default::default(),
|
||||
});
|
||||
track!(self.client, group);
|
||||
self.client.add_server_obj(&group);
|
||||
group.opt.set(Some(group.clone()));
|
||||
node.ext_workspace_groups
|
||||
.set(self.manager_id, group.clone());
|
||||
self.send_workspace_group(&group);
|
||||
group.send_capabilities();
|
||||
if let Some(bindings) = node.global.bindings.borrow().get(&self.client.id) {
|
||||
for wl_output in bindings.values() {
|
||||
group.send_output_enter(wl_output);
|
||||
}
|
||||
}
|
||||
for ws in node.workspaces.iter() {
|
||||
if let Some(ws) = ws.ext_workspaces.get(&self.manager_id) {
|
||||
ws.handle_new_output(node);
|
||||
} else {
|
||||
self.announce_workspace(node, &ws);
|
||||
}
|
||||
}
|
||||
self.schedule_done();
|
||||
}
|
||||
|
||||
pub(super) fn announce_workspace(&self, output: &OutputNode, workspace: &WorkspaceNode) {
|
||||
let id = match self.client.new_id() {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
self.client.error(e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let group = output.ext_workspace_groups.get(&self.manager_id);
|
||||
let ws = Rc::new(ExtWorkspaceHandleV1 {
|
||||
id,
|
||||
client: self.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version: self.version,
|
||||
group: CloneCell::new(group_or_dangling(&self.client, group.as_deref())),
|
||||
workspace: workspace.opt.clone(),
|
||||
manager_id: self.manager_id,
|
||||
manager: self.opt.clone(),
|
||||
destroyed: Cell::new(false),
|
||||
});
|
||||
track!(self.client, ws);
|
||||
self.client.add_server_obj(&ws);
|
||||
workspace.ext_workspaces.set(self.manager_id, ws.clone());
|
||||
self.send_workspace(&ws);
|
||||
ws.send_capabilities();
|
||||
ws.send_id(&workspace.name);
|
||||
ws.send_name(&workspace.name);
|
||||
ws.send_current_state();
|
||||
if let Some(group) = group {
|
||||
group.send_workspace_enter(&ws);
|
||||
}
|
||||
self.schedule_done();
|
||||
}
|
||||
|
||||
fn send_workspace_group(&self, workspace_group: &ExtWorkspaceGroupHandleV1) {
|
||||
self.client.event(WorkspaceGroup {
|
||||
self_id: self.id,
|
||||
workspace_group: workspace_group.id,
|
||||
});
|
||||
}
|
||||
|
||||
fn send_workspace(&self, workspace: &ExtWorkspaceHandleV1) {
|
||||
self.client.event(Workspace {
|
||||
self_id: self.id,
|
||||
workspace: workspace.id,
|
||||
});
|
||||
}
|
||||
|
||||
pub(super) fn send_done(&self) {
|
||||
self.done_scheduled.set(false);
|
||||
self.client.event(Done { self_id: self.id });
|
||||
}
|
||||
|
||||
fn send_finished(&self) {
|
||||
self.client.event(Finished { self_id: self.id });
|
||||
}
|
||||
|
||||
fn detach(&self) {
|
||||
self.opt.set(None);
|
||||
self.pending.clear();
|
||||
self.client
|
||||
.state
|
||||
.workspace_managers
|
||||
.managers
|
||||
.remove(&self.manager_id);
|
||||
}
|
||||
|
||||
pub(super) fn schedule_done(&self) {
|
||||
if self.done_scheduled.replace(true) {
|
||||
return;
|
||||
}
|
||||
self.client
|
||||
.state
|
||||
.workspace_managers
|
||||
.queue
|
||||
.push(self.opt.clone());
|
||||
}
|
||||
}
|
||||
|
||||
global_base!(
|
||||
ExtWorkspaceManagerV1Global,
|
||||
ExtWorkspaceManagerV1,
|
||||
ExtWorkspaceManagerV1Error
|
||||
);
|
||||
|
||||
impl Global for ExtWorkspaceManagerV1Global {
|
||||
fn singleton(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn version(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn required_caps(&self) -> ClientCaps {
|
||||
CAP_WORKSPACE
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_global!(ExtWorkspaceManagerV1Global);
|
||||
|
||||
object_base! {
|
||||
self = ExtWorkspaceManagerV1;
|
||||
version = self.version;
|
||||
}
|
||||
|
||||
impl Object for ExtWorkspaceManagerV1 {
|
||||
fn break_loops(&self) {
|
||||
self.detach();
|
||||
}
|
||||
}
|
||||
|
||||
simple_add_obj!(ExtWorkspaceManagerV1);
|
||||
|
||||
impl ExtWorkspaceManagerV1RequestHandler for ExtWorkspaceManagerV1 {
|
||||
type Error = ExtWorkspaceManagerV1Error;
|
||||
|
||||
fn commit(&self, _req: Commit, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
while let Some(change) = self.pending.pop() {
|
||||
match change {
|
||||
WorkspaceChange::ActivateWorkspace(w) => {
|
||||
let Some(ws) = w.get() else {
|
||||
continue;
|
||||
};
|
||||
let output = ws.output.get();
|
||||
output.show_workspace(&ws);
|
||||
ws.flush_jay_workspaces();
|
||||
output.schedule_update_render_data();
|
||||
self.client.state.tree_changed();
|
||||
}
|
||||
WorkspaceChange::AssignWorkspace(w, o) => {
|
||||
let Some(ws) = w.get() else {
|
||||
continue;
|
||||
};
|
||||
let Some(o) = o.node() else {
|
||||
continue;
|
||||
};
|
||||
let link = match &*ws.output_link.borrow() {
|
||||
None => continue,
|
||||
Some(l) => l.to_ref(),
|
||||
};
|
||||
let config = WsMoveConfig {
|
||||
make_visible_always: false,
|
||||
make_visible_if_empty: true,
|
||||
source_is_destroyed: false,
|
||||
before: None,
|
||||
};
|
||||
move_ws_to_output(&link, &o, config);
|
||||
ws.desired_output.set(o.global.output_id.clone());
|
||||
self.client.state.tree_changed();
|
||||
}
|
||||
WorkspaceChange::CreateWorkspace(name, output) => {
|
||||
if self.client.state.workspaces.contains(&name) {
|
||||
return Ok(());
|
||||
}
|
||||
let Some(output) = output.node() else {
|
||||
return Ok(());
|
||||
};
|
||||
output.create_workspace(&name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self, _req: Stop, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||
self.detach();
|
||||
self.send_finished();
|
||||
self.client.remove_obj(self)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ExtWorkspaceManagerV1Error {
|
||||
#[error(transparent)]
|
||||
ClientError(Box<ClientError>),
|
||||
}
|
||||
efrom!(ExtWorkspaceManagerV1Error, ClientError);
|
||||
Loading…
Add table
Add a link
Reference in a new issue