From 3e6f892d20d918479e67d1e6c90c4be824a9d4ab Mon Sep 17 00:00:00 2001 From: Shawn Wallace Date: Fri, 8 Nov 2024 22:36:27 -0500 Subject: [PATCH] Avoid processing events from a reparented window Should fix #71 --- src/server/mod.rs | 30 +++++++++++++++++++++------ src/xstate/mod.rs | 6 ++++++ tests/integration.rs | 48 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/server/mod.rs b/src/server/mod.rs index ada47c9..e71a6a2 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -575,7 +575,10 @@ impl ServerState { } pub fn set_win_title(&mut self, window: x::Window, name: WmName) { - let win = self.windows.get_mut(&window).unwrap(); + let Some(win) = self.windows.get_mut(&window) else { + debug!("not setting title for unknown window {window:?}"); + return; + }; let new_title = match &mut win.attrs.title { Some(w) => { @@ -607,7 +610,10 @@ impl ServerState { } pub fn set_win_class(&mut self, window: x::Window, class: String) { - let win = self.windows.get_mut(&window).unwrap(); + let Some(win) = self.windows.get_mut(&window) else { + debug!("not setting class for unknown window {window:?}"); + return; + }; let class = win.attrs.class.insert(class); if let Some(key) = win.surface_key { @@ -623,12 +629,18 @@ impl ServerState { } pub fn set_win_hints(&mut self, window: x::Window, hints: WmHints) { - let win = self.windows.get_mut(&window).unwrap(); + let Some(win) = self.windows.get_mut(&window) else { + debug!("not setting hints for unknown window {window:?}"); + return; + }; win.attrs.group = hints.window_group; } pub fn set_size_hints(&mut self, window: x::Window, hints: WmNormalHints) { - let win = self.windows.get_mut(&window).unwrap(); + let Some(win) = self.windows.get_mut(&window) else { + debug!("not setting size hints for unknown window {window:?}"); + return; + }; if win.attrs.size_hints.is_none() || *win.attrs.size_hints.as_ref().unwrap() != hints { debug!("setting {window:?} hints {hints:?}"); @@ -669,7 +681,10 @@ impl ServerState { } pub fn reconfigure_window(&mut self, event: x::ConfigureNotifyEvent) { - let win = self.windows.get_mut(&event.window()).unwrap(); + let Some(win) = self.windows.get_mut(&event.window()) else { + debug!("not reconfiguring unknown window {:?}", event.window()); + return; + }; let dims = WindowDims { x: event.x(), y: event.y(), @@ -716,7 +731,10 @@ impl ServerState { pub fn map_window(&mut self, window: x::Window) { debug!("mapping {window:?}"); - let window = self.windows.get_mut(&window).unwrap(); + let Some(window) = self.windows.get_mut(&window) else { + debug!("not mapping unknown window {window:?}"); + return; + }; window.mapped = true; } diff --git a/src/xstate/mod.rs b/src/xstate/mod.rs index 8f40ce9..a8d93cd 100644 --- a/src/xstate/mod.rs +++ b/src/xstate/mod.rs @@ -235,6 +235,8 @@ impl XState { } }; } + + let mut ignored_windows = Vec::new(); while let Some(event) = self.connection.poll_for_event().unwrap() { trace!("x11 event: {event:?}"); @@ -268,6 +270,7 @@ impl XState { } else { debug!("destroying window since its parent is no longer root!"); server_state.destroy_window(e.window()); + ignored_windows.push(e.window()); } } xcb::Event::X(x::Event::MapRequest(e)) => { @@ -325,6 +328,9 @@ impl XState { server_state.destroy_window(e.window()); } xcb::Event::X(x::Event::PropertyNotify(e)) => { + if ignored_windows.contains(&e.window()) { + continue; + } self.handle_property_change(e, server_state); } xcb::Event::X(x::Event::ConfigureRequest(e)) => { diff --git a/tests/integration.rs b/tests/integration.rs index 37723e8..da3fcdf 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -428,7 +428,7 @@ fn toplevel_flow() { window, x::ATOM_STRING, x::ATOM_WM_CLASS, - &[c"boink".to_bytes()].concat(), + c"boink".to_bytes(), ); connection.set_property( window, @@ -485,6 +485,52 @@ fn reparent() { f.configure_and_verify_new_toplevel(&mut connection, child, surface); } +#[test] +fn window_properties_after_reparent() { + let mut f = Fixture::new(); + let mut connection = Connection::new(&f.display); + + let child = connection.new_window(connection.root, 0, 0, 1, 1, true); + connection.map_window(child); + f.wait_and_dispatch(); + let child_surface = f + .testwl + .last_created_surface_id() + .expect("No surface created!"); + f.configure_and_verify_new_toplevel(&mut connection, child, child_surface); + + let other = connection.new_window(connection.root, 0, 0, 100, 100, false); + connection.map_window(other); + f.wait_and_dispatch(); + let other_surface = f + .testwl + .last_created_surface_id() + .expect("No surface created!"); + f.configure_and_verify_new_toplevel(&mut connection, other, other_surface); + + connection.send_request(&x::UnmapWindow { window: child }); + let parent = connection.new_window(connection.root, 0, 0, 20, 20, false); + + connection.send_request(&x::ReparentWindow { + window: child, + parent, + x: 0, + y: 0, + }); + + // The server should get the notifications for these properties and shouldn't crash + connection.set_property( + child, + x::ATOM_WM_SIZE_HINTS, + x::ATOM_WM_NORMAL_HINTS, + &[16u32, 0, 0, 0, 0, 200, 400], + ); + connection.set_property(child, x::ATOM_STRING, x::ATOM_WM_NAME, b"title\0"); + connection.set_property(child, x::ATOM_STRING, x::ATOM_WM_CLASS, c"class".to_bytes()); + + f.wait_and_dispatch(); +} + #[test] fn input_focus() { let mut f = Fixture::new();