Similarly to https://github.com/pop-os/cosmic-comp/pull/1061, track a weak reference to the manager each workspace/group instance was created from, instead of just matching by client.
641 lines
22 KiB
Rust
641 lines
22 KiB
Rust
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
use std::{collections::HashSet, sync::Mutex};
|
|
|
|
use smithay::{
|
|
output::Output,
|
|
reexports::wayland_server::{
|
|
backend::{ClientId, GlobalId},
|
|
protocol::{wl_output::WlOutput, wl_surface::WlSurface},
|
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
|
},
|
|
utils::{user_data::UserDataMap, IsAlive, Logical, Rectangle},
|
|
wayland::foreign_toplevel_list::{
|
|
ForeignToplevelHandle, ForeignToplevelListHandler, ForeignToplevelListState,
|
|
},
|
|
};
|
|
|
|
use crate::utils::prelude::{Global, OutputExt, RectGlobalExt};
|
|
|
|
use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState};
|
|
|
|
use cosmic_protocols::toplevel_info::v1::server::{
|
|
zcosmic_toplevel_handle_v1::{self, State as States, ZcosmicToplevelHandleV1},
|
|
zcosmic_toplevel_info_v1::{self, ZcosmicToplevelInfoV1},
|
|
};
|
|
use tracing::error;
|
|
|
|
pub trait Window: IsAlive + Clone + PartialEq + Send {
|
|
fn title(&self) -> String;
|
|
fn app_id(&self) -> String;
|
|
fn is_activated(&self) -> bool;
|
|
fn is_maximized(&self) -> bool;
|
|
fn is_fullscreen(&self) -> bool;
|
|
fn is_minimized(&self) -> bool;
|
|
fn is_sticky(&self) -> bool;
|
|
fn is_resizing(&self) -> bool;
|
|
fn global_geometry(&self) -> Option<Rectangle<i32, Global>>;
|
|
fn user_data(&self) -> &UserDataMap;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ToplevelInfoState<D, W: Window> {
|
|
dh: DisplayHandle,
|
|
pub(super) toplevels: Vec<W>,
|
|
instances: Vec<ZcosmicToplevelInfoV1>,
|
|
dirty: bool,
|
|
last_dirty: bool,
|
|
pub(in crate::wayland) foreign_toplevel_list: ForeignToplevelListState,
|
|
global: GlobalId,
|
|
_dispatch_data: std::marker::PhantomData<D>,
|
|
}
|
|
|
|
pub trait ToplevelInfoHandler: ForeignToplevelListHandler + WorkspaceHandler + Sized {
|
|
type Window: Window;
|
|
fn toplevel_info_state(&self) -> &ToplevelInfoState<Self, Self::Window>;
|
|
fn toplevel_info_state_mut(&mut self) -> &mut ToplevelInfoState<Self, Self::Window>;
|
|
}
|
|
|
|
pub struct ToplevelInfoGlobalData {
|
|
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub(super) struct ToplevelStateInner {
|
|
foreign_handle: Option<ForeignToplevelHandle>,
|
|
instances: Vec<(Weak<ZcosmicToplevelInfoV1>, ZcosmicToplevelHandleV1)>,
|
|
outputs: Vec<Output>,
|
|
workspaces: Vec<WorkspaceHandle>,
|
|
pub(super) rectangles: Vec<(Weak<WlSurface>, Rectangle<i32, Logical>)>,
|
|
}
|
|
pub(super) type ToplevelState = Mutex<ToplevelStateInner>;
|
|
|
|
impl ToplevelStateInner {
|
|
fn from_foreign(foreign_handle: ForeignToplevelHandle) -> Mutex<Self> {
|
|
Mutex::new(ToplevelStateInner {
|
|
foreign_handle: Some(foreign_handle),
|
|
..Default::default()
|
|
})
|
|
}
|
|
|
|
pub fn foreign_handle(&self) -> Option<&ForeignToplevelHandle> {
|
|
self.foreign_handle.as_ref()
|
|
}
|
|
|
|
pub fn in_workspace(&self, handle: &WorkspaceHandle) -> bool {
|
|
self.workspaces.contains(handle)
|
|
}
|
|
}
|
|
|
|
pub struct ToplevelHandleStateInner<W: Window> {
|
|
outputs: Vec<Output>,
|
|
geometry: Option<Rectangle<i32, Global>>,
|
|
wl_outputs: HashSet<WlOutput>,
|
|
workspaces: Vec<WorkspaceHandle>,
|
|
title: String,
|
|
app_id: String,
|
|
states: Option<Vec<States>>,
|
|
pub(super) window: Option<W>,
|
|
}
|
|
pub type ToplevelHandleState<W> = Mutex<ToplevelHandleStateInner<W>>;
|
|
|
|
impl<W: Window> ToplevelHandleStateInner<W> {
|
|
fn from_window(window: &W) -> ToplevelHandleState<W> {
|
|
ToplevelHandleState::new(ToplevelHandleStateInner {
|
|
outputs: Vec::new(),
|
|
geometry: None,
|
|
wl_outputs: HashSet::new(),
|
|
workspaces: Vec::new(),
|
|
title: String::new(),
|
|
app_id: String::new(),
|
|
states: None,
|
|
window: Some(window.clone()),
|
|
})
|
|
}
|
|
|
|
fn empty() -> ToplevelHandleState<W> {
|
|
ToplevelHandleState::new(ToplevelHandleStateInner {
|
|
outputs: Vec::new(),
|
|
geometry: None,
|
|
wl_outputs: HashSet::new(),
|
|
workspaces: Vec::new(),
|
|
title: String::new(),
|
|
app_id: String::new(),
|
|
states: None,
|
|
window: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl<D, W> GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData, D>
|
|
for ToplevelInfoState<D, W>
|
|
where
|
|
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
|
+ ToplevelInfoHandler<Window = W>
|
|
+ 'static,
|
|
W: Window + 'static,
|
|
{
|
|
fn bind(
|
|
state: &mut D,
|
|
dh: &DisplayHandle,
|
|
_client: &Client,
|
|
resource: New<ZcosmicToplevelInfoV1>,
|
|
_global_data: &ToplevelInfoGlobalData,
|
|
data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
let instance = data_init.init(resource, ());
|
|
for window in &state.toplevel_info_state().toplevels {
|
|
send_toplevel_to_client::<D, W>(dh, state.workspace_state(), &instance, window);
|
|
}
|
|
state.toplevel_info_state_mut().instances.push(instance);
|
|
}
|
|
|
|
fn can_view(client: Client, global_data: &ToplevelInfoGlobalData) -> bool {
|
|
(global_data.filter)(&client)
|
|
}
|
|
}
|
|
|
|
impl<D, W> Dispatch<ZcosmicToplevelInfoV1, (), D> for ToplevelInfoState<D, W>
|
|
where
|
|
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
|
+ ToplevelInfoHandler<Window = W>
|
|
+ 'static,
|
|
W: Window + 'static,
|
|
{
|
|
fn request(
|
|
state: &mut D,
|
|
_client: &Client,
|
|
obj: &ZcosmicToplevelInfoV1,
|
|
request: zcosmic_toplevel_info_v1::Request,
|
|
_data: &(),
|
|
_dh: &DisplayHandle,
|
|
data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
match request {
|
|
zcosmic_toplevel_info_v1::Request::GetCosmicToplevel {
|
|
cosmic_toplevel,
|
|
foreign_toplevel,
|
|
} => {
|
|
let toplevel_state = state.toplevel_info_state();
|
|
if let Some(window) = toplevel_state.toplevels.iter().find(|w| {
|
|
w.user_data().get::<ToplevelState>().and_then(|inner| {
|
|
inner
|
|
.lock()
|
|
.unwrap()
|
|
.foreign_handle
|
|
.as_ref()
|
|
.map(|handle| handle.identifier())
|
|
}) == ForeignToplevelHandle::from_resource(&foreign_toplevel)
|
|
.map(|handle| handle.identifier())
|
|
}) {
|
|
let instance = data_init.init(
|
|
cosmic_toplevel,
|
|
ToplevelHandleStateInner::from_window(window),
|
|
);
|
|
window
|
|
.user_data()
|
|
.get::<ToplevelState>()
|
|
.unwrap()
|
|
.lock()
|
|
.unwrap()
|
|
.instances
|
|
.push((obj.downgrade(), instance));
|
|
} else {
|
|
let _ = data_init.init(cosmic_toplevel, ToplevelHandleStateInner::empty());
|
|
error!(?foreign_toplevel, "Toplevel for foreign-toplevel-list not registered for cosmic-toplevel-info.");
|
|
}
|
|
}
|
|
zcosmic_toplevel_info_v1::Request::Stop => {
|
|
state
|
|
.toplevel_info_state_mut()
|
|
.instances
|
|
.retain(|i| i != obj);
|
|
obj.finished();
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn destroyed(state: &mut D, _client: ClientId, resource: &ZcosmicToplevelInfoV1, _data: &()) {
|
|
state
|
|
.toplevel_info_state_mut()
|
|
.instances
|
|
.retain(|i| i != resource);
|
|
}
|
|
}
|
|
|
|
impl<D, W> Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>, D> for ToplevelInfoState<D, W>
|
|
where
|
|
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
|
+ ToplevelInfoHandler<Window = W>
|
|
+ 'static,
|
|
W: Window,
|
|
{
|
|
fn request(
|
|
_state: &mut D,
|
|
_client: &Client,
|
|
_obj: &ZcosmicToplevelHandleV1,
|
|
request: zcosmic_toplevel_handle_v1::Request,
|
|
_data: &ToplevelHandleState<W>,
|
|
_dh: &DisplayHandle,
|
|
_data_init: &mut DataInit<'_, D>,
|
|
) {
|
|
match request {
|
|
zcosmic_toplevel_handle_v1::Request::Destroy => {}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
fn destroyed(
|
|
state: &mut D,
|
|
_client: ClientId,
|
|
resource: &ZcosmicToplevelHandleV1,
|
|
_data: &ToplevelHandleState<W>,
|
|
) {
|
|
for toplevel in &state.toplevel_info_state_mut().toplevels {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
state
|
|
.lock()
|
|
.unwrap()
|
|
.instances
|
|
.retain(|(_, i)| i != resource);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn toplevel_enter_output(toplevel: &impl Window, output: &Output) {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
state.lock().unwrap().outputs.push(output.clone());
|
|
}
|
|
}
|
|
|
|
pub fn toplevel_leave_output(toplevel: &impl Window, output: &Output) {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
state.lock().unwrap().outputs.retain(|o| o != output);
|
|
}
|
|
}
|
|
|
|
pub fn toplevel_enter_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
state.lock().unwrap().workspaces.push(workspace.clone());
|
|
}
|
|
}
|
|
|
|
pub fn toplevel_leave_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
state.lock().unwrap().workspaces.retain(|w| w != workspace);
|
|
}
|
|
}
|
|
|
|
impl<D, W> ToplevelInfoState<D, W>
|
|
where
|
|
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
|
+ ForeignToplevelListHandler
|
|
+ ToplevelInfoHandler<Window = W>
|
|
+ 'static,
|
|
W: Window + 'static,
|
|
{
|
|
pub fn new<F>(dh: &DisplayHandle, client_filter: F) -> ToplevelInfoState<D, W>
|
|
where
|
|
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static,
|
|
{
|
|
let global = dh.create_global::<D, ZcosmicToplevelInfoV1, _>(
|
|
2,
|
|
ToplevelInfoGlobalData {
|
|
filter: Box::new(client_filter.clone()),
|
|
},
|
|
);
|
|
let foreign_toplevel_list =
|
|
ForeignToplevelListState::new_with_filter::<D>(dh, client_filter);
|
|
ToplevelInfoState {
|
|
dh: dh.clone(),
|
|
toplevels: Vec::new(),
|
|
instances: Vec::new(),
|
|
dirty: false,
|
|
last_dirty: false,
|
|
foreign_toplevel_list,
|
|
global,
|
|
_dispatch_data: std::marker::PhantomData,
|
|
}
|
|
}
|
|
|
|
pub fn new_toplevel(&mut self, toplevel: &W, workspace_state: &WorkspaceState<D>) {
|
|
let toplevel_handle = self
|
|
.foreign_toplevel_list
|
|
.new_toplevel::<D>(toplevel.title(), toplevel.app_id());
|
|
toplevel
|
|
.user_data()
|
|
.insert_if_missing(move || ToplevelStateInner::from_foreign(toplevel_handle));
|
|
for instance in &self.instances {
|
|
send_toplevel_to_client::<D, W>(&self.dh, workspace_state, instance, toplevel);
|
|
}
|
|
self.toplevels.push(toplevel.clone());
|
|
self.dirty = true;
|
|
}
|
|
|
|
pub fn remove_toplevel(&mut self, toplevel: &W) {
|
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
|
let mut state_inner = state.lock().unwrap();
|
|
for (_info, handle) in &state_inner.instances {
|
|
// don't send events to stopped instances
|
|
if handle.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE
|
|
&& self
|
|
.instances
|
|
.iter()
|
|
.any(|i| i.id().same_client_as(&handle.id()))
|
|
{
|
|
handle.closed();
|
|
}
|
|
}
|
|
if let Some(handle) = state_inner.foreign_handle.take() {
|
|
self.foreign_toplevel_list.remove_toplevel(&handle);
|
|
}
|
|
*state_inner = Default::default();
|
|
self.dirty = true;
|
|
}
|
|
self.toplevels.retain(|w| w != toplevel);
|
|
}
|
|
|
|
pub fn refresh(&mut self, workspace_state: &WorkspaceState<D>) {
|
|
let mut dirty = std::mem::replace(&mut self.dirty, false);
|
|
|
|
self.toplevels.retain(|window| {
|
|
let mut state = window
|
|
.user_data()
|
|
.get::<ToplevelState>()
|
|
.unwrap()
|
|
.lock()
|
|
.unwrap();
|
|
state
|
|
.rectangles
|
|
.retain(|(surface, _)| surface.upgrade().is_ok());
|
|
if window.alive() {
|
|
std::mem::drop(state);
|
|
for instance in &self.instances {
|
|
let changed = send_toplevel_to_client::<D, W>(
|
|
&self.dh,
|
|
workspace_state,
|
|
instance,
|
|
window,
|
|
);
|
|
dirty = dirty || changed;
|
|
}
|
|
true
|
|
} else {
|
|
for (_info, handle) in &state.instances {
|
|
// don't send events to stopped instances
|
|
if handle.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE
|
|
&& self
|
|
.instances
|
|
.iter()
|
|
.any(|i| i.id().same_client_as(&handle.id()))
|
|
{
|
|
handle.closed();
|
|
}
|
|
}
|
|
dirty = true;
|
|
false
|
|
}
|
|
});
|
|
|
|
if !dirty && self.last_dirty {
|
|
for instance in &self.instances {
|
|
if instance.version() >= zcosmic_toplevel_info_v1::EVT_DONE_SINCE {
|
|
instance.done();
|
|
}
|
|
}
|
|
}
|
|
|
|
self.last_dirty = dirty;
|
|
}
|
|
|
|
pub fn global_id(&self) -> GlobalId {
|
|
self.global.clone()
|
|
}
|
|
|
|
pub fn registered_toplevels(&self) -> impl Iterator<Item = &W> {
|
|
self.toplevels.iter()
|
|
}
|
|
}
|
|
|
|
fn send_toplevel_to_client<D, W: 'static>(
|
|
dh: &DisplayHandle,
|
|
workspace_state: &WorkspaceState<D>,
|
|
info: &ZcosmicToplevelInfoV1,
|
|
window: &W,
|
|
) -> bool
|
|
where
|
|
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
|
+ ToplevelInfoHandler<Window = W>
|
|
+ 'static,
|
|
W: Window,
|
|
{
|
|
let mut state = window
|
|
.user_data()
|
|
.get::<ToplevelState>()
|
|
.unwrap()
|
|
.lock()
|
|
.unwrap();
|
|
let (_info, instance) = match state.instances.iter().find(|(i, _)| i == info) {
|
|
Some(i) => i,
|
|
None => {
|
|
if info.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
|
if let Ok(client) = dh.get_client(info.id()) {
|
|
if let Ok(toplevel_handle) = client
|
|
.create_resource::<ZcosmicToplevelHandleV1, _, D>(
|
|
dh,
|
|
info.version(),
|
|
ToplevelHandleStateInner::from_window(window),
|
|
)
|
|
{
|
|
info.toplevel(&toplevel_handle);
|
|
state.instances.push((info.downgrade(), toplevel_handle));
|
|
state.instances.last().unwrap()
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
};
|
|
|
|
let mut handle_state = instance
|
|
.data::<ToplevelHandleState<W>>()
|
|
.unwrap()
|
|
.lock()
|
|
.unwrap();
|
|
let foreign_toplevel_handle = state.foreign_handle.as_ref();
|
|
let mut changed = false;
|
|
|
|
if handle_state.title != window.title() {
|
|
handle_state.title = window.title();
|
|
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
|
instance.title(handle_state.title.clone());
|
|
}
|
|
if let Some(handle) = foreign_toplevel_handle {
|
|
handle.send_title(&handle_state.title);
|
|
}
|
|
changed = true;
|
|
}
|
|
if handle_state.app_id != window.app_id() {
|
|
handle_state.app_id = window.app_id();
|
|
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
|
instance.app_id(handle_state.app_id.clone());
|
|
}
|
|
if let Some(handle) = foreign_toplevel_handle {
|
|
handle.send_app_id(&handle_state.app_id);
|
|
}
|
|
changed = true;
|
|
}
|
|
|
|
if handle_state.states.as_ref().map_or(true, |states| {
|
|
(states.contains(&States::Maximized) != window.is_maximized())
|
|
|| (states.contains(&States::Fullscreen) != window.is_fullscreen())
|
|
|| (states.contains(&States::Activated) != window.is_activated())
|
|
|| (states.contains(&States::Minimized) != window.is_minimized())
|
|
}) {
|
|
let mut states = Vec::new();
|
|
if window.is_maximized() {
|
|
states.push(States::Maximized);
|
|
}
|
|
if window.is_fullscreen() {
|
|
states.push(States::Fullscreen);
|
|
}
|
|
if window.is_activated() {
|
|
states.push(States::Activated);
|
|
}
|
|
if window.is_minimized() {
|
|
states.push(States::Minimized);
|
|
}
|
|
if instance.version() >= zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE
|
|
&& window.is_sticky()
|
|
{
|
|
states.push(States::Sticky);
|
|
}
|
|
handle_state.states = Some(states.clone());
|
|
|
|
let states = states
|
|
.iter()
|
|
.flat_map(|state| (*state as u32).to_ne_bytes())
|
|
.collect::<Vec<u8>>();
|
|
instance.state(states);
|
|
changed = true;
|
|
}
|
|
|
|
let mut geometry_changed = false;
|
|
if !window.is_resizing() {
|
|
let geometry = window.global_geometry();
|
|
if handle_state.geometry != geometry {
|
|
handle_state.geometry = geometry;
|
|
changed = true;
|
|
geometry_changed = true;
|
|
}
|
|
}
|
|
|
|
if let Ok(client) = dh.get_client(instance.id()) {
|
|
handle_state.outputs = state.outputs.clone();
|
|
|
|
let handle_state = &mut *handle_state;
|
|
for output in &handle_state.outputs {
|
|
let geometry = handle_state
|
|
.geometry
|
|
.filter(|_| instance.version() >= zcosmic_toplevel_handle_v1::EVT_GEOMETRY_SINCE)
|
|
.filter(|geo| output.geometry().intersection(*geo).is_some())
|
|
.map(|geo| geo.to_local(&output));
|
|
for wl_output in output.client_outputs(&client) {
|
|
if handle_state.wl_outputs.insert(wl_output.clone()) {
|
|
instance.output_enter(&wl_output);
|
|
if let Some(geo) = geometry {
|
|
instance.geometry(&wl_output, geo.loc.x, geo.loc.y, geo.size.w, geo.size.h);
|
|
}
|
|
changed = true;
|
|
} else if geometry_changed {
|
|
if let Some(geo) = geometry {
|
|
instance.geometry(&wl_output, geo.loc.x, geo.loc.y, geo.size.w, geo.size.h);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
handle_state.wl_outputs.retain(|wl_output| {
|
|
let retain = wl_output.is_alive()
|
|
&& handle_state
|
|
.outputs
|
|
.iter()
|
|
.any(|output| output.owns(wl_output));
|
|
if !retain {
|
|
instance.output_leave(&wl_output);
|
|
changed = true;
|
|
}
|
|
retain
|
|
});
|
|
}
|
|
|
|
for new_workspace in state
|
|
.workspaces
|
|
.iter()
|
|
.filter(|w| !handle_state.workspaces.contains(w))
|
|
{
|
|
for handle in workspace_state.raw_workspace_handles(&new_workspace, &instance.id()) {
|
|
instance.workspace_enter(&handle);
|
|
changed = true;
|
|
}
|
|
}
|
|
for old_workspace in handle_state
|
|
.workspaces
|
|
.iter()
|
|
.filter(|w| !state.workspaces.contains(w))
|
|
{
|
|
for handle in workspace_state.raw_workspace_handles(&old_workspace, &instance.id()) {
|
|
instance.workspace_leave(&handle);
|
|
changed = true;
|
|
}
|
|
}
|
|
handle_state.workspaces = state.workspaces.clone();
|
|
|
|
if changed {
|
|
if instance.version() < zcosmic_toplevel_info_v1::REQ_GET_COSMIC_TOPLEVEL_SINCE {
|
|
instance.done();
|
|
}
|
|
if let Some(handle) = foreign_toplevel_handle {
|
|
handle.send_done();
|
|
}
|
|
}
|
|
|
|
changed
|
|
}
|
|
|
|
pub fn window_from_handle<W: Window + 'static>(handle: ZcosmicToplevelHandleV1) -> Option<W> {
|
|
handle
|
|
.data::<ToplevelHandleState<W>>()
|
|
.and_then(|state| state.lock().unwrap().window.clone())
|
|
}
|
|
|
|
macro_rules! delegate_toplevel_info {
|
|
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, $window: ty) => {
|
|
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1: $crate::wayland::protocols::toplevel_info::ToplevelInfoGlobalData
|
|
] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState<Self, $window>);
|
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_info_v1::ZcosmicToplevelInfoV1: ()
|
|
] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState<Self, $window>);
|
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
|
cosmic_protocols::toplevel_info::v1::server::zcosmic_toplevel_handle_v1::ZcosmicToplevelHandleV1: $crate::wayland::protocols::toplevel_info::ToplevelHandleState<$window>
|
|
] => $crate::wayland::protocols::toplevel_info::ToplevelInfoState<Self, $window>);
|
|
};
|
|
}
|
|
pub(crate) use delegate_toplevel_info;
|