diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index 43c477ee..e08f6784 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -2,6 +2,8 @@ mod test_buffer; pub mod test_callback; pub mod test_compositor; pub mod test_display; +pub mod test_ext_foreign_toplevel_handle; +pub mod test_ext_foreign_toplevel_list; pub mod test_jay_compositor; pub mod test_keyboard; pub mod test_pointer; diff --git a/src/it/test_ifs/test_display.rs b/src/it/test_ifs/test_display.rs index 531beed9..e71d5c81 100644 --- a/src/it/test_ifs/test_display.rs +++ b/src/it/test_ifs/test_display.rs @@ -1,5 +1,6 @@ use { crate::{ + client::MIN_SERVER_ID, it::{ test_error::TestError, test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, @@ -36,7 +37,9 @@ impl TestDisplay { } Some(obj) => { obj.on_remove(&self.tran); - self.tran.obj_ids.borrow_mut().release(ev.id); + if ev.id < MIN_SERVER_ID { + self.tran.obj_ids.borrow_mut().release(ev.id); + } } } Ok(()) diff --git a/src/it/test_ifs/test_ext_foreign_toplevel_handle.rs b/src/it/test_ifs/test_ext_foreign_toplevel_handle.rs new file mode 100644 index 00000000..a2d0127b --- /dev/null +++ b/src/it/test_ifs/test_ext_foreign_toplevel_handle.rs @@ -0,0 +1,74 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{ext_foreign_toplevel_handle_v1::*, ExtForeignToplevelHandleV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestExtForeignToplevelHandle { + pub id: ExtForeignToplevelHandleV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub closed: Cell, + pub title: Cell>, + pub app_id: Cell>, + pub identifier: Cell>, +} + +impl TestExtForeignToplevelHandle { + fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + fn handle_closed(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Closed::parse_full(parser)?; + self.closed.set(true); + self.destroy()?; + Ok(()) + } + + fn handle_done(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Done::parse_full(parser)?; + Ok(()) + } + + fn handle_title(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Title::parse_full(parser)?; + self.title.set(Some(ev.title.to_string())); + Ok(()) + } + + fn handle_app_id(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = AppId::parse_full(parser)?; + self.app_id.set(Some(ev.app_id.to_string())); + Ok(()) + } + + fn handle_identifier(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Identifier::parse_full(parser)?; + self.identifier.set(Some(ev.identifier.to_string())); + Ok(()) + } +} + +test_object! { + TestExtForeignToplevelHandle, ExtForeignToplevelHandleV1; + + CLOSED => handle_closed, + DONE => handle_done, + TITLE => handle_title, + APP_ID => handle_app_id, + IDENTIFIER => handle_identifier, +} + +impl TestObject for TestExtForeignToplevelHandle {} diff --git a/src/it/test_ifs/test_ext_foreign_toplevel_list.rs b/src/it/test_ifs/test_ext_foreign_toplevel_list.rs new file mode 100644 index 00000000..cff7f979 --- /dev/null +++ b/src/it/test_ifs/test_ext_foreign_toplevel_list.rs @@ -0,0 +1,70 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_ifs::test_ext_foreign_toplevel_handle::TestExtForeignToplevelHandle, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{ext_foreign_toplevel_list_v1::*, ExtForeignToplevelListV1Id}, + }, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, +}; + +pub struct TestExtForeignToplevelList { + pub id: ExtForeignToplevelListV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub toplevels: RefCell>>, +} + +impl TestExtForeignToplevelList { + #[allow(dead_code)] + pub fn stop(&self) -> TestResult { + self.tran.send(Stop { self_id: self.id })?; + Ok(()) + } + + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + fn handle_toplevel(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Toplevel::parse_full(parser)?; + let tl = Rc::new(TestExtForeignToplevelHandle { + id: ev.toplevel, + tran: self.tran.clone(), + destroyed: Cell::new(false), + closed: Cell::new(false), + title: Cell::new(None), + app_id: Cell::new(None), + identifier: Cell::new(None), + }); + self.tran.add_obj(tl.clone())?; + self.toplevels.borrow_mut().push(tl); + Ok(()) + } + + fn handle_finished(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Finished::parse_full(parser)?; + self.destroy()?; + Ok(()) + } +} + +test_object! { + TestExtForeignToplevelList, ExtForeignToplevelListV1; + + TOPLEVEL => handle_toplevel, + FINISHED => handle_finished, +} + +impl TestObject for TestExtForeignToplevelList {} diff --git a/src/it/test_ifs/test_jay_compositor.rs b/src/it/test_ifs/test_jay_compositor.rs index 70e806d1..aea1265f 100644 --- a/src/it/test_ifs/test_jay_compositor.rs +++ b/src/it/test_ifs/test_jay_compositor.rs @@ -2,8 +2,11 @@ use { crate::{ client::ClientId, it::{ - test_error::TestError, test_ifs::test_screenshot::TestJayScreenshot, - test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, + test_error::{TestError, TestResult}, + test_ifs::test_screenshot::TestJayScreenshot, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, }, utils::{buffd::MsgParser, cell_ext::CellExt}, wire::{ @@ -33,6 +36,11 @@ impl TestJayCompositor { } } + pub fn enable_symmetric_delete(&self) -> TestResult { + self.tran.send(EnableSymmetricDelete { self_id: self.id })?; + Ok(()) + } + pub async fn take_screenshot(&self, include_cursor: bool) -> Result { let js = Rc::new(TestJayScreenshot { id: self.tran.id(), diff --git a/src/it/test_ifs/test_registry.rs b/src/it/test_ifs/test_registry.rs index 0e4f384d..990b4dfb 100644 --- a/src/it/test_ifs/test_registry.rs +++ b/src/it/test_ifs/test_registry.rs @@ -5,8 +5,10 @@ use { it::{ test_error::TestError, test_ifs::{ - test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_shm::TestShm, test_single_pixel_buffer_manager::TestSinglePixelBufferManager, + test_compositor::TestCompositor, + test_ext_foreign_toplevel_list::TestExtForeignToplevelList, + test_jay_compositor::TestJayCompositor, test_shm::TestShm, + test_single_pixel_buffer_manager::TestSinglePixelBufferManager, test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter, test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase, }, @@ -17,7 +19,10 @@ use { utils::{buffd::MsgParser, clonecell::CloneCell, copyhashmap::CopyHashMap}, wire::{wl_registry::*, WlRegistryId, WlSeat}, }, - std::{cell::Cell, rc::Rc}, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, }; pub struct TestGlobal { @@ -35,6 +40,7 @@ pub struct TestRegistrySingletons { pub wp_single_pixel_buffer_manager_v1: u32, pub wp_viewporter: u32, pub xdg_activation_v1: u32, + pub ext_foreign_toplevel_list_v1: u32, } pub struct TestRegistry { @@ -50,6 +56,7 @@ pub struct TestRegistry { pub viewporter: CloneCell>>, pub xdg: CloneCell>>, pub activation: CloneCell>>, + pub foreign_toplevel_list: CloneCell>>, pub seats: CopyHashMap>, } @@ -100,6 +107,7 @@ impl TestRegistry { wp_single_pixel_buffer_manager_v1, wp_viewporter, xdg_activation_v1, + ext_foreign_toplevel_list_v1, }; self.singletons.set(Some(singletons.clone())); Ok(singletons) @@ -215,6 +223,23 @@ impl TestRegistry { Ok(jc) } + pub async fn get_foreign_toplevel_list( + &self, + ) -> Result, TestError> { + singleton!(self.foreign_toplevel_list); + let singletons = self.get_singletons().await?; + singleton!(self.foreign_toplevel_list); + let jc = Rc::new(TestExtForeignToplevelList { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + toplevels: RefCell::new(vec![]), + }); + self.bind(&jc, singletons.ext_foreign_toplevel_list_v1, 1)?; + self.foreign_toplevel_list.set(Some(jc.clone())); + Ok(jc) + } + pub fn bind( &self, obj: &Rc, diff --git a/src/it/test_ifs/test_xdg_toplevel.rs b/src/it/test_ifs/test_xdg_toplevel.rs index a921d4e7..ca9ee1e8 100644 --- a/src/it/test_ifs/test_xdg_toplevel.rs +++ b/src/it/test_ifs/test_xdg_toplevel.rs @@ -56,6 +56,14 @@ impl TestXdgToplevelCore { Ok(()) } + pub fn set_title(&self, title: &str) -> Result<(), TestError> { + self.tran.send(SetTitle { + self_id: self.id, + title, + })?; + Ok(()) + } + fn handle_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = Configure::parse_full(parser)?; self.width.set(ev.width); diff --git a/src/it/test_macros.rs b/src/it/test_macros.rs index 69549a0b..beba05ff 100644 --- a/src/it/test_macros.rs +++ b/src/it/test_macros.rs @@ -13,18 +13,20 @@ macro_rules! tassert { macro_rules! tassert_eq { ($left:expr, $right:expr) => {{ - let left = $left; - let right = $right; - if left != right { - bail!( - "Assert `{} = {:?} = {:?} = {}` failed ({}:{})", - stringify!($left), - left, - right, - stringify!($right), - file!(), - line!() - ); + match ($left, $right) { + (left, right) => { + if left != right { + bail!( + "Assert `{} = {:?} = {:?} = {}` failed ({}:{})", + stringify!($left), + left, + right, + stringify!($right), + file!(), + line!() + ); + } + } } }}; } diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index da57bc21..621fce11 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -60,6 +60,7 @@ impl TestTransport { viewporter: Default::default(), xdg: Default::default(), activation: Default::default(), + foreign_toplevel_list: Default::default(), seats: Default::default(), }); self.send(wl_display::GetRegistry { diff --git a/src/it/testrun.rs b/src/it/testrun.rs index e8a40247..03924926 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -74,6 +74,7 @@ impl TestRun { tran.init(); let registry = tran.get_registry(); let jc = registry.get_jay_compositor().await?; + jc.enable_symmetric_delete()?; let client_id = jc.get_client_id().await?; let client = self.state.clients.get(client_id)?; Ok(Rc::new(TestClient { diff --git a/src/it/tests.rs b/src/it/tests.rs index fb56e4c1..40c250e0 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -54,6 +54,7 @@ mod t0020_surface_offset; mod t0021_preferred_buffer_scale; mod t0022_toplevel_suspended; mod t0023_xdg_activation; +mod t0024_foreign_toplevel_list; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -96,5 +97,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0021_preferred_buffer_scale, t0022_toplevel_suspended, t0023_xdg_activation, + t0024_foreign_toplevel_list, } } diff --git a/src/it/tests/t0024_foreign_toplevel_list.rs b/src/it/tests/t0024_foreign_toplevel_list.rs new file mode 100644 index 00000000..ed0aaa28 --- /dev/null +++ b/src/it/tests/t0024_foreign_toplevel_list.rs @@ -0,0 +1,62 @@ +use { + crate::{ + it::{test_error::TestResult, testrun::TestRun}, + wire::WlBufferId, + }, + ahash::AHashSet, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> TestResult { + let _ds = run.create_default_setup().await?; + + let client1 = run.create_client().await?; + let client2 = run.create_client().await?; + + let list = client2.registry.get_foreign_toplevel_list().await?; + + let win1 = client1.create_window().await?; + win1.tl.core.set_title("a")?; + win1.map().await?; + let win2 = client1.create_window().await?; + win2.tl.core.set_title("b")?; + win2.map().await?; + + client2.sync().await; + let tls = list.toplevels.take(); + tassert_eq!(tls.len(), 2); + + tassert_eq!(tls[0].title.take().as_deref(), Some("a")); + tassert_eq!(tls[1].title.take().as_deref(), Some("b")); + + let mut ids = AHashSet::new(); + ids.insert(tls[0].identifier.take().unwrap()); + ids.insert(tls[1].identifier.take().unwrap()); + + win2.tl.core.set_title("c")?; + client1.sync().await; + + client2.sync().await; + tassert_eq!(tls[1].title.take().as_deref(), Some("c")); + + win2.surface.attach(WlBufferId::NONE)?; + win2.surface.commit()?; + client1.sync().await; + + client2.sync().await; + tassert!(tls[1].closed.get()); + + win2.map().await?; + + client1.sync().await; + let tls = list.toplevels.take(); + tassert_eq!(tls.len(), 1); + tassert_eq!(tls[0].title.take().as_deref(), Some("c")); + + ids.insert(tls[0].identifier.take().unwrap()); + tassert_eq!(ids.len(), 3); + + Ok(()) +}