Skip to content

Commit

Permalink
Add XdgShellHandler::parent_changed() callback
Browse files Browse the repository at this point in the history
Called when the surface parent changes, either via set_parent() or via
xdg-foreign.
  • Loading branch information
YaLTeR authored and Drakulix committed Dec 16, 2024
1 parent 6b31f72 commit 68555cf
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 20 deletions.
8 changes: 8 additions & 0 deletions src/wayland/shell/xdg/handlers/surface/toplevel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,18 @@ where
.clone()
});

let old_parent = get_parent(toplevel);
let changed = old_parent != parent_surface;

// Parent is not double buffered, we can set it directly
if !set_parent(toplevel, parent_surface) {
toplevel.post_error(xdg_toplevel::Error::InvalidParent, "invalid parent toplevel");
}

if changed {
let handle = make_toplevel_handle(toplevel);
XdgShellHandler::parent_changed(state, handle);
}
}
xdg_toplevel::Request::SetTitle { title } => {
// Title is not double buffered, we can set it directly
Expand Down
3 changes: 3 additions & 0 deletions src/wayland/shell/xdg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,9 @@ pub trait XdgShellHandler {

/// The toplevel surface set a different title.
fn title_changed(&mut self, surface: ToplevelSurface) {}

/// The parent of a toplevel surface has changed.
fn parent_changed(&mut self, surface: ToplevelSurface) {}
}

/// Shell global state
Expand Down
95 changes: 76 additions & 19 deletions src/wayland/xdg_foreign/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use wayland_server::{

use crate::wayland::{
compositor,
shell::{is_valid_parent, xdg::XdgToplevelSurfaceData},
shell::{
is_valid_parent,
xdg::{XdgShellHandler, XdgToplevelSurfaceData},
},
};

use super::{
Expand Down Expand Up @@ -80,7 +83,10 @@ where
}
}

impl<D: XdgForeignHandler> Dispatch<ZxdgExportedV2, XdgExportedUserData, D> for XdgForeignState {
impl<D> Dispatch<ZxdgExportedV2, XdgExportedUserData, D> for XdgForeignState
where
D: XdgForeignHandler + XdgShellHandler,
{
fn request(
_state: &mut D,
_client: &Client,
Expand All @@ -95,9 +101,8 @@ impl<D: XdgForeignHandler> Dispatch<ZxdgExportedV2, XdgExportedUserData, D> for
fn destroyed(state: &mut D, _client: ClientId, _resource: &ZxdgExportedV2, data: &XdgExportedUserData) {
// Revoke the previously exported surface.
// This invalidates any relationship the importer may have set up using the xdg_imported created given the handle sent via xdg_exported.handle.
if let Some(mut state) = state.xdg_foreign_state().exported.remove(&data.handle) {
invalidate_all_relationships(&mut state);
}
invalidate_all_relationships(state, &data.handle);
state.xdg_foreign_state().exported.remove(&data.handle);
}
}

Expand Down Expand Up @@ -164,7 +169,10 @@ where
}
}

impl<D: XdgForeignHandler> Dispatch<ZxdgImportedV2, XdgImportedUserData, D> for XdgForeignState {
impl<D> Dispatch<ZxdgImportedV2, XdgImportedUserData, D> for XdgForeignState
where
D: XdgForeignHandler + XdgShellHandler,
{
fn request(
state: &mut D,
_client: &Client,
Expand All @@ -176,19 +184,22 @@ impl<D: XdgForeignHandler> Dispatch<ZxdgImportedV2, XdgImportedUserData, D> for
) {
match request {
zxdg_imported_v2::Request::SetParentOf { surface: child } => {
if let Some((_, state)) = state
if let Some((_, exported_state)) = state
.xdg_foreign_state()
.exported
.iter_mut()
.find(|(key, _)| key.as_str() == data.handle.as_str())
{
let parent = &state.exported_surface;
let parent = &exported_state.exported_surface;

let mut invalid = false;
let mut changed = false;
compositor::with_states(&child, |states| {
if let Some(data) = states.data_map.get::<XdgToplevelSurfaceData>() {
if is_valid_parent(&child, parent) {
data.lock().unwrap().parent = Some(parent.clone());
let mut role = data.lock().unwrap();
changed = role.parent.as_ref() != Some(parent);
role.parent = Some(parent.clone());
} else {
invalid = true;
}
Expand All @@ -203,7 +214,19 @@ impl<D: XdgForeignHandler> Dispatch<ZxdgImportedV2, XdgImportedUserData, D> for
return;
}

state.requested_child = Some((child, resource.clone()));
exported_state.requested_child = Some((child.clone(), resource.clone()));

if changed {
if let Some(toplevel) = state
.xdg_shell_state()
.toplevel_surfaces()
.iter()
.find(|toplevel| *toplevel.wl_surface() == child)
.cloned()
{
XdgShellHandler::parent_changed(state, toplevel);
}
}
}
}
zxdg_imported_v2::Request::Destroy => {}
Expand All @@ -212,24 +235,43 @@ impl<D: XdgForeignHandler> Dispatch<ZxdgImportedV2, XdgImportedUserData, D> for
}

fn destroyed(state: &mut D, _client: ClientId, resource: &ZxdgImportedV2, data: &XdgImportedUserData) {
if let Some((_, state)) = state
if let Some((_, exported_state)) = state
.xdg_foreign_state()
.exported
.iter_mut()
.find(|(key, _)| key.as_str() == data.handle.as_str())
{
state.imported_by.remove(resource);
invalidate_relationship_for(state, Some(resource));
exported_state.imported_by.remove(resource);
}

invalidate_relationship_for(state, &data.handle, Some(resource));
}
}

fn invalidate_all_relationships(state: &mut ExportedState) {
invalidate_relationship_for(state, None);
fn invalidate_all_relationships<D>(state: &mut D, handle: &XdgForeignHandle)
where
D: XdgForeignHandler + XdgShellHandler,
{
invalidate_relationship_for(state, handle, None);
}

fn invalidate_relationship_for(state: &mut ExportedState, invalidate_for: Option<&ZxdgImportedV2>) {
let Some((requested_child, requested_by)) = state.requested_child.as_ref() else {
fn invalidate_relationship_for<D>(
state: &mut D,
handle: &XdgForeignHandle,
invalidate_for: Option<&ZxdgImportedV2>,
) where
D: XdgForeignHandler + XdgShellHandler,
{
let Some((_, exported_state)) = state
.xdg_foreign_state()
.exported
.iter_mut()
.find(|(key, _)| key.as_str() == handle.as_str())
else {
return;
};

let Some((requested_child, requested_by)) = exported_state.requested_child.as_ref() else {
return;
};

Expand All @@ -239,16 +281,31 @@ fn invalidate_relationship_for(state: &mut ExportedState, invalidate_for: Option
}
}

let mut changed = false;
compositor::with_states(requested_child, |states| {
let Some(data) = states.data_map.get::<XdgToplevelSurfaceData>() else {
return;
};

let data = &mut *data.lock().unwrap();
if data.parent.as_ref() == Some(&state.exported_surface) {
if data.parent.as_ref() == Some(&exported_state.exported_surface) {
data.parent = None;
changed = true;
}
});

state.requested_child = None;
let requested_child = requested_child.clone();
exported_state.requested_child = None;

if changed {
if let Some(toplevel) = state
.xdg_shell_state()
.toplevel_surfaces()
.iter()
.find(|toplevel| *toplevel.wl_surface() == requested_child)
.cloned()
{
XdgShellHandler::parent_changed(state, toplevel);
}
}
}
3 changes: 2 additions & 1 deletion src/wayland/xdg_foreign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ impl XdgForeignState {

/// Macro to delegate implementation of the xdg foreign to [`XdgForeignState`].
///
/// You must also implement [`XdgForeignHandler`] to use this.
/// You must also implement [`XdgForeignHandler`] and
/// [`XdgShellHandler`](crate::wayland::shell::xdg::XdgShellHandler) to use this.
#[macro_export]
macro_rules! delegate_xdg_foreign {
($(@<$( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+>)? $ty: ty) => {
Expand Down

0 comments on commit 68555cf

Please sign in to comment.