diff --git a/src/ifs/jay_compositor.rs b/src/ifs/jay_compositor.rs index 9acf0035..66c7b778 100644 --- a/src/ifs/jay_compositor.rs +++ b/src/ifs/jay_compositor.rs @@ -18,7 +18,7 @@ use { clonecell::CloneCell, errorfmt::ErrorFmt, }, - wire::{jay_compositor::*, JayCompositorId}, + wire::{jay_compositor::*, JayCompositorId, JayScreenshotId}, }, bstr::ByteSlice, log::Level, @@ -125,14 +125,27 @@ impl JayCompositor { fn take_screenshot(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> { let req: TakeScreenshot = self.client.parse(self, parser)?; + self.take_screenshot_impl(req.id, false) + } + + fn take_screenshot2(&self, parser: MsgParser<'_, '_>) -> Result<(), JayCompositorError> { + let req: TakeScreenshot2 = self.client.parse(self, parser)?; + self.take_screenshot_impl(req.id, req.include_cursor != 0) + } + + fn take_screenshot_impl( + &self, + id: JayScreenshotId, + include_cursor: bool, + ) -> Result<(), JayCompositorError> { let ss = Rc::new(JayScreenshot { - id: req.id, + id, client: self.client.clone(), tracker: Default::default(), }); track!(self.client, ss); self.client.add_client_obj(&ss)?; - match take_screenshot(&self.client.state) { + match take_screenshot(&self.client.state, include_cursor) { Ok(s) => { let dmabuf = s.bo.dmabuf(); let plane = &dmabuf.planes[0]; @@ -347,6 +360,7 @@ object_base! { CREATE_SCREENCAST => create_screencast, GET_RANDR => get_randr, GET_INPUT => get_input, + TAKE_SCREENSHOT2 => take_screenshot2, } impl Object for JayCompositor {} diff --git a/src/it/test_client.rs b/src/it/test_client.rs index 323f6db5..41bcfbb3 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -11,7 +11,7 @@ use { test_registry::TestRegistry, test_seat::TestSeat, test_shm::TestShm, test_single_pixel_buffer_manager::TestSinglePixelBufferManager, test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter, - test_xdg_base::TestXdgWmBase, + test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase, }, test_transport::TestTransport, test_utils::test_window::TestWindow, @@ -34,6 +34,7 @@ pub struct TestClient { pub spbm: Rc, pub viewporter: Rc, pub xdg: Rc, + pub activation: Rc, } pub struct DefaultSeat { @@ -86,22 +87,26 @@ impl TestClient { self.tran.sync().await; } - pub async fn take_screenshot(&self) -> Result, TestError> { - let dmabuf = self.jc.take_screenshot().await?; + pub async fn take_screenshot(&self, include_cursor: bool) -> Result, TestError> { + let dmabuf = self.jc.take_screenshot(include_cursor).await?; let qoi = buf_to_qoi(&self.server.state.dma_buf_ids, &dmabuf); Ok(qoi) } #[allow(dead_code)] - pub async fn save_screenshot(&self, name: &str) -> Result<(), TestError> { - let qoi = self.take_screenshot().await?; + pub async fn save_screenshot(&self, name: &str, include_cursor: bool) -> Result<(), TestError> { + let qoi = self.take_screenshot(include_cursor).await?; let path = format!("{}/screenshot_{}.qoi", self.run.out_dir, name); std::fs::write(path, qoi)?; Ok(()) } - pub async fn compare_screenshot(&self, name: &str) -> Result<(), TestError> { - let actual = self.take_screenshot().await?; + pub async fn compare_screenshot( + &self, + name: &str, + include_cursor: bool, + ) -> Result<(), TestError> { + let actual = self.take_screenshot(include_cursor).await?; let expected_path = format!("{}/screenshot_{}.qoi", self.run.in_dir, name); let expected = std::fs::read(expected_path)?; if actual != expected { diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index b8440987..43c477ee 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -18,6 +18,8 @@ pub mod test_subsurface; pub mod test_surface; pub mod test_viewport; pub mod test_viewporter; +pub mod test_xdg_activation; +pub mod test_xdg_activation_token; pub mod test_xdg_base; pub mod test_xdg_surface; pub mod test_xdg_toplevel; diff --git a/src/it/test_ifs/test_jay_compositor.rs b/src/it/test_ifs/test_jay_compositor.rs index c17d65f7..70e806d1 100644 --- a/src/it/test_ifs/test_jay_compositor.rs +++ b/src/it/test_ifs/test_jay_compositor.rs @@ -33,14 +33,15 @@ impl TestJayCompositor { } } - pub async fn take_screenshot(&self) -> Result { + pub async fn take_screenshot(&self, include_cursor: bool) -> Result { let js = Rc::new(TestJayScreenshot { id: self.tran.id(), result: Cell::new(None), }); - self.tran.send(TakeScreenshot { + self.tran.send(TakeScreenshot2 { self_id: self.id, id: js.id, + include_cursor: include_cursor as _, })?; self.tran.add_obj(js.clone())?; self.tran.sync().await; diff --git a/src/it/test_ifs/test_registry.rs b/src/it/test_ifs/test_registry.rs index 75b0d813..0e4f384d 100644 --- a/src/it/test_ifs/test_registry.rs +++ b/src/it/test_ifs/test_registry.rs @@ -8,7 +8,7 @@ use { test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, test_shm::TestShm, test_single_pixel_buffer_manager::TestSinglePixelBufferManager, test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter, - test_xdg_base::TestXdgWmBase, + test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase, }, test_object::TestObject, test_transport::TestTransport, @@ -34,6 +34,7 @@ pub struct TestRegistrySingletons { pub xdg_wm_base: u32, pub wp_single_pixel_buffer_manager_v1: u32, pub wp_viewporter: u32, + pub xdg_activation_v1: u32, } pub struct TestRegistry { @@ -48,6 +49,7 @@ pub struct TestRegistry { pub spbm: CloneCell>>, pub viewporter: CloneCell>>, pub xdg: CloneCell>>, + pub activation: CloneCell>>, pub seats: CopyHashMap>, } @@ -97,6 +99,7 @@ impl TestRegistry { xdg_wm_base, wp_single_pixel_buffer_manager_v1, wp_viewporter, + xdg_activation_v1, }; self.singletons.set(Some(singletons.clone())); Ok(singletons) @@ -184,6 +187,20 @@ impl TestRegistry { Ok(jc) } + pub async fn get_activation(&self) -> Result, TestError> { + singleton!(self.activation); + let singletons = self.get_singletons().await?; + singleton!(self.activation); + let jc = Rc::new(TestXdgActivation { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.bind(&jc, singletons.xdg_activation_v1, 1)?; + self.activation.set(Some(jc.clone())); + Ok(jc) + } + pub async fn get_xdg(&self) -> Result, TestError> { singleton!(self.xdg); let singletons = self.get_singletons().await?; diff --git a/src/it/test_ifs/test_xdg_activation.rs b/src/it/test_ifs/test_xdg_activation.rs new file mode 100644 index 00000000..6cc6aa86 --- /dev/null +++ b/src/it/test_ifs/test_xdg_activation.rs @@ -0,0 +1,66 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_ifs::{ + test_surface::TestSurface, test_xdg_activation_token::TestXdgActivationToken, + }, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{xdg_activation_v1::*, XdgActivationV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestXdgActivation { + pub id: XdgActivationV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestXdgActivation { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub async fn get_token(&self) -> Result { + let token = Rc::new(TestXdgActivationToken { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + token: Cell::new(None), + }); + self.tran.add_obj(token.clone())?; + self.tran.send(GetActivationToken { + self_id: self.id, + id: token.id, + })?; + let res = token.commit().await?; + token.destroy()?; + Ok(res) + } + + pub fn activate(&self, tl: &TestSurface, token: &str) -> TestResult { + self.tran.send(Activate { + self_id: self.id, + token, + surface: tl.id, + }) + } +} + +test_object! { + TestXdgActivation, XdgActivationV1; +} + +impl TestObject for TestXdgActivation {} + +impl Drop for TestXdgActivation { + fn drop(&mut self) { + let _ = self.destroy(); + } +} diff --git a/src/it/test_ifs/test_xdg_activation_token.rs b/src/it/test_ifs/test_xdg_activation_token.rs new file mode 100644 index 00000000..fba8312b --- /dev/null +++ b/src/it/test_ifs/test_xdg_activation_token.rs @@ -0,0 +1,56 @@ +use { + crate::{ + it::{ + test_error::TestError, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{xdg_activation_token_v1::*, XdgActivationTokenV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestXdgActivationToken { + pub id: XdgActivationTokenV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub token: Cell>, +} + +impl TestXdgActivationToken { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub async fn commit(&self) -> Result { + self.tran.send(Commit { self_id: self.id })?; + self.tran.sync().await; + match self.token.take() { + Some(t) => Ok(t), + _ => bail!("Server did not send a token"), + } + } + + fn handle_done(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Done::parse_full(parser)?; + self.token.set(Some(ev.token.to_string())); + Ok(()) + } +} + +test_object! { + TestXdgActivationToken, XdgActivationTokenV1; + + DONE => handle_done, +} + +impl TestObject for TestXdgActivationToken {} + +impl Drop for TestXdgActivationToken { + fn drop(&mut self) { + let _ = self.destroy(); + } +} diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index d7708da2..da57bc21 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -59,6 +59,7 @@ impl TestTransport { spbm: Default::default(), viewporter: Default::default(), xdg: Default::default(), + activation: Default::default(), seats: Default::default(), }); self.send(wl_display::GetRegistry { @@ -161,7 +162,12 @@ impl TestTransport { _ => bail!("Object with id {} has already been deleted", msg.id()), }; if obj.interface().name() != msg.interface().name() { - bail!("Object with id {} has an incompatible interface", msg.id()); + bail!( + "Object with id {} has an incompatible interface: {} != {}", + msg.id(), + obj.interface().name(), + msg.interface().name() + ); } let mut fds = vec![]; let mut swapchain = self.swapchain.borrow_mut(); diff --git a/src/it/testrun.rs b/src/it/testrun.rs index 157258cc..e8a40247 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -87,6 +87,7 @@ impl TestRun { spbm: registry.get_spbm().await?, viewporter: registry.get_viewporter().await?, xdg: registry.get_xdg().await?, + activation: registry.get_activation().await?, registry, })) } diff --git a/src/it/tests.rs b/src/it/tests.rs index 375fe9f5..fb56e4c1 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -53,6 +53,7 @@ mod t0019_natural_scrolling; mod t0020_surface_offset; mod t0021_preferred_buffer_scale; mod t0022_toplevel_suspended; +mod t0023_xdg_activation; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -94,5 +95,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0020_surface_offset, t0021_preferred_buffer_scale, t0022_toplevel_suspended, + t0023_xdg_activation, } } diff --git a/src/it/tests/t0007_subsurface.rs b/src/it/tests/t0007_subsurface.rs index 34fec5a8..087b564d 100644 --- a/src/it/tests/t0007_subsurface.rs +++ b/src/it/tests/t0007_subsurface.rs @@ -43,19 +43,17 @@ async fn test(run: Rc) -> Result<(), TestError> { parent.map().await?; - seat.set_app_cursor(None); - - client.compare_screenshot("1").await?; + client.compare_screenshot("1", false).await?; sub.place_below(parent.surface.id)?; child.commit()?; parent.map().await?; - client.compare_screenshot("2").await?; + client.compare_screenshot("2", false).await?; sub.place_above(parent.surface.id)?; child.commit()?; parent.map().await?; - client.compare_screenshot("1").await?; + client.compare_screenshot("1", false).await?; Ok(()) } diff --git a/src/it/tests/t0020_surface_offset.rs b/src/it/tests/t0020_surface_offset.rs index 351cdfd6..f33940e9 100644 --- a/src/it/tests/t0020_surface_offset.rs +++ b/src/it/tests/t0020_surface_offset.rs @@ -44,12 +44,12 @@ async fn test(run: Rc) -> Result<(), TestError> { let enter = enter.next()?; seat.pointer.set_cursor(enter.serial, &surface, 0, 0)?; - client.compare_screenshot("1").await?; + client.compare_screenshot("1", true).await?; surface.offset(-100, -100)?; surface.commit()?; - client.compare_screenshot("2").await?; + client.compare_screenshot("2", true).await?; Ok(()) } diff --git a/src/it/tests/t0023_xdg_activation.rs b/src/it/tests/t0023_xdg_activation.rs new file mode 100644 index 00000000..035ebb03 --- /dev/null +++ b/src/it/tests/t0023_xdg_activation.rs @@ -0,0 +1,40 @@ +use { + crate::it::{ + test_error::TestResult, + test_utils::{ + test_ouput_node_ext::TestOutputNodeExt, test_toplevel_node_ext::TestToplevelNodeExt, + }, + testrun::TestRun, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> TestResult { + let ds = run.create_default_setup().await?; + + let client = run.create_client().await?; + + let win1 = client.create_window().await?; + win1.set_color(255, 0, 0, 255); + win1.map2().await?; + + let win2 = client.create_window().await?; + win2.set_color(0, 255, 0, 255); + win2.map2().await?; + + let (x, y) = ds.output.first_toplevel()?.center(); + ds.move_to(x, y); + + client.sync().await; + run.cfg.set_mono(ds.seat.id(), true)?; + + let token = client.activation.get_token().await?; + client.activation.activate(&win2.surface, &token)?; + client.sync().await; + + client.compare_screenshot("1", false).await?; + + Ok(()) +} diff --git a/src/it/tests/t0023_xdg_activation/screenshot_1.qoi b/src/it/tests/t0023_xdg_activation/screenshot_1.qoi new file mode 100644 index 00000000..1fa8d204 Binary files /dev/null and b/src/it/tests/t0023_xdg_activation/screenshot_1.qoi differ diff --git a/src/screenshoter.rs b/src/screenshoter.rs index acfbaab4..28e80129 100644 --- a/src/screenshoter.rs +++ b/src/screenshoter.rs @@ -39,7 +39,10 @@ pub struct Screenshot { pub bo: GbmBo, } -pub fn take_screenshot(state: &State) -> Result { +pub fn take_screenshot( + state: &State, + include_cursor: bool, +) -> Result { let ctx = match state.render_ctx.get() { Some(ctx) => ctx, _ => return Err(ScreenshooterError::NoRenderContext), @@ -72,7 +75,7 @@ pub fn take_screenshot(state: &State) -> Result fb.render_node( state.root.deref(), state, - Some(state.root.extents.get()), + include_cursor.then_some(state.root.extents.get()), None, Scale::from_int(1), true, diff --git a/wire/jay_compositor.txt b/wire/jay_compositor.txt index 10fb4d60..03fc2ebf 100644 --- a/wire/jay_compositor.txt +++ b/wire/jay_compositor.txt @@ -73,6 +73,11 @@ msg get_input = 17 { id: id(jay_input), } +msg take_screenshot2 = 18 { + id: id(jay_screenshot), + include_cursor: u32, +} + # events msg client_id = 0 {