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:
2026-08-09 23:07:10 +07:00
parent 6ce5e75b0b
commit 84bdd3b9df
5 changed files with 500 additions and 1 deletions
+1
View File
@@ -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);
+1
View File
@@ -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;
+29 -1
View File
@@ -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;