From 67efa2c559b53d3ca90c131c06c247278783880d Mon Sep 17 00:00:00 2001 From: Shawn Wallace Date: Fri, 25 Oct 2024 02:40:33 -0400 Subject: [PATCH] Kill clients missing WM_DELETE_WINDOW protocol on close Allows the compositor to close glxgears and other similar apps. --- src/clientside.rs | 2 +- src/xstate/mod.rs | 38 +++++++++++++++++++++++++++----------- tests/integration.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 16 deletions(-) diff --git a/src/clientside.rs b/src/clientside.rs index fd1df3c..31a3aaa 100644 --- a/src/clientside.rs +++ b/src/clientside.rs @@ -1,6 +1,6 @@ use crate::server::{ObjectEvent, ObjectKey}; use std::os::unix::net::UnixStream; -use std::sync::{mpsc, Mutex, Once, OnceLock}; +use std::sync::{mpsc, Mutex, OnceLock}; use wayland_client::protocol::{ wl_buffer::WlBuffer, wl_callback::WlCallback, wl_compositor::WlCompositor, wl_keyboard::WlKeyboard, wl_output::WlOutput, wl_pointer::WlPointer, wl_region::WlRegion, diff --git a/src/xstate/mod.rs b/src/xstate/mod.rs index 48bd20c..0d6ae45 100644 --- a/src/xstate/mod.rs +++ b/src/xstate/mod.rs @@ -830,19 +830,35 @@ impl super::XConnection for Arc { } fn close_window(&mut self, window: x::Window, atoms: Self::ExtraData) { - let data = [atoms.wm_delete_window.resource_id(), 0, 0, 0, 0]; - let event = &x::ClientMessageEvent::new( + let cookie = self.send_request(&x::GetProperty { window, - atoms.wm_protocols, - x::ClientMessageData::Data32(data), - ); + delete: false, + property: atoms.wm_protocols, + r#type: x::ATOM_ATOM, + long_offset: 0, + long_length: 10, + }); + let reply = unwrap_or_skip_bad_window!(self.wait_for_reply(cookie)); - unwrap_or_skip_bad_window!(self.send_and_check_request(&x::SendEvent { - destination: x::SendEventDest::Window(window), - propagate: false, - event_mask: x::EventMask::empty(), - event, - })); + if reply.value::().contains(&atoms.wm_delete_window) { + let data = [atoms.wm_delete_window.resource_id(), 0, 0, 0, 0]; + let event = &x::ClientMessageEvent::new( + window, + atoms.wm_protocols, + x::ClientMessageData::Data32(data), + ); + + unwrap_or_skip_bad_window!(self.send_and_check_request(&x::SendEvent { + destination: x::SendEventDest::Window(window), + propagate: false, + event_mask: x::EventMask::empty(), + event, + })); + } else { + unwrap_or_skip_bad_window!(self.send_and_check_request(&x::KillClient { + resource: window.resource_id() + })) + } } fn raise_to_top(&mut self, window: x::Window) { diff --git a/tests/integration.rs b/tests/integration.rs index 37948fc..a114e7b 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -12,7 +12,7 @@ use wayland_protocols::xdg::shell::server::xdg_toplevel; use wayland_server::Resource; use xcb::{x, Xid}; use xwayland_satellite as xwls; -use xwayland_satellite::xstate::{WmHintsFlags, WmSizeHintsFlags}; +use xwayland_satellite::xstate::WmSizeHintsFlags; #[derive(Default)] struct TestDataInner { @@ -195,12 +195,18 @@ impl Fixture { /// Triggers a Wayland side toplevel Close event and processes the corresponding /// X11 side WM_DELETE_WINDOW client message - fn close_toplevel( + fn wm_delete_window( &mut self, connection: &mut Connection, window: x::Window, surface: testwl::SurfaceId, ) { + connection.set_property( + window, + x::ATOM_ATOM, + connection.atoms.wm_protocols, + &[connection.atoms.wm_delete_window], + ); self.testwl.close_toplevel(surface); connection.await_event(); let event = connection @@ -443,7 +449,7 @@ fn toplevel_flow() { Some(testwl::Vec2 { x: 150, y: 200 }) ); - f.close_toplevel(&mut connection, window, surface); + f.wm_delete_window(&mut connection, window, surface); // Simulate killing client drop(connection); @@ -498,7 +504,7 @@ fn input_focus() { .focus(); assert_eq!(win, focus); - f.close_toplevel(&mut connection, win, surface); + f.wm_delete_window(&mut connection, win, surface); } #[test] @@ -987,3 +993,31 @@ fn funny_window_title() { let data = f.testwl.get_surface_data(surface).unwrap(); assert_eq!(data.toplevel().title, Some("title".into())); } + +#[test] +fn close_window() { + let mut f = Fixture::new(); + let mut connection = Connection::new(&f.display); + let window = connection.new_window(connection.root, 0, 0, 20, 20, false); + connection.map_window(window); + f.wait_and_dispatch(); + let surface = f + .testwl + .last_created_surface_id() + .expect("No surface created"); + f.configure_and_verify_new_toplevel(&mut connection, window, surface); + f.wm_delete_window(&mut connection, window, surface); + + connection + .send_and_check_request(&x::DeleteProperty { + window, + property: connection.atoms.wm_protocols, + }) + .unwrap(); + f.testwl.close_toplevel(surface); + connection.await_event(); + f.wait_and_dispatch(); + + // Connection should no longer work (KillClient) + assert!(connection.poll_for_event().is_err()); +}