toplevel-info/mgmt: Update to v2/v3
This commit is contained in:
parent
bbda6fb13d
commit
9c7c41c508
11 changed files with 270 additions and 119 deletions
|
|
@ -10,14 +10,20 @@ use smithay::{
|
|||
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;
|
||||
|
|
@ -26,6 +32,8 @@ pub trait Window: IsAlive + Clone + PartialEq + Send {
|
|||
fn is_maximized(&self) -> bool;
|
||||
fn is_fullscreen(&self) -> bool;
|
||||
fn is_minimized(&self) -> bool;
|
||||
fn is_sticky(&self) -> bool;
|
||||
fn global_geometry(&self) -> Option<Rectangle<i32, Global>>;
|
||||
fn user_data(&self) -> &UserDataMap;
|
||||
}
|
||||
|
||||
|
|
@ -34,11 +42,14 @@ 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: WorkspaceHandler + Sized {
|
||||
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>;
|
||||
|
|
@ -50,6 +61,7 @@ pub struct ToplevelInfoGlobalData {
|
|||
|
||||
#[derive(Default)]
|
||||
pub(super) struct ToplevelStateInner {
|
||||
foreign_handle: Option<ForeignToplevelHandle>,
|
||||
instances: Vec<ZcosmicToplevelHandleV1>,
|
||||
outputs: Vec<Output>,
|
||||
workspaces: Vec<WorkspaceHandle>,
|
||||
|
|
@ -57,8 +69,18 @@ pub(super) struct ToplevelStateInner {
|
|||
}
|
||||
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 struct ToplevelHandleStateInner<W: Window> {
|
||||
outputs: Vec<Output>,
|
||||
geometry: Option<Rectangle<i32, Global>>,
|
||||
wl_outputs: HashSet<WlOutput>,
|
||||
workspaces: Vec<WorkspaceHandle>,
|
||||
title: String,
|
||||
|
|
@ -72,6 +94,7 @@ 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(),
|
||||
|
|
@ -119,7 +142,7 @@ where
|
|||
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
||||
+ ToplevelInfoHandler<Window = W>
|
||||
+ 'static,
|
||||
W: Window,
|
||||
W: Window + 'static,
|
||||
{
|
||||
fn request(
|
||||
state: &mut D,
|
||||
|
|
@ -128,14 +151,47 @@ where
|
|||
request: zcosmic_toplevel_info_v1::Request,
|
||||
_data: &(),
|
||||
_dh: &DisplayHandle,
|
||||
_data_init: &mut DataInit<'_, D>,
|
||||
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(instance);
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -216,37 +272,47 @@ 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 + 'static,
|
||||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static,
|
||||
{
|
||||
let global = dh.create_global::<D, ZcosmicToplevelInfoV1, _>(
|
||||
1,
|
||||
2,
|
||||
ToplevelInfoGlobalData {
|
||||
filter: Box::new(client_filter),
|
||||
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(ToplevelState::default);
|
||||
.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) {
|
||||
|
|
@ -254,20 +320,27 @@ where
|
|||
let mut state_inner = state.lock().unwrap();
|
||||
for handle in &state_inner.instances {
|
||||
// don't send events to stopped instances
|
||||
if self
|
||||
.instances
|
||||
.iter()
|
||||
.any(|i| i.id().same_client_as(&handle.id()))
|
||||
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()
|
||||
|
|
@ -281,23 +354,41 @@ where
|
|||
if window.alive() {
|
||||
std::mem::drop(state);
|
||||
for instance in &self.instances {
|
||||
send_toplevel_to_client::<D, W>(&self.dh, workspace_state, instance, window);
|
||||
let changed = send_toplevel_to_client::<D, W>(
|
||||
&self.dh,
|
||||
workspace_state,
|
||||
instance,
|
||||
window,
|
||||
);
|
||||
dirty = dirty || changed;
|
||||
}
|
||||
true
|
||||
} else {
|
||||
for handle in &state.instances {
|
||||
// don't send events to stopped instances
|
||||
if self
|
||||
.instances
|
||||
.iter()
|
||||
.any(|i| i.id().same_client_as(&handle.id()))
|
||||
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 {
|
||||
|
|
@ -310,7 +401,8 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
workspace_state: &WorkspaceState<D>,
|
||||
info: &ZcosmicToplevelInfoV1,
|
||||
window: &W,
|
||||
) where
|
||||
) -> bool
|
||||
where
|
||||
D: GlobalDispatch<ZcosmicToplevelInfoV1, ToplevelInfoGlobalData>
|
||||
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
||||
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
||||
|
|
@ -331,22 +423,26 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
{
|
||||
Some(i) => i,
|
||||
None => {
|
||||
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(toplevel_handle);
|
||||
state.instances.last().unwrap()
|
||||
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(toplevel_handle);
|
||||
state.instances.last().unwrap()
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -356,15 +452,27 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
.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();
|
||||
instance.title(handle_state.title.clone());
|
||||
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();
|
||||
instance.app_id(handle_state.app_id.clone());
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -386,6 +494,11 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
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 = states.clone();
|
||||
|
||||
let states: Vec<u8> = {
|
||||
|
|
@ -400,15 +513,35 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
changed = true;
|
||||
}
|
||||
|
||||
let geometry = window.global_geometry();
|
||||
let mut geometry_changed = false;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -449,8 +582,15 @@ fn send_toplevel_to_client<D, W: 'static>(
|
|||
handle_state.workspaces = state.workspaces.clone();
|
||||
|
||||
if changed {
|
||||
instance.done();
|
||||
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> {
|
||||
|
|
|
|||
|
|
@ -61,6 +61,9 @@ where
|
|||
fn unmaximize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
|
||||
fn minimize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
|
||||
fn unminimize(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
|
||||
fn set_sticky(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {}
|
||||
fn unset_sticky(&mut self, dh: &DisplayHandle, window: &<Self as ToplevelInfoHandler>::Window) {
|
||||
}
|
||||
fn move_to_workspace(
|
||||
&mut self,
|
||||
dh: &DisplayHandle,
|
||||
|
|
@ -113,7 +116,7 @@ impl ToplevelManagementState {
|
|||
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + 'static,
|
||||
{
|
||||
let global = dh.create_global::<D, ZcosmicToplevelManagerV1, _>(
|
||||
2,
|
||||
3,
|
||||
ToplevelManagerGlobalData {
|
||||
filter: Box::new(client_filter),
|
||||
},
|
||||
|
|
@ -216,6 +219,14 @@ where
|
|||
let window = window_from_handle(toplevel).unwrap();
|
||||
state.unminimize(dh, &window);
|
||||
}
|
||||
zcosmic_toplevel_manager_v1::Request::SetSticky { toplevel } => {
|
||||
let window = window_from_handle(toplevel).unwrap();
|
||||
state.set_sticky(dh, &window);
|
||||
}
|
||||
zcosmic_toplevel_manager_v1::Request::UnsetSticky { toplevel } => {
|
||||
let window = window_from_handle(toplevel).unwrap();
|
||||
state.unset_sticky(dh, &window);
|
||||
}
|
||||
zcosmic_toplevel_manager_v1::Request::SetRectangle {
|
||||
toplevel,
|
||||
surface,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue