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 toplevel_info;
|
||||
pub mod toplevel_management;
|
||||
pub mod wlr_foreign_toplevel;
|
||||
pub mod workspace;
|
||||
pub mod xdg_activation;
|
||||
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 toplevel_info;
|
||||
pub mod toplevel_management;
|
||||
pub mod wlr_foreign_toplevel;
|
||||
pub mod workspace;
|
||||
|
||||
@@ -9,6 +9,10 @@ use smithay::{
|
||||
ext_foreign_toplevel_handle_v1::ExtForeignToplevelHandleV1,
|
||||
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::{
|
||||
Client, DataInit, Dispatch, DisplayHandle, GlobalDispatch, New, Resource, Weak,
|
||||
backend::{ClientId, GlobalId},
|
||||
@@ -24,6 +28,9 @@ use smithay::{
|
||||
|
||||
use crate::utils::prelude::{Global, OutputExt, RectGlobalExt};
|
||||
|
||||
use super::wlr_foreign_toplevel::{
|
||||
WlrForeignToplevelManagerGlobalData, WlrForeignToplevelManagerState, WlrToplevelHandleState,
|
||||
};
|
||||
use super::workspace::{WorkspaceHandle, WorkspaceHandler, WorkspaceState};
|
||||
|
||||
use cosmic_protocols::toplevel_info::v1::server::{
|
||||
@@ -53,6 +60,7 @@ pub struct ToplevelInfoState<D, W: Window> {
|
||||
dirty: bool,
|
||||
last_dirty: bool,
|
||||
pub(in crate::wayland) foreign_toplevel_list: ForeignToplevelListState,
|
||||
pub(in crate::wayland) wlr_foreign_toplevel: WlrForeignToplevelManagerState<D, W>,
|
||||
global: GlobalId,
|
||||
_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>() {
|
||||
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) {
|
||||
if let Some(state) = toplevel.user_data().get::<ToplevelState>() {
|
||||
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) {
|
||||
@@ -308,6 +318,9 @@ where
|
||||
+ Dispatch<ExtForeignToplevelHandleV1, ForeignToplevelHandle>
|
||||
+ Dispatch<ZcosmicToplevelInfoV1, ()>
|
||||
+ Dispatch<ZcosmicToplevelHandleV1, ToplevelHandleState<W>>
|
||||
+ GlobalDispatch<ZwlrForeignToplevelManagerV1, WlrForeignToplevelManagerGlobalData>
|
||||
+ Dispatch<ZwlrForeignToplevelManagerV1, ()>
|
||||
+ Dispatch<ZwlrForeignToplevelHandleV1, WlrToplevelHandleState<W>>
|
||||
+ ForeignToplevelListHandler
|
||||
+ ToplevelInfoHandler<Window = W>
|
||||
+ 'static,
|
||||
@@ -324,7 +337,9 @@ where
|
||||
},
|
||||
);
|
||||
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 {
|
||||
dh: dh.clone(),
|
||||
toplevels: Vec::new(),
|
||||
@@ -332,6 +347,7 @@ where
|
||||
dirty: false,
|
||||
last_dirty: false,
|
||||
foreign_toplevel_list,
|
||||
wlr_foreign_toplevel,
|
||||
global,
|
||||
_dispatch_data: std::marker::PhantomData,
|
||||
}
|
||||
@@ -350,6 +366,7 @@ where
|
||||
.user_data()
|
||||
.insert_if_missing(move || ToplevelStateInner::from_foreign(toplevel_handle));
|
||||
}
|
||||
self.wlr_foreign_toplevel.new_toplevel(toplevel);
|
||||
|
||||
for instance in &self.instances {
|
||||
send_toplevel_to_client::<D, W>(&self.dh, workspace_state, instance, toplevel);
|
||||
@@ -378,6 +395,7 @@ where
|
||||
*state_inner = Default::default();
|
||||
self.dirty = true;
|
||||
}
|
||||
self.wlr_foreign_toplevel.remove_toplevel(toplevel);
|
||||
self.toplevels.retain(|w| w != toplevel);
|
||||
}
|
||||
|
||||
@@ -405,6 +423,7 @@ where
|
||||
);
|
||||
dirty = dirty || changed;
|
||||
}
|
||||
self.wlr_foreign_toplevel.refresh(window);
|
||||
true
|
||||
} else {
|
||||
for (_info, handle) in &state.instances {
|
||||
@@ -434,6 +453,15 @@ where
|
||||
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 {
|
||||
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