Merge pull request #349 from mahkoh/jorth/workspace
ext-workspace: implement v1
This commit is contained in:
commit
67549d8cb3
22 changed files with 948 additions and 4 deletions
|
|
@ -148,6 +148,7 @@ Jay supports the following wayland protocols:
|
|||
| ext_output_image_capture_source_manager_v1 | 1 | |
|
||||
| ext_session_lock_manager_v1 | 1 | Yes |
|
||||
| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes |
|
||||
| ext_workspace_manager_v1 | 1 | Yes |
|
||||
| jay_tray_v1 | 1 | |
|
||||
| org_kde_kwin_server_decoration_manager | 1 | |
|
||||
| wl_compositor | 6 | |
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
- Add an idle grace period. During the grace period, the screen goes black but is neither
|
||||
disabled nor locked. This is similar to how android handles going idle. The default is
|
||||
5 seconds.
|
||||
- Implement ext-workspace-v1.
|
||||
|
||||
# 1.7.0 (2024-10-25)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ bitflags! {
|
|||
CAP_SEAT_MANAGER = 1 << 8,
|
||||
CAP_DRM_LEASE = 1 << 9,
|
||||
CAP_INPUT_METHOD = 1 << 10,
|
||||
CAP_WORKSPACE = 1 << 11,
|
||||
}
|
||||
|
||||
pub const CAPS_DEFAULT: ClientCaps = ClientCaps(CAP_LAYER_SHELL.0 | CAP_DRM_LEASE.0);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use {
|
|||
xdg_surface::{xdg_popup::XdgPopup, xdg_toplevel::XdgToplevel, XdgSurface},
|
||||
WlSurface,
|
||||
},
|
||||
workspace_manager::ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1,
|
||||
wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
|
||||
wp_linux_drm_syncobj_timeline_v1::WpLinuxDrmSyncobjTimelineV1,
|
||||
xdg_positioner::XdgPositioner,
|
||||
|
|
@ -39,9 +40,9 @@ use {
|
|||
},
|
||||
wire::{
|
||||
ExtDataControlSourceV1Id, ExtForeignToplevelHandleV1Id, ExtImageCaptureSourceV1Id,
|
||||
ExtImageCopyCaptureSessionV1Id, JayOutputId, JayScreencastId, JayToplevelId,
|
||||
JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId, WlPointerId, WlRegionId,
|
||||
WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
|
||||
ExtImageCopyCaptureSessionV1Id, ExtWorkspaceGroupHandleV1Id, JayOutputId,
|
||||
JayScreencastId, JayToplevelId, JayWorkspaceId, WlBufferId, WlDataSourceId, WlOutputId,
|
||||
WlPointerId, WlRegionId, WlRegistryId, WlSeatId, WlSurfaceId, WpDrmLeaseConnectorV1Id,
|
||||
WpLinuxDrmSyncobjTimelineV1Id, XdgPopupId, XdgPositionerId, XdgSurfaceId,
|
||||
XdgToplevelId, XdgWmBaseId, ZwlrDataControlSourceV1Id, ZwpPrimarySelectionSourceV1Id,
|
||||
ZwpTabletToolV2Id,
|
||||
|
|
@ -82,6 +83,8 @@ pub struct Objects {
|
|||
pub ext_copy_sessions:
|
||||
CopyHashMap<ExtImageCopyCaptureSessionV1Id, Rc<ExtImageCopyCaptureSessionV1>>,
|
||||
pub ext_data_sources: CopyHashMap<ExtDataControlSourceV1Id, Rc<ExtDataControlSourceV1>>,
|
||||
pub ext_workspace_groups:
|
||||
CopyHashMap<ExtWorkspaceGroupHandleV1Id, Rc<ExtWorkspaceGroupHandleV1>>,
|
||||
ids: RefCell<Vec<usize>>,
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +122,7 @@ impl Objects {
|
|||
foreign_toplevel_handles: Default::default(),
|
||||
ext_copy_sessions: Default::default(),
|
||||
ext_data_sources: Default::default(),
|
||||
ext_workspace_groups: Default::default(),
|
||||
ids: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
|
@ -160,6 +164,7 @@ impl Objects {
|
|||
self.foreign_toplevel_handles.clear();
|
||||
self.ext_copy_sessions.clear();
|
||||
self.ext_data_sources.clear();
|
||||
self.ext_workspace_groups.clear();
|
||||
}
|
||||
|
||||
pub fn id<T>(&self, client_data: &Client) -> Result<T, ClientError>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ use {
|
|||
jay_screencast::{perform_screencast_realloc, perform_toplevel_screencasts},
|
||||
wl_output::{OutputId, PersistentOutputState, WlOutputGlobal},
|
||||
wl_surface::{zwp_input_popup_surface_v2::input_popup_positioning, NoneSurfaceExt},
|
||||
workspace_manager::workspace_manager_done,
|
||||
},
|
||||
io_uring::{IoUring, IoUringError},
|
||||
leaks,
|
||||
|
|
@ -278,6 +279,7 @@ fn start_compositor2(
|
|||
const_40hz_latch: Default::default(),
|
||||
tray_item_ids: Default::default(),
|
||||
data_control_device_ids: Default::default(),
|
||||
workspace_managers: Default::default(),
|
||||
});
|
||||
state.tracker.register(ClientId::from_raw(0));
|
||||
create_dummy_output(&state);
|
||||
|
|
@ -446,6 +448,10 @@ fn start_global_event_handlers(
|
|||
Phase::Present,
|
||||
handle_const_40hz_latch(state.clone()),
|
||||
),
|
||||
eng.spawn(
|
||||
"workspace manager done",
|
||||
workspace_manager_done(state.clone()),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -593,6 +599,7 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
before_latch_event: Default::default(),
|
||||
tray_start_rel: Default::default(),
|
||||
tray_items: Default::default(),
|
||||
ext_workspace_groups: Default::default(),
|
||||
});
|
||||
let dummy_workspace = Rc::new(WorkspaceNode {
|
||||
id: state.node_ids.next(),
|
||||
|
|
@ -615,6 +622,8 @@ fn create_dummy_output(state: &Rc<State>) {
|
|||
title_texture: Default::default(),
|
||||
attention_requests: Default::default(),
|
||||
render_highlight: Default::default(),
|
||||
ext_workspaces: Default::default(),
|
||||
opt: Default::default(),
|
||||
});
|
||||
*dummy_workspace.output_link.borrow_mut() =
|
||||
Some(dummy_output.workspaces.add_last(dummy_workspace.clone()));
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ use {
|
|||
wl_shm::WlShmGlobal,
|
||||
wl_subcompositor::WlSubcompositorGlobal,
|
||||
wl_surface::xwayland_shell_v1::XwaylandShellV1Global,
|
||||
workspace_manager::ext_workspace_manager_v1::ExtWorkspaceManagerV1Global,
|
||||
wp_alpha_modifier_v1::WpAlphaModifierV1Global,
|
||||
wp_commit_timing_manager_v1::WpCommitTimingManagerV1Global,
|
||||
wp_content_type_manager_v1::WpContentTypeManagerV1Global,
|
||||
|
|
@ -213,6 +214,7 @@ impl Globals {
|
|||
add_singleton!(WpCommitTimingManagerV1Global);
|
||||
add_singleton!(ExtDataControlManagerV1Global);
|
||||
add_singleton!(WlFixesGlobal);
|
||||
add_singleton!(ExtWorkspaceManagerV1Global);
|
||||
}
|
||||
|
||||
pub fn add_backend_singletons(&self, backend: &Rc<dyn Backend>) {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ pub mod wl_shm;
|
|||
pub mod wl_shm_pool;
|
||||
pub mod wl_subcompositor;
|
||||
pub mod wl_surface;
|
||||
pub mod workspace_manager;
|
||||
pub mod wp_alpha_modifier_v1;
|
||||
pub mod wp_commit_timing_manager_v1;
|
||||
pub mod wp_content_type_manager_v1;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -57,6 +57,7 @@ use {
|
|||
zwp_input_popup_surface_v2::ZwpInputPopupSurfaceV2,
|
||||
NoneSurfaceExt,
|
||||
},
|
||||
workspace_manager::WorkspaceManagerState,
|
||||
wp_drm_lease_connector_v1::WpDrmLeaseConnectorV1,
|
||||
wp_drm_lease_device_v1::WpDrmLeaseDeviceV1Global,
|
||||
wp_linux_drm_syncobj_manager_v1::WpLinuxDrmSyncobjManagerV1Global,
|
||||
|
|
@ -229,6 +230,7 @@ pub struct State {
|
|||
pub const_40hz_latch: EventSource<dyn LatchListener>,
|
||||
pub tray_item_ids: TrayItemIds,
|
||||
pub data_control_device_ids: DataControlDeviceIds,
|
||||
pub workspace_managers: WorkspaceManagerState,
|
||||
}
|
||||
|
||||
// impl Drop for State {
|
||||
|
|
@ -917,6 +919,7 @@ impl State {
|
|||
self.ei_clients.clear();
|
||||
self.slow_ei_clients.clear();
|
||||
self.toplevels.clear();
|
||||
self.workspace_managers.clear();
|
||||
}
|
||||
|
||||
pub fn damage_hardware_cursors(&self, render: bool) {
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ impl ConnectorHandler {
|
|||
before_latch_event: Default::default(),
|
||||
tray_start_rel: Default::default(),
|
||||
tray_items: Default::default(),
|
||||
ext_workspace_groups: Default::default(),
|
||||
});
|
||||
on.update_visible();
|
||||
on.update_rects();
|
||||
|
|
@ -259,6 +260,7 @@ impl ConnectorHandler {
|
|||
self.state.add_global(&tray);
|
||||
self.state.tree_changed();
|
||||
on.update_presentation_type();
|
||||
self.state.workspace_managers.announce_output(&on);
|
||||
'outer: loop {
|
||||
while let Some(event) = self.data.connector.event() {
|
||||
match event {
|
||||
|
|
@ -331,6 +333,9 @@ impl ConnectorHandler {
|
|||
};
|
||||
move_ws_to_output(&ws, &target, config);
|
||||
}
|
||||
for group in on.ext_workspace_groups.lock().drain_values() {
|
||||
group.handle_destroyed();
|
||||
}
|
||||
for seat in self.state.globals.seats.lock().values() {
|
||||
seat.cursor_group().output_disconnected(&on, &target);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ use {
|
|||
zwlr_layer_surface_v1::{ExclusiveSize, ZwlrLayerSurfaceV1},
|
||||
SurfaceSendPreferredScaleVisitor, SurfaceSendPreferredTransformVisitor,
|
||||
},
|
||||
workspace_manager::{
|
||||
ext_workspace_group_handle_v1::ExtWorkspaceGroupHandleV1,
|
||||
ext_workspace_manager_v1::WorkspaceManagerId,
|
||||
},
|
||||
wp_content_type_v1::ContentType,
|
||||
zwlr_layer_shell_v1::{BACKGROUND, BOTTOM, OVERLAY, TOP},
|
||||
zwlr_screencopy_frame_v1::ZwlrScreencopyFrameV1,
|
||||
|
|
@ -97,6 +101,7 @@ pub struct OutputNode {
|
|||
pub before_latch_event: EventSource<dyn BeforeLatchListener>,
|
||||
pub tray_start_rel: Cell<i32>,
|
||||
pub tray_items: LinkedList<Rc<dyn DynTrayItem>>,
|
||||
pub ext_workspace_groups: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceGroupHandleV1>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
|
|
@ -414,6 +419,7 @@ impl OutputNode {
|
|||
self.screencasts.clear();
|
||||
self.screencopies.clear();
|
||||
self.ext_copy_sessions.clear();
|
||||
self.ext_workspace_groups.clear();
|
||||
}
|
||||
|
||||
pub fn on_spaces_changed(self: &Rc<Self>) {
|
||||
|
|
@ -635,6 +641,9 @@ impl OutputNode {
|
|||
jw.send_destroyed();
|
||||
jw.workspace.set(None);
|
||||
}
|
||||
for wh in old.ext_workspaces.lock().values() {
|
||||
wh.handle_destroyed();
|
||||
}
|
||||
old.clear();
|
||||
self.state.workspaces.remove(&old.name);
|
||||
} else {
|
||||
|
|
@ -678,7 +687,10 @@ impl OutputNode {
|
|||
title_texture: Default::default(),
|
||||
attention_requests: Default::default(),
|
||||
render_highlight: Default::default(),
|
||||
ext_workspaces: Default::default(),
|
||||
opt: Default::default(),
|
||||
});
|
||||
ws.opt.set(Some(ws.clone()));
|
||||
ws.update_has_captures();
|
||||
*ws.output_link.borrow_mut() = Some(self.workspaces.add_last(ws.clone()));
|
||||
self.state.workspaces.set(name.to_string(), ws.clone());
|
||||
|
|
@ -694,6 +706,7 @@ impl OutputNode {
|
|||
for (client, e) in clients_to_kill.values() {
|
||||
client.error(e);
|
||||
}
|
||||
self.state.workspace_managers.announce_workspace(self, &ws);
|
||||
self.schedule_update_render_data();
|
||||
ws
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ use {
|
|||
wl_surface::{
|
||||
x_surface::xwindow::Xwindow, xdg_surface::xdg_toplevel::XdgToplevel, WlSurface,
|
||||
},
|
||||
workspace_manager::{
|
||||
ext_workspace_handle_v1::ExtWorkspaceHandleV1,
|
||||
ext_workspace_manager_v1::WorkspaceManagerId,
|
||||
},
|
||||
},
|
||||
rect::Rect,
|
||||
renderer::Renderer,
|
||||
|
|
@ -25,6 +29,7 @@ use {
|
|||
copyhashmap::CopyHashMap,
|
||||
linkedlist::{LinkedList, LinkedNode, NodeRef},
|
||||
numcell::NumCell,
|
||||
opt::Opt,
|
||||
threshold_counter::ThresholdCounter,
|
||||
},
|
||||
wire::JayWorkspaceId,
|
||||
|
|
@ -60,6 +65,8 @@ pub struct WorkspaceNode {
|
|||
pub title_texture: RefCell<Option<TextTexture>>,
|
||||
pub attention_requests: ThresholdCounter,
|
||||
pub render_highlight: NumCell<u32>,
|
||||
pub ext_workspaces: CopyHashMap<WorkspaceManagerId, Rc<ExtWorkspaceHandleV1>>,
|
||||
pub opt: Rc<Opt<WorkspaceNode>>,
|
||||
}
|
||||
|
||||
impl WorkspaceNode {
|
||||
|
|
@ -68,6 +75,8 @@ impl WorkspaceNode {
|
|||
*self.output_link.borrow_mut() = None;
|
||||
self.fullscreen.set(None);
|
||||
self.jay_workspaces.clear();
|
||||
self.ext_workspaces.clear();
|
||||
self.opt.set(None);
|
||||
}
|
||||
|
||||
pub fn update_has_captures(&self) {
|
||||
|
|
@ -95,6 +104,9 @@ impl WorkspaceNode {
|
|||
|
||||
pub fn set_output(&self, output: &Rc<OutputNode>) {
|
||||
self.output.set(output.clone());
|
||||
for wh in self.ext_workspaces.lock().values() {
|
||||
wh.handle_new_output(output);
|
||||
}
|
||||
for jw in self.jay_workspaces.lock().values() {
|
||||
jw.send_output(output);
|
||||
}
|
||||
|
|
@ -171,6 +183,9 @@ impl WorkspaceNode {
|
|||
for jw in self.jay_workspaces.lock().values() {
|
||||
jw.send_visible(visible);
|
||||
}
|
||||
for wh in self.ext_workspaces.lock().values() {
|
||||
wh.handle_visibility_changed();
|
||||
}
|
||||
for stacked in self.stacked.iter() {
|
||||
stacked.stacked_prepare_set_visible();
|
||||
}
|
||||
|
|
@ -236,6 +251,9 @@ impl WorkspaceNode {
|
|||
fn mod_attention_requested(&self, set: bool) {
|
||||
let crossed_threshold = self.attention_requests.adj(set);
|
||||
if crossed_threshold {
|
||||
for wh in self.ext_workspaces.lock().values() {
|
||||
wh.handle_urgent_changed();
|
||||
}
|
||||
self.output.get().schedule_update_render_data();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ pub mod on_drop_event;
|
|||
pub mod once;
|
||||
pub mod opaque;
|
||||
pub mod opaque_cell;
|
||||
pub mod opt;
|
||||
pub mod option_ext;
|
||||
pub mod oserror;
|
||||
pub mod page_size;
|
||||
|
|
|
|||
27
src/utils/opt.rs
Normal file
27
src/utils/opt.rs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
use {crate::utils::clonecell::CloneCell, std::rc::Rc};
|
||||
|
||||
pub struct Opt<T> {
|
||||
t: CloneCell<Option<Rc<T>>>,
|
||||
}
|
||||
|
||||
impl<T> Default for Opt<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
t: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Opt<T> {
|
||||
pub fn set(&self, t: Option<Rc<T>>) {
|
||||
self.t.set(t);
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<Rc<T>> {
|
||||
self.t.get()
|
||||
}
|
||||
|
||||
pub fn is_none(&self) -> bool {
|
||||
self.t.is_none()
|
||||
}
|
||||
}
|
||||
|
|
@ -53,4 +53,10 @@ impl<T> SyncQueue<T> {
|
|||
self.swap(&mut res);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
unsafe {
|
||||
self.el.get().deref_mut().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
31
wire/ext_workspace_group_handle_v1.txt
Normal file
31
wire/ext_workspace_group_handle_v1.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
event capabilities {
|
||||
capabilities: u32,
|
||||
}
|
||||
|
||||
event output_enter {
|
||||
output: id(wl_output),
|
||||
}
|
||||
|
||||
event output_leave {
|
||||
output: id(wl_output),
|
||||
}
|
||||
|
||||
event workspace_enter {
|
||||
workspace: id(ext_workspace_handle_v1),
|
||||
}
|
||||
|
||||
event workspace_leave {
|
||||
workspace: id(ext_workspace_handle_v1),
|
||||
}
|
||||
|
||||
event removed {
|
||||
|
||||
}
|
||||
|
||||
request create_workspace {
|
||||
workspace: str,
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
43
wire/ext_workspace_handle_v1.txt
Normal file
43
wire/ext_workspace_handle_v1.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
event id {
|
||||
id: str,
|
||||
}
|
||||
|
||||
event name {
|
||||
name: str,
|
||||
}
|
||||
|
||||
event coordinates {
|
||||
coordinates: array(u32),
|
||||
}
|
||||
|
||||
event state {
|
||||
state: u32,
|
||||
}
|
||||
|
||||
event capabilities {
|
||||
capabilities: u32,
|
||||
}
|
||||
|
||||
event removed {
|
||||
|
||||
}
|
||||
|
||||
request destroy {
|
||||
|
||||
}
|
||||
|
||||
request activate {
|
||||
|
||||
}
|
||||
|
||||
request deactivate {
|
||||
|
||||
}
|
||||
|
||||
request assign {
|
||||
workspace_group: id(ext_workspace_group_handle_v1),
|
||||
}
|
||||
|
||||
request remove {
|
||||
|
||||
}
|
||||
23
wire/ext_workspace_manager_v1.txt
Normal file
23
wire/ext_workspace_manager_v1.txt
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
event workspace_group {
|
||||
workspace_group: id(ext_workspace_group_handle_v1),
|
||||
}
|
||||
|
||||
event workspace {
|
||||
workspace: id(ext_workspace_handle_v1),
|
||||
}
|
||||
|
||||
request commit {
|
||||
|
||||
}
|
||||
|
||||
event done {
|
||||
|
||||
}
|
||||
|
||||
event finished {
|
||||
|
||||
}
|
||||
|
||||
request stop {
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue