mirror of
https://github.com/outbackdingo/hyprcosmic-comp.git
synced 2026-08-25 14:53:22 +00:00
Patch A: zwlr_foreign_toplevel_management_v1
Implements the wlr foreign-toplevel management protocol on top of the existing ToplevelInfoState/ToplevelManagementHandler, so waybar's wlr/taskbar module works. cosmic-comp previously shipped only ext-foreign-toplevel-list, which enumerates but cannot activate or close. The wlr manager state is constructed alongside ForeignToplevelListState and driven from the same new_toplevel/remove_toplevel/refresh hooks, so no separate registration in state.rs is needed. Accessors global_id and registered_toplevels moved to an unbounded impl block: adding the wlr dispatch bounds to the shared impl made them viral and broke overlap_notify.rs, which needs neither. cargo check passes clean. NOT runtime-verified — no Wayland session here.
This commit is contained in:
@@ -37,6 +37,7 @@ pub mod shm;
|
|||||||
pub mod tablet_manager;
|
pub mod tablet_manager;
|
||||||
pub mod toplevel_info;
|
pub mod toplevel_info;
|
||||||
pub mod toplevel_management;
|
pub mod toplevel_management;
|
||||||
|
pub mod wlr_foreign_toplevel;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
pub mod xdg_activation;
|
pub mod xdg_activation;
|
||||||
pub mod xdg_foreign;
|
pub mod xdg_foreign;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
// `zwlr_foreign_toplevel_manager_v1` is implemented entirely on top of the already-existing
|
||||||
|
// `ToplevelInfoHandler` (toplevel tracking) and `ToplevelManagementHandler` (activate/close/
|
||||||
|
// maximize/minimize/fullscreen actions) implementations for `State`, see
|
||||||
|
// `wayland::protocols::wlr_foreign_toplevel`. There is nothing left to wire up here beyond
|
||||||
|
// registering the dispatch implementations.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
shell::CosmicSurface, state::State,
|
||||||
|
wayland::protocols::wlr_foreign_toplevel::delegate_wlr_foreign_toplevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
delegate_wlr_foreign_toplevel!(State, CosmicSurface);
|
||||||
@@ -10,4 +10,5 @@ pub mod output_power;
|
|||||||
pub mod overlap_notify;
|
pub mod overlap_notify;
|
||||||
pub mod toplevel_info;
|
pub mod toplevel_info;
|
||||||
pub mod toplevel_management;
|
pub mod toplevel_management;
|
||||||
|
pub mod wlr_foreign_toplevel;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ use smithay::{
|
|||||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||||
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
ext_foreign_toplevel_list_v1::ExtForeignToplevelListV1,
|
||||||
},
|
},
|
||||||
|
wayland_protocols_wlr::foreign_toplevel::v1::server::{
|
||||||
|
zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1,
|
||||||
|
zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1,
|
||||||
|
},
|
||||||
wayland_server::{
|
wayland_server::{
|
||||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
||||||
backend::{ClientId, GlobalId},
|
backend::{ClientId, GlobalId},
|
||||||
@@ -24,6 +28,9 @@ use smithay::{
|
|||||||
|
|
||||||
use crate::utils::prelude::{Global, OutputExt, RectGlobalExt};
|
use crate::utils::prelude::{Global, OutputExt, RectGlobalExt};
|
||||||
|
|
||||||
|
use super::wlr_foreign_toplevel::{
|
||||||
|
WlrForeignToplevelManagerGlobalData, WlrForeignToplevelManagerState, WlrToplevelHandleState,
|
||||||
|
};
|
||||||
use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState};
|
use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState};
|
||||||
|
|
||||||
use cosmic_protocols::toplevel_info::v1::server::{
|
use cosmic_protocols::toplevel_info::v1::server::{
|
||||||
@@ -53,6 +60,7 @@ pub struct ToplevelInfoState<D, W: Window> {
|
|||||||
dirty: bool,
|
dirty: bool,
|
||||||
last_dirty: bool,
|
last_dirty: bool,
|
||||||
pub(in crate::wayland) foreign_toplevel_list: ForeignToplevelListState,
|
pub(in crate::wayland) foreign_toplevel_list: ForeignToplevelListState,
|
||||||
|
pub(in crate::wayland) wlr_foreign_toplevel: WlrForeignToplevelManagerState<D, W>,
|
||||||
global: GlobalId,
|
global: GlobalId,
|
||||||
_dispatch_data: std::marker::PhantomData<D>,
|
_dispatch_data: std::marker::PhantomData<D>,
|
||||||
}
|
}
|
||||||
@@ -281,12 +289,14 @@ pub fn toplevel_enter_output(toplevel: &impl Window, output: &Output) {
|
|||||||
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
||||||
state.lock().unwrap().outputs.push(output.clone());
|
state.lock().unwrap().outputs.push(output.clone());
|
||||||
}
|
}
|
||||||
|
super::wlr_foreign_toplevel::toplevel_enter_output(toplevel, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toplevel_leave_output(toplevel: &impl Window, output: &Output) {
|
pub fn toplevel_leave_output(toplevel: &impl Window, output: &Output) {
|
||||||
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
||||||
state.lock().unwrap().outputs.retain(|o| o != output);
|
state.lock().unwrap().outputs.retain(|o| o != output);
|
||||||
}
|
}
|
||||||
|
super::wlr_foreign_toplevel::toplevel_leave_output(toplevel, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toplevel_enter_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) {
|
pub fn toplevel_enter_workspace(toplevel: &impl Window, workspace: &WorkspaceHandle) {
|
||||||
@@ -308,6 +318,9 @@ where
|
|||||||
+ Dispatch<ExtForeignToplevelHandleV1, ForeignToplevelHandle>
|
+ Dispatch<ExtForeignToplevelHandleV1, ForeignToplevelHandle>
|
||||||
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
||||||
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
||||||
|
+ GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||||
+ ForeignToplevelListHandler
|
+ ForeignToplevelListHandler
|
||||||
+ ToplevelInfoHandler<Window = W>
|
+ ToplevelInfoHandler<Window = W>
|
||||||
+ 'static,
|
+ 'static,
|
||||||
@@ -324,7 +337,9 @@ where
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
let foreign_toplevel_list =
|
let foreign_toplevel_list =
|
||||||
ForeignToplevelListState::new_with_filter::<D>(dh, client_filter);
|
ForeignToplevelListState::new_with_filter::<D>(dh, client_filter.clone());
|
||||||
|
let wlr_foreign_toplevel: WlrForeignToplevelManagerState<D, W> =
|
||||||
|
WlrForeignToplevelManagerState::new(dh, client_filter);
|
||||||
ToplevelInfoState {
|
ToplevelInfoState {
|
||||||
dh: dh.clone(),
|
dh: dh.clone(),
|
||||||
toplevels: Vec::new(),
|
toplevels: Vec::new(),
|
||||||
@@ -332,6 +347,7 @@ where
|
|||||||
dirty: false,
|
dirty: false,
|
||||||
last_dirty: false,
|
last_dirty: false,
|
||||||
foreign_toplevel_list,
|
foreign_toplevel_list,
|
||||||
|
wlr_foreign_toplevel,
|
||||||
global,
|
global,
|
||||||
_dispatch_data: std::marker::PhantomData,
|
_dispatch_data: std::marker::PhantomData,
|
||||||
}
|
}
|
||||||
@@ -350,6 +366,7 @@ where
|
|||||||
.user_data()
|
.user_data()
|
||||||
.insert_if_missing(move || ToplevelStateInner::from_foreign(toplevel_handle));
|
.insert_if_missing(move || ToplevelStateInner::from_foreign(toplevel_handle));
|
||||||
}
|
}
|
||||||
|
self.wlr_foreign_toplevel.new_toplevel(toplevel);
|
||||||
|
|
||||||
for instance in &self.instances {
|
for instance in &self.instances {
|
||||||
send_toplevel_to_client::<D, W>(&self.dh, workspace_state, instance, toplevel);
|
send_toplevel_to_client::<D, W>(&self.dh, workspace_state, instance, toplevel);
|
||||||
@@ -378,6 +395,7 @@ where
|
|||||||
*state_inner = Default::default();
|
*state_inner = Default::default();
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
self.wlr_foreign_toplevel.remove_toplevel(toplevel);
|
||||||
self.toplevels.retain(|w| w != toplevel);
|
self.toplevels.retain(|w| w != toplevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,6 +423,7 @@ where
|
|||||||
);
|
);
|
||||||
dirty = dirty || changed;
|
dirty = dirty || changed;
|
||||||
}
|
}
|
||||||
|
self.wlr_foreign_toplevel.refresh(window);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
for (_info, handle) in &state.instances {
|
for (_info, handle) in &state.instances {
|
||||||
@@ -434,6 +453,15 @@ where
|
|||||||
self.last_dirty = dirty;
|
self.last_dirty = dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plain accessors, deliberately kept out of the dispatch-bounded impl above.
|
||||||
|
///
|
||||||
|
/// Adding the wlr-foreign-toplevel bounds to that block made them viral: every
|
||||||
|
/// caller of these accessors would have needed the new bounds too, which broke
|
||||||
|
/// `overlap_notify.rs` for no reason. They need nothing beyond the struct
|
||||||
|
/// itself, so they belong here.
|
||||||
|
impl<D, W: Window> ToplevelInfoState<D, W> {
|
||||||
pub fn global_id(&self) -> GlobalId {
|
pub fn global_id(&self) -> GlobalId {
|
||||||
self.global.clone()
|
self.global.clone()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,455 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use std::{collections::HashSet, marker::PhantomData, sync::Mutex};
|
||||||
|
|
||||||
|
use smithay::{
|
||||||
|
input::{Seat, SeatHandler},
|
||||||
|
output::Output,
|
||||||
|
reexports::{
|
||||||
|
wayland_protocols_wlr::foreign_toplevel::v1::server::{
|
||||||
|
zwlr_foreign_toplevel_handle_v1::{
|
||||||
|
self, State as States, ZwlrForeignToplevelHandleV1,
|
||||||
|
},
|
||||||
|
zwlr_foreign_toplevel_manager_v1::{self, ZwlrForeignToplevelManagerV1},
|
||||||
|
},
|
||||||
|
wayland_server::{
|
||||||
|
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
||||||
|
backend::{ClientId, GlobalId},
|
||||||
|
protocol::wl_output::WlOutput,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils::Rectangle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
toplevel_info::{ToplevelInfoHandler, ToplevelState, Window},
|
||||||
|
toplevel_management::{ManagementWindow, ToplevelManagementHandler},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Aggregate, protocol-agnostic bookkeeping for a single window, mirroring the equivalent
|
||||||
|
/// state kept for the cosmic-toplevel-info protocol in `toplevel_info::ToplevelStateInner`.
|
||||||
|
///
|
||||||
|
/// This intentionally does not depend on `W`/`D`, so it can be updated from the same
|
||||||
|
/// output-tracking call sites used by `toplevel_info::{toplevel_enter_output, toplevel_leave_output}`
|
||||||
|
/// without requiring those free functions to become generic over the window type.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct WlrToplevelStateInner {
|
||||||
|
instances: Vec<(Weak<ZwlrForeignToplevelManagerV1>, ZwlrForeignToplevelHandleV1)>,
|
||||||
|
outputs: Vec<Output>,
|
||||||
|
}
|
||||||
|
type WlrToplevelState = Mutex<WlrToplevelStateInner>;
|
||||||
|
|
||||||
|
/// Per-instance cache used to only emit events for state that actually changed,
|
||||||
|
/// mirroring `toplevel_info::ToplevelHandleStateInner`.
|
||||||
|
pub struct WlrToplevelHandleStateInner<W: Window> {
|
||||||
|
outputs: HashSet<WlOutput>,
|
||||||
|
title: String,
|
||||||
|
app_id: String,
|
||||||
|
states: Option<Vec<States>>,
|
||||||
|
window: W,
|
||||||
|
}
|
||||||
|
pub type WlrToplevelHandleState<W> = Mutex<WlrToplevelHandleStateInner<W>>;
|
||||||
|
|
||||||
|
impl<W: Window> WlrToplevelHandleStateInner<W> {
|
||||||
|
fn new(window: &W) -> WlrToplevelHandleState<W> {
|
||||||
|
WlrToplevelHandleState::new(WlrToplevelHandleStateInner {
|
||||||
|
outputs: HashSet::new(),
|
||||||
|
title: String::new(),
|
||||||
|
app_id: String::new(),
|
||||||
|
states: None,
|
||||||
|
window: window.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WlrForeignToplevelManagerGlobalData {
|
||||||
|
filter: Box<dyn for<'a> Fn(&'a Client) -> bool + Send + Sync>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// State of the `zwlr_foreign_toplevel_manager_v1` global.
|
||||||
|
///
|
||||||
|
/// This is meant to be embedded in [`toplevel_info::ToplevelInfoState`], which already tracks
|
||||||
|
/// the authoritative list of toplevels; see `toplevel_info::ToplevelInfoState::{new_toplevel,
|
||||||
|
/// remove_toplevel, refresh}` for the call sites driving this state.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WlrForeignToplevelManagerState<D, W: Window> {
|
||||||
|
dh: DisplayHandle,
|
||||||
|
instances: Vec<ZwlrForeignToplevelManagerV1>,
|
||||||
|
global: GlobalId,
|
||||||
|
_dispatch_data: PhantomData<(D, W)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, W> WlrForeignToplevelManagerState<D, W>
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||||
|
+ ToplevelInfoHandler<Window = W>
|
||||||
|
+ 'static,
|
||||||
|
W: Window + 'static,
|
||||||
|
{
|
||||||
|
pub fn new<F>(dh: &DisplayHandle, client_filter: F) -> WlrForeignToplevelManagerState<D, W>
|
||||||
|
where
|
||||||
|
F: for<'a> Fn(&'a Client) -> bool + Send + Sync + Clone + 'static,
|
||||||
|
{
|
||||||
|
let global = dh.create_global::<D, ZwlrForeignToplevelManagerV1, _>(
|
||||||
|
3,
|
||||||
|
WlrForeignToplevelManagerGlobalData {
|
||||||
|
filter: Box::new(client_filter),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
WlrForeignToplevelManagerState {
|
||||||
|
dh: dh.clone(),
|
||||||
|
instances: Vec::new(),
|
||||||
|
global,
|
||||||
|
_dispatch_data: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_id(&self) -> GlobalId {
|
||||||
|
self.global.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A new toplevel appeared, announce it to every bound manager instance.
|
||||||
|
pub fn new_toplevel(&mut self, window: &W) {
|
||||||
|
for manager in &self.instances {
|
||||||
|
send_new_toplevel::<D, W>(&self.dh, manager, window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A toplevel was destroyed, close all of its handles.
|
||||||
|
pub fn remove_toplevel(&mut self, window: &W) {
|
||||||
|
if let Some(state) = window.user_data().get::<WlrToplevelState>() {
|
||||||
|
for (_manager, handle) in state.lock().unwrap().instances.drain(..) {
|
||||||
|
handle.closed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diff the current window state against what was last sent to each handle instance,
|
||||||
|
/// and emit the necessary events, matching `toplevel_info::send_toplevel_to_client`.
|
||||||
|
pub fn refresh(&mut self, window: &W) {
|
||||||
|
let Some(state) = window.user_data().get::<WlrToplevelState>() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let (instances, outputs) = {
|
||||||
|
let state = state.lock().unwrap();
|
||||||
|
(
|
||||||
|
state
|
||||||
|
.instances
|
||||||
|
.iter()
|
||||||
|
.map(|(_, handle)| handle.clone())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
state.outputs.clone(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
for instance in instances {
|
||||||
|
let Some(handle_state) = instance.data::<WlrToplevelHandleState<W>>() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let mut handle_state = handle_state.lock().unwrap();
|
||||||
|
let mut changed = false;
|
||||||
|
|
||||||
|
let title = window.title();
|
||||||
|
if handle_state.title != title {
|
||||||
|
handle_state.title = title.clone();
|
||||||
|
instance.title(title);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let app_id = window.app_id();
|
||||||
|
if handle_state.app_id != app_id {
|
||||||
|
handle_state.app_id = app_id.clone();
|
||||||
|
instance.app_id(app_id);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_states = window_states(window);
|
||||||
|
if handle_state.states.as_deref() != Some(new_states.as_slice()) {
|
||||||
|
handle_state.states = Some(new_states.clone());
|
||||||
|
instance.state(encode_states(&new_states));
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(client) = self.dh.get_client(instance.id()) {
|
||||||
|
for output in &outputs {
|
||||||
|
for wl_output in output.client_outputs(&client) {
|
||||||
|
if handle_state.outputs.insert(wl_output.clone()) {
|
||||||
|
instance.output_enter(&wl_output);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_state.outputs.retain(|wl_output| {
|
||||||
|
let retain =
|
||||||
|
wl_output.is_alive() && outputs.iter().any(|output| output.owns(wl_output));
|
||||||
|
if !retain {
|
||||||
|
instance.output_leave(wl_output);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
retain
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if changed {
|
||||||
|
instance.done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Track that a toplevel entered an output, to be picked up by the next `refresh`.
|
||||||
|
///
|
||||||
|
/// Mirrors `toplevel_info::toplevel_enter_output`, but operates on the wlr-specific
|
||||||
|
/// per-window state instead, since it is called from the same shell call sites.
|
||||||
|
pub fn toplevel_enter_output(toplevel: &impl Window, output: &Output) {
|
||||||
|
if let Some(state) = toplevel.user_data().get::<WlrToplevelState>() {
|
||||||
|
state.lock().unwrap().outputs.push(output.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`toplevel_enter_output`].
|
||||||
|
pub fn toplevel_leave_output(toplevel: &impl Window, output: &Output) {
|
||||||
|
if let Some(state) = toplevel.user_data().get::<WlrToplevelState>() {
|
||||||
|
state.lock().unwrap().outputs.retain(|o| o != output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_states(window: &impl Window) -> Vec<States> {
|
||||||
|
let mut states = Vec::new();
|
||||||
|
if window.is_maximized() {
|
||||||
|
states.push(States::Maximized);
|
||||||
|
}
|
||||||
|
if window.is_minimized() {
|
||||||
|
states.push(States::Minimized);
|
||||||
|
}
|
||||||
|
if window.is_activated() {
|
||||||
|
states.push(States::Activated);
|
||||||
|
}
|
||||||
|
if window.is_fullscreen() {
|
||||||
|
states.push(States::Fullscreen);
|
||||||
|
}
|
||||||
|
states
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encode_states(states: &[States]) -> Vec<u8> {
|
||||||
|
states
|
||||||
|
.iter()
|
||||||
|
.flat_map(|state| (*state as u32).to_ne_bytes())
|
||||||
|
.collect::<Vec<u8>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_new_toplevel<D, W>(dh: &DisplayHandle, manager: &ZwlrForeignToplevelManagerV1, window: &W)
|
||||||
|
where
|
||||||
|
D: Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>> + 'static,
|
||||||
|
W: Window + 'static,
|
||||||
|
{
|
||||||
|
let Ok(client) = dh.get_client(manager.id()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Ok(handle) = client.create_resource::<ZwlrForeignToplevelHandleV1, _, D>(
|
||||||
|
dh,
|
||||||
|
manager.version(),
|
||||||
|
WlrToplevelHandleStateInner::new(window),
|
||||||
|
) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
manager.toplevel(&handle);
|
||||||
|
handle.title(window.title());
|
||||||
|
handle.app_id(window.app_id());
|
||||||
|
handle.state(encode_states(&window_states(window)));
|
||||||
|
handle.done();
|
||||||
|
|
||||||
|
if let Some(handle_state) = handle.data::<WlrToplevelHandleState<W>>() {
|
||||||
|
let mut handle_state = handle_state.lock().unwrap();
|
||||||
|
handle_state.title = window.title();
|
||||||
|
handle_state.app_id = window.app_id();
|
||||||
|
handle_state.states = Some(window_states(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(state) = window.user_data().get::<WlrToplevelState>() {
|
||||||
|
state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.instances
|
||||||
|
.push((manager.downgrade(), handle));
|
||||||
|
} else {
|
||||||
|
window.user_data().insert_if_missing(|| {
|
||||||
|
Mutex::new(WlrToplevelStateInner {
|
||||||
|
instances: vec![(manager.downgrade(), handle)],
|
||||||
|
outputs: Vec::new(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, W> GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData, D>
|
||||||
|
for WlrForeignToplevelManagerState<D, W>
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||||
|
+ ToplevelInfoHandler<Window = W>
|
||||||
|
+ 'static,
|
||||||
|
W: Window + 'static,
|
||||||
|
{
|
||||||
|
fn bind(
|
||||||
|
state: &mut D,
|
||||||
|
dh: &DisplayHandle,
|
||||||
|
_client: &Client,
|
||||||
|
resource: New<ZwlrForeignToplevelManagerV1>,
|
||||||
|
_global_data: &WlrForeignToplevelManagerGlobalData,
|
||||||
|
data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
let instance = data_init.init(resource, ());
|
||||||
|
for window in state
|
||||||
|
.toplevel_info_state()
|
||||||
|
.registered_toplevels()
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
send_new_toplevel::<D, W>(dh, &instance, &window);
|
||||||
|
}
|
||||||
|
state
|
||||||
|
.toplevel_info_state_mut()
|
||||||
|
.wlr_foreign_toplevel
|
||||||
|
.instances
|
||||||
|
.push(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_view(client: Client, global_data: &WlrForeignToplevelManagerGlobalData) -> bool {
|
||||||
|
(global_data.filter)(&client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, W> Dispatch<ZwlrForeignToplevelManagerV1, (), D> for WlrForeignToplevelManagerState<D, W>
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||||
|
+ ToplevelInfoHandler<Window = W>
|
||||||
|
+ 'static,
|
||||||
|
W: Window + 'static,
|
||||||
|
{
|
||||||
|
fn request(
|
||||||
|
state: &mut D,
|
||||||
|
_client: &Client,
|
||||||
|
obj: &ZwlrForeignToplevelManagerV1,
|
||||||
|
request: zwlr_foreign_toplevel_manager_v1::Request,
|
||||||
|
_data: &(),
|
||||||
|
_dh: &DisplayHandle,
|
||||||
|
_data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
if let zwlr_foreign_toplevel_manager_v1::Request::Stop = request {
|
||||||
|
state
|
||||||
|
.toplevel_info_state_mut()
|
||||||
|
.wlr_foreign_toplevel
|
||||||
|
.instances
|
||||||
|
.retain(|i| i != obj);
|
||||||
|
obj.finished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroyed(state: &mut D, _client: ClientId, resource: &ZwlrForeignToplevelManagerV1, _data: &()) {
|
||||||
|
state
|
||||||
|
.toplevel_info_state_mut()
|
||||||
|
.wlr_foreign_toplevel
|
||||||
|
.instances
|
||||||
|
.retain(|i| i != resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D, W> Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>, D>
|
||||||
|
for WlrForeignToplevelManagerState<D, W>
|
||||||
|
where
|
||||||
|
D: GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||||
|
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||||
|
+ ToplevelInfoHandler<Window = W>
|
||||||
|
+ ToplevelManagementHandler
|
||||||
|
+ SeatHandler
|
||||||
|
+ 'static,
|
||||||
|
W: Window + ManagementWindow + 'static,
|
||||||
|
{
|
||||||
|
fn request(
|
||||||
|
state: &mut D,
|
||||||
|
_client: &Client,
|
||||||
|
_obj: &ZwlrForeignToplevelHandleV1,
|
||||||
|
request: zwlr_foreign_toplevel_handle_v1::Request,
|
||||||
|
data: &WlrToplevelHandleState<W>,
|
||||||
|
dh: &DisplayHandle,
|
||||||
|
_data_init: &mut DataInit<'_, D>,
|
||||||
|
) {
|
||||||
|
let window = data.lock().unwrap().window.clone();
|
||||||
|
if !window.alive() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
match request {
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetMaximized => state.maximize(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetMaximized => state.unmaximize(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetMinimized => state.minimize(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetMinimized => state.unminimize(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::Activate { seat } => {
|
||||||
|
state.activate(dh, &window, Seat::from_resource(&seat))
|
||||||
|
}
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetFullscreen { output } => {
|
||||||
|
state.fullscreen(dh, &window, output.as_ref().and_then(Output::from_resource))
|
||||||
|
}
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::UnsetFullscreen => state.unfullscreen(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::Close => state.close(dh, &window),
|
||||||
|
zwlr_foreign_toplevel_handle_v1::Request::SetRectangle {
|
||||||
|
surface,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
} => {
|
||||||
|
if let Some(toplevel_state) = window.user_data().get::<ToplevelState>() {
|
||||||
|
let mut toplevel_state = toplevel_state.lock().unwrap();
|
||||||
|
toplevel_state.rectangles.retain(|(s, _)| s.id() != surface.id());
|
||||||
|
if width != 0 || height != 0 {
|
||||||
|
toplevel_state.rectangles.push((
|
||||||
|
surface.downgrade(),
|
||||||
|
Rectangle::new((x, y).into(), (width, height).into()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn destroyed(
|
||||||
|
_state: &mut D,
|
||||||
|
_client: ClientId,
|
||||||
|
resource: &ZwlrForeignToplevelHandleV1,
|
||||||
|
data: &WlrToplevelHandleState<W>,
|
||||||
|
) {
|
||||||
|
let window = data.lock().unwrap().window.clone();
|
||||||
|
if let Some(state) = window.user_data().get::<WlrToplevelState>() {
|
||||||
|
state
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.instances
|
||||||
|
.retain(|(_, handle)| handle != resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! delegate_wlr_foreign_toplevel {
|
||||||
|
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty, $window: ty) => {
|
||||||
|
smithay::reexports::wayland_server::delegate_global_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: $crate::wayland::protocols::wlr_foreign_toplevel::WlrForeignToplevelManagerGlobalData
|
||||||
|
] => $crate::wayland::protocols::wlr_foreign_toplevel::WlrForeignToplevelManagerState<Self, $window>);
|
||||||
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_manager_v1::ZwlrForeignToplevelManagerV1: ()
|
||||||
|
] => $crate::wayland::protocols::wlr_foreign_toplevel::WlrForeignToplevelManagerState<Self, $window>);
|
||||||
|
smithay::reexports::wayland_server::delegate_dispatch!($(@< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $ty: [
|
||||||
|
smithay::reexports::wayland_protocols_wlr::foreign_toplevel::v1::server::zwlr_foreign_toplevel_handle_v1::ZwlrForeignToplevelHandleV1: $crate::wayland::protocols::wlr_foreign_toplevel::WlrToplevelHandleState<$window>
|
||||||
|
] => $crate::wayland::protocols::wlr_foreign_toplevel::WlrForeignToplevelManagerState<Self, $window>);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
pub(crate) use delegate_wlr_foreign_toplevel;
|
||||||
Reference in New Issue
Block a user