diff --git a/src/backends/metal/video.rs b/src/backends/metal/video.rs index c5300ded..650bc030 100644 --- a/src/backends/metal/video.rs +++ b/src/backends/metal/video.rs @@ -22,7 +22,7 @@ use { utils::{ asyncevent::AsyncEvent, bitflags::BitflagsExt, clonecell::CloneCell, copyhashmap::CopyHashMap, debug_fn::debug_fn, errorfmt::ErrorFmt, numcell::NumCell, - opaque_cell::OpaqueCell, oserror::OsError, syncqueue::SyncQueue, + on_change::OnChange, opaque_cell::OpaqueCell, oserror::OsError, transform_ext::TransformExt, }, video::{ @@ -320,38 +320,6 @@ impl Debug for ConnectorFutures { } } -pub struct OnChange { - pub on_change: CloneCell>>, - pub events: SyncQueue, -} - -impl OnChange { - pub fn send_event(&self, event: T) { - self.events.push(event); - if let Some(cb) = self.on_change.get() { - cb(); - } - } -} - -impl Default for OnChange { - fn default() -> Self { - Self { - on_change: Default::default(), - events: Default::default(), - } - } -} - -impl Debug for OnChange { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self.on_change.get() { - None => f.write_str("None"), - Some(_) => f.write_str("Some"), - } - } -} - #[derive(Debug)] pub struct DirectScanoutCache { tex: Weak, 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/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index e4bdabde..567efb58 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -909,6 +909,11 @@ impl WlSeatGlobal { } } + #[cfg_attr(not(feature = "it"), allow(dead_code))] + pub fn get_desired_known_cursor(&self) -> Option { + self.desired_known_cursor.get() + } + pub fn set_known_cursor(&self, cursor: KnownCursor) { self.desired_known_cursor.set(Some(cursor)); let cursors = match self.state.cursors.get() { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index e90f6004..7e777197 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -246,7 +246,7 @@ pub struct WlSurface { tearing: Cell, version: u32, pub has_content_type_manager: Cell, - content_type: Cell>, + pub content_type: Cell>, pub drm_feedback: CopyHashMap>, sync_obj_surface: CloneCell>>, destroyed: Cell, @@ -553,6 +553,11 @@ impl WlSurface { Ok(ext.into_xsurface().unwrap()) } + #[cfg_attr(not(feature = "it"), allow(dead_code))] + pub fn get_output(&self) -> Rc { + self.output.get() + } + pub fn set_output(&self, output: &Rc) { let old = self.output.set(output.clone()); if old.id == output.id { @@ -910,14 +915,6 @@ impl WlSurface { release, }; self.buffer.set(Some(Rc::new(surface_buffer))); - self.buf_x.fetch_add(dx); - self.buf_y.fetch_add(dy); - if (dx, dy) != (0, 0) { - self.need_extents_update.set(true); - for (_, cursor) in &self.cursors { - cursor.dec_hotspot(dx, dy); - } - } } else { self.buf_x.set(0); self.buf_y.set(0); @@ -926,6 +923,14 @@ impl WlSurface { } } } + if self.buffer.is_some() && (dx, dy) != (0, 0) { + self.buf_x.fetch_add(dx); + self.buf_y.fetch_add(dy); + self.need_extents_update.set(true); + for (_, cursor) in &self.cursors { + cursor.dec_hotspot(dx, dy); + } + } let transform_changed = viewport_changed || scale_changed || buffer_transform_changed; if buffer_changed || transform_changed { let mut buffer_points = self.buffer_points.borrow_mut(); @@ -1019,6 +1024,7 @@ impl WlSurface { { if let Some(region) = pending.input_region.take() { self.input_region.set(region); + self.client.state.tree_changed(); } if let Some(region) = pending.opaque_region.take() { self.opaque_region.set(region); diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index f77943ba..f8909b09 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -64,7 +64,7 @@ const STATE_TILED_LEFT: u32 = 5; const STATE_TILED_RIGHT: u32 = 6; const STATE_TILED_TOP: u32 = 7; const STATE_TILED_BOTTOM: u32 = 8; -const STATE_SUSPENDED: u32 = 9; +pub const STATE_SUSPENDED: u32 = 9; #[allow(dead_code)] const CAP_WINDOW_MENU: u32 = 1; diff --git a/src/it.rs b/src/it.rs index f77d8285..d1b93262 100644 --- a/src/it.rs +++ b/src/it.rs @@ -42,6 +42,10 @@ mod tests; const SINGLE_THREAD: bool = false; pub fn run_tests() { + run_tests_(tests::tests()); +} + +fn run_tests_(tests: Vec<&'static dyn TestCase>) { leaks::init(); test_logger::install(); test_logger::set_level(Level::Trace); @@ -54,13 +58,13 @@ pub fn run_tests() { failed: Default::default(), }); if SINGLE_THREAD { - for test in tests::tests() { + for test in tests { with_test_config(|cfg| { run_test(&it_run, test, cfg); }) } } else { - let queue = Arc::new(Mutex::new(VecDeque::from_iter(tests::tests()))); + let queue = Arc::new(Mutex::new(VecDeque::from_iter(tests))); let mut threads = vec![]; let num_cpus = match num_cpus() { Ok(n) => n, @@ -139,7 +143,7 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase, cfg: Rc) { Box::new(async move { let future: Pin<_> = test.run(testrun.clone()).into(); let future = state.eng.spawn2(Phase::Present, future); - let timeout = state.wheel.timeout(5000); + let timeout = state.wheel.timeout(500000); match future::select(future, timeout).await { Either::Left((Ok(..), _)) => {} Either::Left((Err(e), _)) => { diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 17ff055f..b1be7de0 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -8,13 +8,15 @@ use { ScrollAxis, TransformMatrix, }, compositor::TestFuture, + drm_feedback::DrmFeedback, fixed::Fixed, gfx_api::GfxError, - it::test_error::TestResult, + it::{test_error::TestResult, test_utils::test_expected_event::TEEH}, state::State, time::now_usec, utils::{ - clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue, + clonecell::CloneCell, copyhashmap::CopyHashMap, on_change::OnChange, oserror::OsError, + syncqueue::SyncQueue, }, video::drm::{ConnectorType, Drm}, }, @@ -39,10 +41,12 @@ pub enum TestBackendError { pub struct TestBackend { pub state: Rc, pub test_future: TestFuture, + pub default_monitor_info: MonitorInfo, pub default_connector: Rc, pub default_mouse: Rc, pub default_kb: Rc, pub render_context_installed: Cell, + pub idle: TEEH, } impl TestBackend { @@ -55,7 +59,7 @@ impl TestBackend { idx: 1, }, events: Default::default(), - on_change: Default::default(), + feedback: Default::default(), }); let default_mouse = Rc::new(TestBackendMouse { common: TestInputDeviceCommon { @@ -89,13 +93,29 @@ impl TestBackend { name: Rc::new("default-keyboard".to_string()), }, }); + let mode = Mode { + width: 800, + height: 600, + refresh_rate_millihz: 60_000, + }; + let default_monitor_info = MonitorInfo { + modes: vec![mode], + manufacturer: "jay".to_string(), + product: "TestConnector".to_string(), + serial_number: default_connector.id.to_string(), + initial_mode: mode, + width_mm: 80, + height_mm: 60, + }; Self { state: state.clone(), test_future: future, + default_monitor_info, default_connector, default_mouse, default_kb, render_context_installed: Cell::new(false), + idle: Rc::new(Default::default()), } } @@ -113,22 +133,9 @@ impl TestBackend { self.state .backend_events .push(BackendEvent::NewConnector(self.default_connector.clone())); - let mode = Mode { - width: 800, - height: 600, - refresh_rate_millihz: 60_000, - }; self.default_connector .events - .push(ConnectorEvent::Connected(MonitorInfo { - modes: vec![mode], - manufacturer: "jay".to_string(), - product: "TestConnector".to_string(), - serial_number: self.default_connector.id.to_string(), - initial_mode: mode, - width_mm: 80, - height_mm: 60, - })); + .send_event(ConnectorEvent::Connected(self.default_monitor_info.clone())); self.state .backend_events .push(BackendEvent::NewInputDevice(self.default_kb.clone())); @@ -205,7 +212,9 @@ impl Backend for TestBackend { let _ = vtnr; } - fn set_idle(&self, _idle: bool) {} + fn set_idle(&self, idle: bool) { + self.idle.push(idle); + } fn supports_presentation_feedback(&self) -> bool { true @@ -215,8 +224,8 @@ impl Backend for TestBackend { pub struct TestConnector { pub id: ConnectorId, pub kernel_id: ConnectorKernelId, - pub events: SyncQueue, - pub on_change: CloneCell>>, + pub events: OnChange, + pub feedback: CloneCell>>, } impl Connector for TestConnector { @@ -229,11 +238,11 @@ impl Connector for TestConnector { } fn event(&self) -> Option { - self.events.pop() + self.events.events.pop() } fn on_change(&self, cb: Rc) { - self.on_change.set(Some(cb)); + self.events.on_change.set(Some(cb)); } fn damage(&self) { @@ -247,6 +256,10 @@ impl Connector for TestConnector { fn set_mode(&self, _mode: Mode) { // todo } + + fn drm_feedback(&self) -> Option> { + self.feedback.get() + } } pub struct TestMouseClick { @@ -319,13 +332,17 @@ impl TestBackendMouse { } pub fn scroll_px(&self, dy: i32) { + self.scroll_px2(dy, false); + } + + pub fn scroll_px2(&self, dy: i32, inverted: bool) { self.common.event(InputEvent::AxisSource { source: AxisSource::Finger, }); self.common.event(InputEvent::AxisPx { dist: Fixed::from_int(dy), axis: ScrollAxis::Vertical, - inverted: false, + inverted, }); self.common.event(InputEvent::AxisFrame { time_usec: now_usec(), diff --git a/src/it/test_client.rs b/src/it/test_client.rs index e6eb7ced..bd178f9f 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -2,22 +2,23 @@ use { crate::{ cli::screenshot::buf_to_qoi, client::Client, - format::ARGB8888, globals::GlobalBase, it::{ test_error::{TestError, TestResult}, test_ifs::{ - test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_keyboard::TestKeyboard, test_pointer::TestPointer, - test_registry::TestRegistry, test_seat::TestSeat, test_shm::TestShm, - test_subcompositor::TestSubcompositor, test_xdg_base::TestXdgWmBase, + test_compositor::TestCompositor, test_cursor_shape_manager::TestCursorShapeManager, + test_data_device_manager::TestDataDeviceManager, + test_jay_compositor::TestJayCompositor, test_keyboard::TestKeyboard, + test_pointer::TestPointer, test_registry::TestRegistry, test_seat::TestSeat, + test_shm::TestShm, test_single_pixel_buffer_manager::TestSinglePixelBufferManager, + test_subcompositor::TestSubcompositor, test_viewporter::TestViewporter, + test_xdg_activation::TestXdgActivation, test_xdg_base::TestXdgWmBase, }, test_transport::TestTransport, test_utils::test_window::TestWindow, testrun::TestRun, }, theme::Color, - utils::clonecell::CloneCell, }, std::{cell::Cell, rc::Rc}, }; @@ -31,7 +32,12 @@ pub struct TestClient { pub comp: Rc, pub sub: Rc, pub shm: Rc, + pub spbm: Rc, + pub viewporter: Rc, pub xdg: Rc, + pub activation: Rc, + pub data_device_manager: Rc, + pub cursor_shape_manager: Rc, } pub struct DefaultSeat { @@ -65,7 +71,7 @@ impl TestClient { caps: Cell::new(0), name: Default::default(), }); - self.registry.bind(&tseat, seat.name().raw(), 7)?; + self.registry.bind(&tseat, seat.name().raw(), 9)?; self.tran.sync().await; let server = self.tran.get_server_obj(tseat.id)?; tseat.server.set(Some(server)); @@ -79,26 +85,32 @@ impl TestClient { } pub async fn sync(&self) { + self.run.state.eng.yield_now().await; self.run.sync().await; self.tran.sync().await; + self.run.state.eng.yield_now().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 { @@ -114,19 +126,18 @@ impl TestClient { pub async fn create_window(&self) -> Result, TestError> { let surface = self.comp.create_surface().await?; - let shm = self.shm.create_pool(0)?; - let buffer = shm.create_buffer(0, 0, 0, 0, ARGB8888)?; + let viewport = self.viewporter.get_viewport(&surface)?; let xdg = self.xdg.create_xdg_surface(surface.id).await?; let tl = xdg.create_toplevel().await?; surface.commit()?; self.sync().await; Ok(Rc::new(TestWindow { surface, + spbm: self.spbm.clone(), + viewport, xdg, tl, - shm, - buffer: CloneCell::new(buffer), - color: Cell::new(Color::from_rgba_straight(0, 0, 0, 0)), + color: Cell::new(Color::SOLID_BLACK), })) } } diff --git a/src/it/test_config.rs b/src/it/test_config.rs index 1768786a..377387d3 100644 --- a/src/it/test_config.rs +++ b/src/it/test_config.rs @@ -3,6 +3,7 @@ use { backend::InputDeviceId, ifs::wl_seat::SeatId, it::test_error::{TestError, TestResult}, + tree::OutputNode, utils::{copyhashmap::CopyHashMap, stack::Stack}, }, bincode::Options, @@ -15,9 +16,10 @@ use { }, input::{InputDevice, Seat}, keyboard::{Keymap, ModifiedKeySym}, + video::{Connector, Transform}, Axis, Direction, }, - std::{cell::Cell, ops::Deref, ptr, rc::Rc}, + std::{cell::Cell, ops::Deref, ptr, rc::Rc, time::Duration}, }; pub static TEST_CONFIG_ENTRY: ConfigEntry = ConfigEntry { @@ -244,6 +246,17 @@ impl TestConfig { }) } + pub fn set_idle(&self, timeout: Duration) -> TestResult { + self.send(ClientMessage::SetIdle { timeout }) + } + + pub fn set_floating(&self, seat: SeatId, floating: bool) -> TestResult { + self.send(ClientMessage::SetFloating { + seat: Seat(seat.raw() as _), + floating, + }) + } + fn clear(&self) { unsafe { if let Some(srv) = self.srv.take() { @@ -251,6 +264,20 @@ impl TestConfig { } } } + + pub fn set_scale(&self, output: &OutputNode, scale: f64) -> TestResult { + self.send(ClientMessage::ConnectorSetScale { + connector: Connector(output.global.connector.connector.id().raw() as _), + scale, + }) + } + + pub fn set_output_transform(&self, output: &OutputNode, transform: Transform) -> TestResult { + self.send(ClientMessage::ConnectorSetTransform { + connector: Connector(output.global.connector.connector.id().raw() as _), + transform, + }) + } } impl Drop for TestConfig { diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index 58ca7d15..eb2d633b 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -1,6 +1,23 @@ +mod test_buffer; pub mod test_callback; pub mod test_compositor; +pub mod test_content_type; +pub mod test_content_type_manager; +pub mod test_cursor_shape_device; +pub mod test_cursor_shape_manager; +pub mod test_data_control_device; +pub mod test_data_control_manager; +pub mod test_data_control_offer; +pub mod test_data_control_source; +pub mod test_data_device; +pub mod test_data_device_manager; +pub mod test_data_offer; +pub mod test_data_source; pub mod test_display; +pub mod test_dmabuf; +pub mod test_dmabuf_feedback; +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; @@ -11,9 +28,19 @@ pub mod test_seat; pub mod test_shm; pub mod test_shm_buffer; pub mod test_shm_pool; +pub mod test_single_pixel_buffer_manager; pub mod test_subcompositor; pub mod test_subsurface; pub mod test_surface; +pub mod test_syncobj_manager; +pub mod test_syncobj_surface; +pub mod test_syncobj_timeline; +pub mod test_toplevel_drag; +pub mod test_toplevel_drag_manager; +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_buffer.rs b/src/it/test_ifs/test_buffer.rs new file mode 100644 index 00000000..8ed6fa6b --- /dev/null +++ b/src/it/test_ifs/test_buffer.rs @@ -0,0 +1,48 @@ +use { + crate::{ + it::{ + test_error::TestError, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{wl_buffer::*, WlBufferId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestBuffer { + pub id: WlBufferId, + pub tran: Rc, + pub released: Cell, + pub destroyed: Cell, +} + +impl TestBuffer { + pub fn destroy(&self) -> Result<(), TestError> { + if self.destroyed.replace(true) { + return Ok(()); + } + self.tran.send(Destroy { self_id: self.id })?; + Ok(()) + } + + fn handle_release(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Release::parse_full(parser)?; + self.released.set(true); + Ok(()) + } +} + +impl Drop for TestBuffer { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestBuffer, WlBuffer; + + RELEASE => handle_release, +} + +impl TestObject for TestBuffer {} diff --git a/src/it/test_ifs/test_compositor.rs b/src/it/test_ifs/test_compositor.rs index 7bfc989a..f8512a79 100644 --- a/src/it/test_ifs/test_compositor.rs +++ b/src/it/test_ifs/test_compositor.rs @@ -20,6 +20,13 @@ pub struct TestCompositor { } impl TestCompositor { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + } + } + pub async fn create_surface(&self) -> Result, TestError> { let id = self.tran.id(); self.tran.send(CreateSurface { @@ -34,6 +41,8 @@ impl TestCompositor { tran: self.tran.clone(), server, destroyed: Cell::new(false), + preferred_buffer_scale: Rc::new(Default::default()), + preferred_buffer_transform: Rc::new(Default::default()), }); self.tran.add_obj(surface.clone())?; Ok(surface) diff --git a/src/it/test_ifs/test_content_type.rs b/src/it/test_ifs/test_content_type.rs new file mode 100644 index 00000000..541f3ac8 --- /dev/null +++ b/src/it/test_ifs/test_content_type.rs @@ -0,0 +1,42 @@ +use { + crate::{ + it::{test_error::TestError, test_object::TestObject, test_transport::TestTransport}, + wire::{wp_content_type_v1::*, WpContentTypeV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestContentType { + pub id: WpContentTypeV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestContentType { + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn set_content_type(&self, content_type: u32) -> Result<(), TestError> { + self.tran.send(SetContentType { + self_id: self.id, + content_type, + })?; + Ok(()) + } +} + +impl Drop for TestContentType { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestContentType, WpContentTypeV1; +} + +impl TestObject for TestContentType {} diff --git a/src/it/test_ifs/test_content_type_manager.rs b/src/it/test_ifs/test_content_type_manager.rs new file mode 100644 index 00000000..5089c56e --- /dev/null +++ b/src/it/test_ifs/test_content_type_manager.rs @@ -0,0 +1,65 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_ifs::{test_content_type::TestContentType, test_surface::TestSurface}, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{wp_content_type_manager_v1::*, WpContentTypeManagerV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestContentTypeManager { + pub id: WpContentTypeManagerV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestContentTypeManager { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + } + } + + pub fn destroy(&self) -> Result<(), TestError> { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn get_surface_content_type( + &self, + surface: &TestSurface, + ) -> TestResult> { + let obj = Rc::new(TestContentType { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.tran.add_obj(obj.clone())?; + self.tran.send(GetSurfaceContentType { + self_id: self.id, + id: obj.id, + surface: surface.id, + })?; + Ok(obj) + } +} + +impl Drop for TestContentTypeManager { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestContentTypeManager, WpContentTypeManagerV1; +} + +impl TestObject for TestContentTypeManager {} diff --git a/src/it/test_ifs/test_cursor_shape_device.rs b/src/it/test_ifs/test_cursor_shape_device.rs new file mode 100644 index 00000000..48e17501 --- /dev/null +++ b/src/it/test_ifs/test_cursor_shape_device.rs @@ -0,0 +1,38 @@ +use { + crate::{ + it::{test_error::TestResult, test_object::TestObject, test_transport::TestTransport}, + wire::{wp_cursor_shape_device_v1::*, WpCursorShapeDeviceV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestCursorShapeDevice { + pub id: WpCursorShapeDeviceV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestCursorShapeDevice { + #[allow(dead_code)] + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn set_shape(&self, serial: u32, shape: u32) -> TestResult { + self.tran.send(SetShape { + self_id: self.id, + serial, + shape, + })?; + Ok(()) + } +} + +test_object! { + TestCursorShapeDevice, WpCursorShapeDeviceV1; +} + +impl TestObject for TestCursorShapeDevice {} diff --git a/src/it/test_ifs/test_cursor_shape_manager.rs b/src/it/test_ifs/test_cursor_shape_manager.rs new file mode 100644 index 00000000..5f61fae9 --- /dev/null +++ b/src/it/test_ifs/test_cursor_shape_manager.rs @@ -0,0 +1,59 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{ + test_cursor_shape_device::TestCursorShapeDevice, test_pointer::TestPointer, + }, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{wp_cursor_shape_manager_v1::*, WpCursorShapeManagerV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestCursorShapeManager { + pub id: WpCursorShapeManagerV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestCursorShapeManager { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + } + } + + #[allow(dead_code)] + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn get_pointer(&self, pointer: &TestPointer) -> TestResult> { + let obj = Rc::new(TestCursorShapeDevice { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.tran.send(GetPointer { + self_id: self.id, + cursor_shape_device: obj.id, + pointer: pointer.id, + })?; + self.tran.add_obj(obj.clone())?; + Ok(obj) + } +} + +test_object! { + TestCursorShapeManager, WpCursorShapeManagerV1; +} + +impl TestObject for TestCursorShapeManager {} diff --git a/src/it/test_ifs/test_data_control_device.rs b/src/it/test_ifs/test_data_control_device.rs new file mode 100644 index 00000000..f15941b2 --- /dev/null +++ b/src/it/test_ifs/test_data_control_device.rs @@ -0,0 +1,111 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_ifs::{ + test_data_control_offer::TestDataControlOffer, + test_data_control_source::TestDataControlSource, + }, + test_object::TestObject, + test_transport::TestTransport, + test_utils::test_expected_event::TEEH, + testrun::ParseFull, + }, + utils::{buffd::MsgParser, copyhashmap::CopyHashMap}, + wire::{ + zwlr_data_control_device_v1::*, ZwlrDataControlDeviceV1Id, ZwlrDataControlOfferV1Id, + }, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDataControlDevice { + pub id: ZwlrDataControlDeviceV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub pending_offer: CopyHashMap>, + pub selection: TEEH>>, + pub primary_selection: TEEH>>, +} + +impl TestDataControlDevice { + #[allow(dead_code)] + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn set_selection(&self, source: &TestDataControlSource) -> TestResult { + self.tran.send(SetSelection { + self_id: self.id, + source: source.id, + })?; + Ok(()) + } + + #[allow(dead_code)] + pub fn set_primary_selection(&self, source: &TestDataControlSource) -> TestResult { + self.tran.send(SetPrimarySelection { + self_id: self.id, + source: source.id, + })?; + Ok(()) + } + + fn handle_data_offer(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = DataOffer::parse_full(parser)?; + let obj = Rc::new(TestDataControlOffer { + id: ev.id, + tran: self.tran.clone(), + destroyed: Cell::new(false), + offers: Default::default(), + }); + self.tran.add_obj(obj.clone())?; + self.pending_offer.set(obj.id, obj); + Ok(()) + } + + fn take_offer( + &self, + id: ZwlrDataControlOfferV1Id, + ) -> TestResult>> { + if id.is_none() { + Ok(None) + } else { + match self.pending_offer.remove(&id) { + Some(o) => Ok(Some(o)), + _ => bail!("Unknown offer {}", id), + } + } + } + + fn handle_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Selection::parse_full(parser)?; + self.selection.push(self.take_offer(ev.id)?); + Ok(()) + } + + fn handle_primary_selection(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = PrimarySelection::parse_full(parser)?; + self.primary_selection.push(self.take_offer(ev.id)?); + Ok(()) + } + + fn handle_finished(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Finished::parse_full(parser)?; + Ok(()) + } +} + +test_object! { + TestDataControlDevice, ZwlrDataControlDeviceV1; + + DATA_OFFER => handle_data_offer, + SELECTION => handle_selection, + FINISHED => handle_finished, + PRIMARY_SELECTION => handle_primary_selection, +} + +impl TestObject for TestDataControlDevice {} diff --git a/src/it/test_ifs/test_data_control_manager.rs b/src/it/test_ifs/test_data_control_manager.rs new file mode 100644 index 00000000..197b1d19 --- /dev/null +++ b/src/it/test_ifs/test_data_control_manager.rs @@ -0,0 +1,84 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{ + test_data_control_device::TestDataControlDevice, + test_data_control_source::TestDataControlSource, test_seat::TestSeat, + }, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{zwlr_data_control_manager_v1::*, ZwlrDataControlManagerV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDataControlManager { + pub id: ZwlrDataControlManagerV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestDataControlManager { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + } + } + + pub fn create_data_source(&self) -> TestResult> { + let obj = Rc::new(TestDataControlSource { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + cancelled: Cell::new(false), + sends: Default::default(), + }); + self.tran.add_obj(obj.clone())?; + self.tran.send(CreateDataSource { + self_id: self.id, + id: obj.id, + })?; + Ok(obj) + } + + pub fn get_data_device(&self, seat: &TestSeat) -> TestResult> { + let obj = Rc::new(TestDataControlDevice { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + pending_offer: Default::default(), + selection: Default::default(), + primary_selection: Default::default(), + }); + self.tran.add_obj(obj.clone())?; + self.tran.send(GetDataDevice { + self_id: self.id, + id: obj.id, + seat: seat.id, + })?; + Ok(obj) + } + + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } +} + +impl Drop for TestDataControlManager { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataControlManager, ZwlrDataControlManagerV1; +} + +impl TestObject for TestDataControlManager {} diff --git a/src/it/test_ifs/test_data_control_offer.rs b/src/it/test_ifs/test_data_control_offer.rs new file mode 100644 index 00000000..dfb6d4fd --- /dev/null +++ b/src/it/test_ifs/test_data_control_offer.rs @@ -0,0 +1,64 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{zwlr_data_control_offer_v1::*, ZwlrDataControlOfferV1Id}, + }, + ahash::AHashSet, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, + uapi::{c, OwnedFd}, +}; + +pub struct TestDataControlOffer { + pub id: ZwlrDataControlOfferV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub offers: RefCell>, +} + +impl TestDataControlOffer { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn receive(&self, mime_type: &str) -> TestResult> { + let (read, write) = uapi::pipe2(c::O_CLOEXEC)?; + self.tran.send(Receive { + self_id: self.id, + mime_type, + fd: Rc::new(write), + })?; + Ok(Rc::new(read)) + } + + fn handle_offer(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Offer::parse_full(parser)?; + self.offers.borrow_mut().insert(ev.mime_type.to_string()); + Ok(()) + } +} + +impl Drop for TestDataControlOffer { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataControlOffer, ZwlrDataControlOfferV1; + + OFFER => handle_offer, +} + +impl TestObject for TestDataControlOffer {} diff --git a/src/it/test_ifs/test_data_control_source.rs b/src/it/test_ifs/test_data_control_source.rs new file mode 100644 index 00000000..6274d1f5 --- /dev/null +++ b/src/it/test_ifs/test_data_control_source.rs @@ -0,0 +1,67 @@ +use { + crate::{ + it::{ + test_error::{TestError, TestResult}, + test_object::TestObject, + test_transport::TestTransport, + test_utils::test_expected_event::TEEH, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{zwlr_data_control_source_v1::*, ZwlrDataControlSourceV1Id}, + }, + std::{cell::Cell, rc::Rc}, + uapi::OwnedFd, +}; + +pub struct TestDataControlSource { + pub id: ZwlrDataControlSourceV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub cancelled: Cell, + pub sends: TEEH<(String, Rc)>, +} + +impl TestDataControlSource { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + pub fn offer(&self, mime_type: &str) -> TestResult { + self.tran.send(Offer { + self_id: self.id, + mime_type, + })?; + Ok(()) + } + + fn handle_send(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Send::parse_full(parser)?; + self.sends.push((ev.mime_type.to_string(), ev.fd)); + Ok(()) + } + + fn handle_cancelled(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Cancelled::parse_full(parser)?; + self.cancelled.set(true); + Ok(()) + } +} + +impl Drop for TestDataControlSource { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataControlSource, ZwlrDataControlSourceV1; + + SEND => handle_send, + CANCELLED => handle_cancelled, +} + +impl TestObject for TestDataControlSource {} diff --git a/src/it/test_ifs/test_data_device.rs b/src/it/test_ifs/test_data_device.rs new file mode 100644 index 00000000..3bbbb744 --- /dev/null +++ b/src/it/test_ifs/test_data_device.rs @@ -0,0 +1,115 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{ + test_data_offer::TestDataOffer, test_data_source::TestDataSource, + test_surface::TestSurface, + }, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{wl_data_device::*, WlDataDeviceId, WlSurfaceId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDataDevice { + pub id: WlDataDeviceId, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestDataDevice { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Release { self_id: self.id })?; + } + Ok(()) + } + + pub fn start_drag( + &self, + source: &TestDataSource, + origin: &TestSurface, + icon: Option<&TestSurface>, + serial: u32, + ) -> TestResult { + self.tran.send(StartDrag { + self_id: self.id, + source: source.id, + origin: origin.id, + icon: icon.map(|i| i.id).unwrap_or(WlSurfaceId::NONE), + serial, + })?; + Ok(()) + } + + #[allow(dead_code)] + pub fn set_selection(&self, source: &TestDataSource, serial: u32) -> TestResult { + self.tran.send(SetSelection { + self_id: self.id, + source: source.id, + serial, + })?; + Ok(()) + } + + fn handle_data_offer(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = DataOffer::parse_full(parser)?; + let offer = Rc::new(TestDataOffer { + id: ev.id, + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.tran.add_obj(offer.clone())?; + offer.destroy()?; + Ok(()) + } + + fn handle_enter(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Enter::parse_full(parser)?; + Ok(()) + } + + fn handle_leave(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Leave::parse_full(parser)?; + Ok(()) + } + + fn handle_motion(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Motion::parse_full(parser)?; + Ok(()) + } + + fn handle_drop(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Drop::parse_full(parser)?; + Ok(()) + } + + fn handle_selection(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Selection::parse_full(parser)?; + Ok(()) + } +} + +impl std::ops::Drop for TestDataDevice { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataDevice, WlDataDevice; + + DATA_OFFER => handle_data_offer, + ENTER => handle_enter, + LEAVE => handle_leave, + MOTION => handle_motion, + DROP => handle_drop, + SELECTION => handle_selection, +} + +impl TestObject for TestDataDevice {} diff --git a/src/it/test_ifs/test_data_device_manager.rs b/src/it/test_ifs/test_data_device_manager.rs new file mode 100644 index 00000000..59dcc8a1 --- /dev/null +++ b/src/it/test_ifs/test_data_device_manager.rs @@ -0,0 +1,65 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{ + test_data_device::TestDataDevice, test_data_source::TestDataSource, + test_seat::TestSeat, + }, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{wl_data_device_manager::*, WlDataDeviceManagerId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDataDeviceManager { + pub id: WlDataDeviceManagerId, + pub tran: Rc, +} + +impl TestDataDeviceManager { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + } + } + + pub fn create_data_source(&self) -> TestResult> { + let data_source = Rc::new(TestDataSource { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + sends: Rc::new(Default::default()), + }); + self.tran.add_obj(data_source.clone())?; + self.tran.send(CreateDataSource { + self_id: self.id, + id: data_source.id, + })?; + Ok(data_source) + } + + pub fn get_data_device(&self, seat: &TestSeat) -> TestResult> { + let data_device = Rc::new(TestDataDevice { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.tran.add_obj(data_device.clone())?; + self.tran.send(GetDataDevice { + self_id: self.id, + id: data_device.id, + seat: seat.id, + })?; + Ok(data_device) + } +} + +test_object! { + TestDataDeviceManager, WlDataDeviceManager; +} + +impl TestObject for TestDataDeviceManager {} diff --git a/src/it/test_ifs/test_data_offer.rs b/src/it/test_ifs/test_data_offer.rs new file mode 100644 index 00000000..508048bb --- /dev/null +++ b/src/it/test_ifs/test_data_offer.rs @@ -0,0 +1,57 @@ +use { + crate::{ + it::{ + test_error::TestResult, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{wl_data_offer::*, WlDataOfferId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDataOffer { + pub id: WlDataOfferId, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestDataOffer { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + fn handle_offer(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Offer::parse_full(parser)?; + Ok(()) + } + + fn handle_source_actions(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = SourceActions::parse_full(parser)?; + Ok(()) + } + + fn handle_action(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Action::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestDataOffer { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataOffer, WlDataOffer; + + OFFER => handle_offer, + SOURCE_ACTIONS => handle_source_actions, + ACTION => handle_action, +} + +impl TestObject for TestDataOffer {} diff --git a/src/it/test_ifs/test_data_source.rs b/src/it/test_ifs/test_data_source.rs new file mode 100644 index 00000000..4c2ff9f7 --- /dev/null +++ b/src/it/test_ifs/test_data_source.rs @@ -0,0 +1,96 @@ +use { + crate::{ + it::{ + test_error::TestResult, test_object::TestObject, test_transport::TestTransport, + test_utils::test_expected_event::TEEH, testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{wl_data_source::*, WlDataSourceId}, + }, + std::{cell::Cell, rc::Rc}, + uapi::OwnedFd, +}; + +pub struct TestDataSource { + pub id: WlDataSourceId, + pub tran: Rc, + pub destroyed: Cell, + pub sends: TEEH<(String, Rc)>, +} + +impl TestDataSource { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + #[allow(dead_code)] + pub fn offer(&self, mime_type: &str) -> TestResult { + self.tran.send(Offer { + self_id: self.id, + mime_type, + })?; + Ok(()) + } + + #[allow(dead_code)] + pub fn set_actions(&self, actions: u32) -> TestResult { + self.tran.send(SetActions { + self_id: self.id, + dnd_actions: actions, + })?; + Ok(()) + } + + fn handle_target(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Target::parse_full(parser)?; + Ok(()) + } + + fn handle_send(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = Send::parse_full(parser)?; + self.sends.push((ev.mime_type.to_string(), ev.fd)); + Ok(()) + } + + fn handle_cancelled(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Cancelled::parse_full(parser)?; + Ok(()) + } + + fn handle_dnd_drop_performed(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = DndDropPerformed::parse_full(parser)?; + Ok(()) + } + + fn handle_dnd_finished(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = DndFinished::parse_full(parser)?; + Ok(()) + } + + fn handle_action(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Action::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestDataSource { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDataSource, WlDataSource; + + TARGET => handle_target, + SEND => handle_send, + CANCELLED => handle_cancelled, + DND_DROP_PERFORMED => handle_dnd_drop_performed, + DND_FINISHED => handle_dnd_finished, + ACTION => handle_action, +} + +impl TestObject for TestDataSource {} 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_dmabuf.rs b/src/it/test_ifs/test_dmabuf.rs new file mode 100644 index 00000000..21f31103 --- /dev/null +++ b/src/it/test_ifs/test_dmabuf.rs @@ -0,0 +1,72 @@ +use { + crate::{ + it::{ + test_error::TestResult, + test_ifs::{test_dmabuf_feedback::TestDmabufFeedback, test_surface::TestSurface}, + test_object::TestObject, + test_transport::TestTransport, + }, + wire::{zwp_linux_dmabuf_v1::*, ZwpLinuxDmabufV1Id}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestDmabuf { + pub id: ZwpLinuxDmabufV1Id, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestDmabuf { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + } + } + + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + #[allow(dead_code)] + pub fn get_default_feedback(&self) -> TestResult> { + let obj = Rc::new(TestDmabufFeedback::new(&self.tran)); + self.tran.add_obj(obj.clone())?; + self.tran.send(GetDefaultFeedback { + self_id: self.id, + id: obj.id, + })?; + Ok(obj) + } + + pub fn get_surface_feedback( + &self, + surface: &TestSurface, + ) -> TestResult> { + let obj = Rc::new(TestDmabufFeedback::new(&self.tran)); + self.tran.add_obj(obj.clone())?; + self.tran.send(GetSurfaceFeedback { + self_id: self.id, + id: obj.id, + surface: surface.id, + })?; + Ok(obj) + } +} + +impl Drop for TestDmabuf { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDmabuf, ZwpLinuxDmabufV1; +} + +impl TestObject for TestDmabuf {} diff --git a/src/it/test_ifs/test_dmabuf_feedback.rs b/src/it/test_ifs/test_dmabuf_feedback.rs new file mode 100644 index 00000000..1abb4050 --- /dev/null +++ b/src/it/test_ifs/test_dmabuf_feedback.rs @@ -0,0 +1,147 @@ +use { + crate::{ + it::{ + test_error::TestResult, test_object::TestObject, test_transport::TestTransport, + test_utils::test_expected_event::TEEH, testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{zwp_linux_dmabuf_feedback_v1::*, ZwpLinuxDmabufFeedbackV1Id}, + }, + std::{ + cell::{Cell, RefCell}, + mem, + ops::DerefMut, + rc::Rc, + }, + uapi::{c, OwnedFd}, +}; + +pub struct TestDmabufFeedback { + pub id: ZwpLinuxDmabufFeedbackV1Id, + pub tran: Rc, + pub destroyed: Cell, + pub feedback: TEEH, + pub pending_feedback: RefCell, +} + +#[derive(Default)] +pub struct PendingFeedback { + pub format_table: Option>, + pub format_table_size: usize, + pub main_device: c::dev_t, + pub tranches: Vec, + pub pending_tranche: Tranche, +} + +pub struct Feedback { + pub format_table: Rc, + pub format_table_size: usize, + pub main_device: c::dev_t, + pub tranches: Vec, +} + +#[derive(Default)] +pub struct Tranche { + pub target_device: c::dev_t, + pub formats: Vec, + pub flags: u32, +} + +impl TestDmabufFeedback { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + feedback: Rc::new(Default::default()), + pending_feedback: RefCell::new(Default::default()), + } + } + + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id })?; + } + Ok(()) + } + + fn handle_done(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Done::parse_full(parser)?; + let mut pending = mem::take(self.pending_feedback.borrow_mut().deref_mut()); + self.feedback.push(Feedback { + format_table: match pending.format_table.take() { + None => bail!("compositor did not send format table"), + Some(ft) => ft, + }, + format_table_size: pending.format_table_size, + main_device: pending.main_device, + tranches: pending.tranches, + }); + Ok(()) + } + + fn handle_format_table(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = FormatTable::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending.format_table = Some(ev.fd); + pending.format_table_size = ev.size as _; + Ok(()) + } + + fn handle_main_device(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = MainDevice::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending.main_device = ev.device; + Ok(()) + } + + fn handle_tranche_done(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = TrancheDone::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending + .tranches + .push(mem::take(&mut pending.pending_tranche)); + Ok(()) + } + + fn handle_tranche_target_device(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = TrancheTargetDevice::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending.pending_tranche.target_device = ev.device; + Ok(()) + } + + fn handle_tranche_formats(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = TrancheFormats::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending.pending_tranche.formats = ev.indices.iter().copied().map(|v| v as usize).collect(); + Ok(()) + } + + fn handle_tranche_flags(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = TrancheFlags::parse_full(parser)?; + let pending = &mut *self.pending_feedback.borrow_mut(); + pending.pending_tranche.flags = ev.flags; + Ok(()) + } +} + +impl Drop for TestDmabufFeedback { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestDmabufFeedback, ZwpLinuxDmabufFeedbackV1; + + DONE => handle_done, + FORMAT_TABLE => handle_format_table, + MAIN_DEVICE => handle_main_device, + TRANCHE_DONE => handle_tranche_done, + TRANCHE_TARGET_DEVICE => handle_tranche_target_device, + TRANCHE_FORMATS => handle_tranche_formats, + TRANCHE_FLAGS => handle_tranche_flags, +} + +impl TestObject for TestDmabufFeedback {} 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..ca0dc137 --- /dev/null +++ b/src/it/test_ifs/test_ext_foreign_toplevel_list.rs @@ -0,0 +1,79 @@ +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 { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + destroyed: Cell::new(false), + toplevels: RefCell::new(vec![]), + } + } + + #[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 c17d65f7..bd0570db 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::{ @@ -22,6 +25,14 @@ pub struct TestJayCompositor { } impl TestJayCompositor { + pub fn new(tran: &Rc) -> Self { + Self { + id: tran.id(), + tran: tran.clone(), + client_id: Cell::new(None), + } + } + pub async fn get_client_id(&self) -> Result { if self.client_id.is_none() { self.tran.send(GetClientId { self_id: self.id })?; @@ -33,14 +44,20 @@ impl TestJayCompositor { } } - pub async fn take_screenshot(&self) -> Result { + 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(), 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_pointer.rs b/src/it/test_ifs/test_pointer.rs index 05249f84..e602316e 100644 --- a/src/it/test_ifs/test_pointer.rs +++ b/src/it/test_ifs/test_pointer.rs @@ -2,11 +2,12 @@ use { crate::{ ifs::wl_seat::wl_pointer::WlPointer, it::{ - test_error::TestResult, test_object::TestObject, test_transport::TestTransport, - test_utils::test_expected_event::TEEH, testrun::ParseFull, + test_error::TestResult, test_ifs::test_surface::TestSurface, test_object::TestObject, + test_transport::TestTransport, test_utils::test_expected_event::TEEH, + testrun::ParseFull, }, utils::{buffd::MsgParser, clonecell::CloneCell}, - wire::{wl_pointer::*, WlPointerId}, + wire::{wl_pointer::*, WlPointerId, WlSurfaceId}, }, std::{cell::Cell, rc::Rc}, }; @@ -19,6 +20,8 @@ pub struct TestPointer { pub leave: TEEH, pub enter: TEEH, pub motion: TEEH, + pub button: TEEH