Kill clients missing WM_DELETE_WINDOW protocol on close

Allows the compositor to close glxgears and other similar apps.
This commit is contained in:
Shawn Wallace 2024-10-25 02:40:33 -04:00
parent 198ac851ee
commit 67efa2c559
3 changed files with 66 additions and 16 deletions

View file

@ -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,

View file

@ -830,19 +830,35 @@ impl super::XConnection for Arc<xcb::Connection> {
}
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::<x::Atom>().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) {

View file

@ -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());
}