From 805dad79fe92172bf47407228a5a1db3495410ee Mon Sep 17 00:00:00 2001 From: Victoria Brekenfeld Date: Fri, 29 Apr 2022 17:40:24 +0200 Subject: [PATCH] wayland: ext_workspace implementation --- src/wayland/workspace.rs | 417 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 416 insertions(+), 1 deletion(-) diff --git a/src/wayland/workspace.rs b/src/wayland/workspace.rs index 438bd48a..85f17ce2 100644 --- a/src/wayland/workspace.rs +++ b/src/wayland/workspace.rs @@ -26,4 +26,419 @@ mod generated { pub(crate) use wayland_server::{AnonymousObject, Main, Resource, ResourceMap}; include!(concat!(env!("OUT_DIR"), "/ext_workspace.rs")); } -} \ No newline at end of file +} + +use std::{ + cell::RefCell, + collections::HashSet, + fmt, + sync::{Arc, Mutex, Weak}, +}; +use smithay::{ + reexports::wayland_server::{Client, DispatchData, Display, Filter, Global, Main}, + wayland::output::Output, +}; +use self::generated::server::{ + zext_workspace_manager_v1::ZextWorkspaceManagerV1, + zext_workspace_group_handle_v1::ZextWorkspaceGroupHandleV1, + zext_workspace_handle_v1::{ZextWorkspaceHandleV1, State}, +}; + +#[derive(Debug, Clone)] +pub struct WorkspaceManager { + inner: Arc>, +} + +struct WorkspaceManagerInner { + instances: Vec>, + groups: Vec>>, + commit: Box)>, DispatchData) + 'static>, +} + +impl fmt::Debug for WorkspaceManagerInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WorkspaceManagerInner") + .field("instances", &self.instances) + .field("groups", &self.groups) + .finish_non_exhaustive() + } +} + +pub fn init_ext_workspace( + display: &mut Display, + commit: C, + client_filter: F, +) -> (WorkspaceManager, Global) +where + C: FnMut(&WorkspaceGroup, Vec<(&Workspace, Vec)>, DispatchData) + 'static, + F: FnMut(Client) -> bool + 'static, +{ + let inner = Arc::new(Mutex::new(WorkspaceManagerInner { + instances: Vec::new(), + groups: Vec::new(), + commit: Box::new(commit), + })); + + let inner_clone = inner.clone(); + let filter = Filter::new(move |(main, _version): (Main, u32), _, _| { + let inner = inner_clone.clone(); + main.quick_assign(move |main, request, mut ddata| { + match request { + zext_workspace_manager_v1::Request::Commit => { + if let Some(client) = main.as_ref().client() { + let mut inner_guard = inner.lock().unwrap(); + let inner = &mut *inner_guard; + for group in inner.groups.iter() { + let group_inner = group.lock().unwrap(); + let mut changes = group_inner.workspaces + .iter() + .flat_map(|x| x.upgrade()) + .flat_map(|w| { + let workspace_inner = w.lock().unwrap(); + let operations = workspace_inner.instances + .iter() + .find(|w_instance| w_instance.as_ref().client().map(|c| c == client).unwrap_or(false)) + .and_then(|w_instance| w_instance.as_ref().user_data().get::()) + .and_then(|user_data| { + let operations = std::mem::take( &mut *user_data.borrow_mut()); + if !operations.is_empty() { + Some(operations) + } else { + None + } + }); + if let Some(ops) = operations { + std::mem::drop(workspace_inner); + Some((Workspace{ inner: w }, ops)) + } else { + None + } + }) + .collect::)>>(); + + std::mem::drop(group_inner); + let group = WorkspaceGroup { + inner: group.clone(), + }; + let borrowed_changes = changes.iter_mut().map(|(w, p)| (&*w, std::mem::take(p))).collect(); + (inner.commit)(&group, borrowed_changes, ddata.reborrow()); + } + } + }, + zext_workspace_manager_v1::Request::Stop => { + let mut inner = inner.lock().unwrap(); + inner.instances.retain(|m| **m != *main); + }, + } + }); + + let inner_guard = inner_clone.lock().unwrap(); + if let Some(client) = main.as_ref().client() { + for group in inner_guard.groups.iter() { + if let Some(new_instance) = WorkspaceGroup::create_instance(&client, group.clone()) { + main.workspace_group(&*new_instance); + + let mut group_inner_guard = group.lock().unwrap(); + group_inner_guard.instances.push(new_instance); + for workspace in group_inner_guard.workspaces.iter() { + if let Some(workspace) = workspace.upgrade() { + let workspace_clone = workspace.clone(); + if let Some(new_instance) = Workspace::create_instance(&client, workspace_clone) { + let mut inner = workspace.lock().unwrap(); + inner.instances.push(new_instance); + inner.send(&client); + } + } + } + } + } + } + }); + let global = display.create_global_with_filter(1, filter, client_filter); + + ( + WorkspaceManager { + inner, + }, global + ) +} + +impl WorkspaceManager { + pub fn new_group(&self, create_new_workspace: F, outputs: impl Iterator) -> WorkspaceGroup + where + F: Fn(&WorkspaceGroup, String, DispatchData) + 'static, + { + let mut inner = self.inner.lock().unwrap(); + let create_new_workspace = Arc::new(Box::new(create_new_workspace) as Box); + let group_inner = Arc::new(Mutex::new(WorkspaceGroupInner { + instances: Vec::new(), + outputs: outputs.collect(), + workspaces: Vec::new(), + create_new_workspace, + })); + + for instance in inner.instances.iter() { + if let Some(client) = instance.as_ref().client() { + if let Some(group) = WorkspaceGroup::create_instance(&client, group_inner.clone()) { + instance.workspace_group(&*group); + group_inner.lock().unwrap().instances.push(group); + } + } + instance.done(); + } + + inner.groups.push(group_inner.clone()); + WorkspaceGroup { + inner: group_inner, + } + } + + pub fn update_outputs(&self, mut update: F) + where + F: FnMut(&WorkspaceGroup, &mut HashSet) + { + let inner = self.inner.lock().unwrap(); + for group in inner.groups.iter() { + let mut group_inner = group.lock().unwrap(); + let group = WorkspaceGroup { + inner: group.clone(), + }; + + let previous_outputs = group_inner.outputs.clone(); + update(&group, &mut group_inner.outputs); + + for output in previous_outputs.difference(&group_inner.outputs) { + for instance in group_inner.instances.iter() { + if let Some(client) = instance.as_ref().client() { + output.with_client_outputs(client.clone(), |output| instance.output_leave(output)); + } + } + } + for output in group_inner.outputs.difference(&previous_outputs) { + for instance in group_inner.instances.iter() { + if let Some(client) = instance.as_ref().client() { + output.with_client_outputs(client.clone(), |output| instance.output_enter(output)); + } + } + } + } + for instance in inner.instances.iter() { + instance.done(); + } + } + + pub fn remove_group(&self, group: &WorkspaceGroup) { + let mut inner = self.inner.lock().unwrap(); + // grr I want drain_filter + if let Some(pos) = inner.groups.iter().position(|x| Arc::ptr_eq(x, &group.inner)) { + let group = inner.groups.remove(pos); + let inner = group.lock().unwrap(); + for instance in inner.instances.iter() { + instance.remove(); + } + } + } +} + +#[derive(Debug)] +pub struct WorkspaceGroup { + inner: Arc>, +} + +struct WorkspaceGroupInner { + instances: Vec>, + outputs: HashSet, + workspaces: Vec>>, + create_new_workspace: Arc>, +} + +impl fmt::Debug for WorkspaceGroupInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("WorkspaceGroupInner") + .field("instances", &self.instances) + .field("outputs", &self.outputs) + .field("workspaces", &self.workspaces) + .finish_non_exhaustive() + } +} + +impl WorkspaceGroup { + pub fn create_workspace(&self, name: String) -> Workspace + { + let mut inner = self.inner.lock().unwrap(); + let workspace_inner = Arc::new(Mutex::new(WorkspaceInner { + instances: Vec::new(), + name: name.clone(), + states: Vec::new(), + coordinates: Vec::new(), + })); + inner.workspaces.retain(|w| w.upgrade().is_some()); + inner.workspaces.push(Arc::downgrade(&workspace_inner)); + + for instance in inner.instances.iter() { + if let Some(client) = instance.as_ref().client() { + let workspace_inner_clone = workspace_inner.clone(); + if let Some(workspace) = Workspace::create_instance(&client, workspace_inner_clone) { + let mut workspace_inner_guard = workspace_inner.lock().unwrap(); + workspace_inner_guard.instances.push(workspace); + workspace_inner_guard.send(&client); + } + } + } + + Workspace { + inner: workspace_inner, + } + } + + fn create_instance(client: &Client, group_inner: Arc>) -> Option> { + if let Some(group) = client.create_resource::(1) { + let group_inner_guard = group_inner.lock().unwrap(); + for output in group_inner_guard.outputs.iter() { + output.with_client_outputs(client.clone(), |output| group.output_enter(output)); + } + + let group_inner_clone = group_inner.clone(); + group.quick_assign(move |group, request, ddata| { + match request { + zext_workspace_group_handle_v1::Request::CreateWorkspace { workspace } => { + let callback = group_inner_clone.lock().unwrap().create_new_workspace.clone(); + let group = WorkspaceGroup { + inner: group_inner_clone.clone(), + }; + callback(&group, workspace, ddata); + }, + zext_workspace_group_handle_v1::Request::Destroy => { + group_inner_clone.lock().unwrap().instances.retain(|g| *g != group); + } + } + }); + Some(group) + } else { + None + } + } +} + +#[derive(Debug)] +pub struct Workspace { + inner: Arc>, +} + +#[derive(Debug)] +struct WorkspaceInner { + instances: Vec>, + name: String, + states: Vec, + coordinates: Vec, +} + +type WorkspaceUserdata = RefCell>; + + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PendingOperation { + Activate, + Deactivate, + Remove, +} + +impl Workspace { + pub fn name(&self) -> String { + self.inner.lock().unwrap().name.clone() + } + + pub fn set_name(&self, name: String) { + let mut inner = self.inner.lock().unwrap(); + for instance in inner.instances.iter() { + instance.name(name.clone()); + } + inner.name = name; + } + + pub fn states(&self) -> Vec { + self.inner.lock().unwrap().states.clone() + } + + pub fn add_state(&self, state: State) { + let mut inner = self.inner.lock().unwrap(); + inner.states.push(state); + for instance in inner.instances.iter() { + instance.state(inner.states.iter().map(|s| s.to_raw() as u8).collect()); + }; + } + + pub fn remove_state(&self, state: State) { + let mut inner = self.inner.lock().unwrap(); + inner.states.retain(|s| *s != state); + for instance in inner.instances.iter() { + instance.state(inner.states.iter().map(|s| s.to_raw() as u8).collect()); + }; + } + + pub fn set_states(&self, states: impl Iterator) { + let mut inner = self.inner.lock().unwrap(); + inner.states = states.collect(); + for instance in inner.instances.iter() { + instance.state(inner.states.iter().map(|s| s.to_raw() as u8).collect()); + } + } + + pub fn coordinates(&self) -> Vec { + self.inner.lock().unwrap().coordinates.clone() + } + + pub fn set_coordinates(&self, coordinates: impl Iterator) { + let mut inner = self.inner.lock().unwrap(); + inner.coordinates = coordinates.collect(); + for instances in inner.instances.iter() { + instances.coordinates(inner.coordinates.clone()); + } + } + + pub fn remove(self) { + for instance in self.inner.lock().unwrap().instances.drain(..) { + instance.remove(); + } + } + + fn create_instance(client: &Client, workspace_inner_clone: Arc>) -> Option> { + if let Some(workspace) = client.create_resource::(1) { + workspace.quick_assign(move |workspace, request, _| { + match request { + zext_workspace_handle_v1::Request::Activate => { + let user_data = workspace.as_ref().user_data(); + user_data.set(|| -> WorkspaceUserdata { RefCell::new(Vec::new()) }); + user_data.get::().unwrap().borrow_mut().push(PendingOperation::Activate); + }, + zext_workspace_handle_v1::Request::Deactivate => { + let user_data = workspace.as_ref().user_data(); + user_data.set(|| -> WorkspaceUserdata { RefCell::new(Vec::new()) }); + user_data.get::().unwrap().borrow_mut().push(PendingOperation::Deactivate); + }, + zext_workspace_handle_v1::Request::Remove => { + let user_data = workspace.as_ref().user_data(); + user_data.set(|| -> WorkspaceUserdata { RefCell::new(Vec::new()) }); + user_data.get::().unwrap().borrow_mut().push(PendingOperation::Remove); + }, + zext_workspace_handle_v1::Request::Destroy => { + workspace_inner_clone.lock().unwrap().instances.retain(|w| **w != *workspace); + }, + } + }); + Some(workspace) + } else { + None + } + } +} + +impl WorkspaceInner { + fn send(&self, client: &Client) { + if let Some(instance) = self.instances.iter().find(|w| w.as_ref().client().map(|c| &c == client).unwrap_or(false)) { + instance.name(self.name.clone()); + instance.state(self.states.iter().map(|x| x.to_raw() as u8).collect()); + instance.coordinates(self.coordinates.clone()); + } + } +}