diff --git a/local/recipes/wayland/redbear-compositor/source/src/main.rs b/local/recipes/wayland/redbear-compositor/source/src/main.rs index 1864b092ed..af924905a2 100644 --- a/local/recipes/wayland/redbear-compositor/source/src/main.rs +++ b/local/recipes/wayland/redbear-compositor/source/src/main.rs @@ -1804,9 +1804,6 @@ impl Compositor { if iface == "wl_seat" { self.send_seat_capabilities(client_id, stream, new_id, object_version)?; } - if iface == "xdg_wm_base" { - self.send_xdg_wm_base_ping(client_id, stream, new_id)?; - } } } _ => { @@ -3605,6 +3602,7 @@ OBJECT_TYPE_WP_PRESENTATION_FEEDBACK => match opcode { self.write_event(client_id, stream, &msg, "xdg_popup.repositioned") } + #[allow(dead_code)] fn send_xdg_wm_base_ping( &self, client_id: u32, diff --git a/local/recipes/wayland/redbear-compositor/source/tests/integration_test.rs b/local/recipes/wayland/redbear-compositor/source/tests/integration_test.rs index 8b8dd07a77..7b66e1ff20 100644 --- a/local/recipes/wayland/redbear-compositor/source/tests/integration_test.rs +++ b/local/recipes/wayland/redbear-compositor/source/tests/integration_test.rs @@ -3,11 +3,39 @@ use std::collections::HashMap; use std::io::{Read, Write}; +use std::ops::{Deref, DerefMut}; use std::os::unix::net::UnixStream; use std::process::{Child, Command}; use std::thread; use std::time::Duration; +/// Owns a spawned compositor process and guarantees it is killed and reaped +/// when dropped — including on test panic. Without this, a panicking test +/// leaves the compositor child running; the child inherits the test binary's +/// stdout pipe, which keeps any downstream pipe consumer (and `cargo test` +/// itself) waiting for EOF indefinitely. +struct CompositorGuard(Child); + +impl Deref for CompositorGuard { + type Target = Child; + fn deref(&self) -> &Child { + &self.0 + } +} + +impl DerefMut for CompositorGuard { + fn deref_mut(&mut self) -> &mut Child { + &mut self.0 + } +} + +impl Drop for CompositorGuard { + fn drop(&mut self) { + let _ = self.0.kill(); + let _ = self.0.wait(); + } +} + fn push_wayland_string(buf: &mut Vec, value: &str) { let bytes = value.as_bytes(); buf.extend_from_slice(&((bytes.len() + 1) as u32).to_le_bytes()); @@ -41,11 +69,20 @@ fn read_wayland_string(payload: &[u8], cursor: &mut usize) -> String { } fn collect_globals(client: &mut WaylandClient, registry: u32) -> HashMap { + // A sync roundtrip is the canonical Wayland way to know every + // registry.global event queued so far has been delivered: the + // wl_callback.done reply lands after all already-sent events on the same + // connection. This avoids a stale hardcoded global count that left + // unconsumed registry events polluting later reads. + let callback = client.sync().expect("wl_display.sync failed"); let mut globals = HashMap::new(); - for _ in 0..9 { + loop { let (object_id, opcode, payload) = client.read_message().expect("read global failed"); - assert_eq!(object_id, registry); - assert_eq!(opcode, 0); // wl_registry.global + if object_id == callback && opcode == 0 { + break; + } + assert_eq!(object_id, registry, "expected wl_registry.global or the sync callback"); + assert_eq!(opcode, 0, "expected wl_registry.global"); let name = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); let mut cursor = 4; let iface = read_wayland_string(&payload, &mut cursor); @@ -141,7 +178,7 @@ impl WaylandClient { } } -fn start_compositor(socket_path: &str) -> Child { +fn start_compositor(socket_path: &str) -> CompositorGuard { let compositor_bin = std::env::var("COMPOSITOR_BIN") .unwrap_or_else(|_| "target/debug/redbear-compositor".into()); @@ -162,7 +199,7 @@ fn start_compositor(socket_path: &str) -> Child { // CAP_SYS_RAWIO) and the compositor can bind its Wayland socket anywhere. .env("FRAMEBUFFER_ADDR", "0"); - cmd.spawn().expect("failed to start compositor") + CompositorGuard(cmd.spawn().expect("failed to start compositor")) } #[test] @@ -286,19 +323,10 @@ fn test_compositor_wl_fixes_destroy_registry() { let registry = client.get_registry().expect("get_registry failed"); - let mut fixes_name = 0u32; - for _ in 0..9 { - let (_, opcode, payload) = client.read_message().expect("read failed"); - assert_eq!(opcode, 0); // wl_registry.global - let name = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); - let mut cursor = 4; - let iface = read_wayland_string(&payload, &mut cursor); - if iface == "wl_fixes" { - fixes_name = name; - } - } - - assert_ne!(fixes_name, 0, "wl_fixes global not found"); + let fixes_name = collect_globals(&mut client, registry) + .get("wl_fixes") + .map(|(name, _)| *name) + .expect("wl_fixes global not found"); let fixes = client .bind(registry, fixes_name, "wl_fixes", 2) @@ -392,7 +420,8 @@ fn test_compositor_output_and_seat_metadata() { assert_eq!(object_id, seat_id); match opcode { 0 => { - assert_eq!(payload, 1u32.to_le_bytes()); + // capability bitfield: WL_SEAT_CAPABILITY_POINTER(1) | KEYBOARD(2) + assert_eq!(payload, 3u32.to_le_bytes()); saw_capabilities = true; } 1 => { @@ -426,15 +455,10 @@ fn test_compositor_real_surface_opcodes() { let mut client = WaylandClient::connect(socket).expect("failed to connect"); let registry = client.get_registry().expect("get_registry failed"); - let mut globals = HashMap::new(); - for _ in 0..9 { - let (_, opcode, payload) = client.read_message().expect("read failed"); - assert_eq!(opcode, 0); // wl_registry.global - let name = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); - let mut cursor = 4; - let iface = read_wayland_string(&payload, &mut cursor); - globals.insert(iface, name); - } + let globals: HashMap = collect_globals(&mut client, registry) + .into_iter() + .map(|(k, (name, _))| (k, name)) + .collect(); let compositor_name = *globals .get("wl_compositor") @@ -499,15 +523,10 @@ fn test_compositor_xdg_popup_lifecycle() { let mut client = WaylandClient::connect(socket).expect("failed to connect"); let registry = client.get_registry().expect("get_registry failed"); - let mut globals = HashMap::new(); - for _ in 0..9 { - let (_, opcode, payload) = client.read_message().expect("read failed"); - assert_eq!(opcode, 0); // wl_registry.global - let name = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]); - let mut cursor = 4; - let iface = read_wayland_string(&payload, &mut cursor); - globals.insert(iface, name); - } + let globals: HashMap = collect_globals(&mut client, registry) + .into_iter() + .map(|(k, (name, _))| (k, name)) + .collect(); let compositor_name = *globals .get("wl_compositor")