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/wl_surface.rs b/src/ifs/wl_surface.rs index 236c2177..7e777197 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -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 { diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index cab5267e..92e587c8 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -14,7 +14,8 @@ use { 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,6 +40,7 @@ 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, @@ -55,7 +57,6 @@ impl TestBackend { idx: 1, }, events: Default::default(), - on_change: Default::default(), }); let default_mouse = Rc::new(TestBackendMouse { common: TestInputDeviceCommon { @@ -89,9 +90,24 @@ 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, @@ -113,22 +129,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())); @@ -215,8 +218,7 @@ impl Backend for TestBackend { pub struct TestConnector { pub id: ConnectorId, pub kernel_id: ConnectorKernelId, - pub events: SyncQueue, - pub on_change: CloneCell>>, + pub events: OnChange, } impl Connector for TestConnector { @@ -229,11 +231,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) { diff --git a/src/it/tests.rs b/src/it/tests.rs index 5fbab25a..6a59fd4c 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -65,6 +65,7 @@ mod t0031_syncobj; mod t0032_content_type; mod t0032_data_control; mod t0033_float_size_memoization; +mod t0034_workspace_restoration; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -117,5 +118,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0031_syncobj, t0032_data_control, t0033_float_size_memoization, + t0034_workspace_restoration, } } diff --git a/src/it/tests/t0034_workspace_restoration.rs b/src/it/tests/t0034_workspace_restoration.rs new file mode 100644 index 00000000..3f253e65 --- /dev/null +++ b/src/it/tests/t0034_workspace_restoration.rs @@ -0,0 +1,94 @@ +use { + crate::{ + backend::{BackendEvent, ConnectorEvent, ConnectorKernelId, Mode, MonitorInfo}, + it::{test_backend::TestConnector, test_error::TestResult, testrun::TestRun}, + video::drm::ConnectorType, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> TestResult { + let ds = run.create_default_setup().await?; + + let client1 = run.create_client().await?; + let win1 = client1.create_window().await?; + win1.map2().await?; + let surface = &win1.surface.server; + + let Some(dummy_output) = run.state.dummy_output.get() else { + bail!("no dummy output"); + }; + + let new_connector = Rc::new(TestConnector { + id: run.state.connector_ids.next(), + kernel_id: ConnectorKernelId { + ty: ConnectorType::VGA, + idx: 2, + }, + events: Default::default(), + }); + let new_monitor_info = MonitorInfo { + modes: vec![], + manufacturer: "jay".to_string(), + product: "jay second connector".to_string(), + serial_number: "".to_string(), + initial_mode: Mode { + width: 400, + height: 400, + refresh_rate_millihz: 60000, + }, + width_mm: 0, + height_mm: 0, + }; + run.backend + .state + .backend_events + .push(BackendEvent::NewConnector(new_connector.clone())); + + new_connector + .events + .send_event(ConnectorEvent::Connected(new_monitor_info.clone())); + run.state.eng.yield_now().await; + tassert_eq!( + surface.get_output().global.connector.connector.id(), + ds.connector.id + ); + + ds.connector.events.send_event(ConnectorEvent::Disconnected); + run.state.eng.yield_now().await; + tassert_eq!( + surface.get_output().global.connector.connector.id(), + new_connector.id + ); + + new_connector + .events + .send_event(ConnectorEvent::Disconnected); + run.state.eng.yield_now().await; + tassert_eq!( + surface.get_output().global.connector.connector.id(), + dummy_output.global.connector.connector.id() + ); + + new_connector + .events + .send_event(ConnectorEvent::Connected(new_monitor_info.clone())); + run.state.eng.yield_now().await; + tassert_eq!( + surface.get_output().global.connector.connector.id(), + new_connector.id + ); + + ds.connector.events.send_event(ConnectorEvent::Connected( + run.backend.default_monitor_info.clone(), + )); + run.state.eng.yield_now().await; + tassert_eq!( + surface.get_output().global.connector.connector.id(), + ds.connector.id + ); + + Ok(()) +} diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index f8bdcc48..07b7d93c 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -237,6 +237,7 @@ impl ConnectorHandler { } global.destroyed.set(true); self.state.root.outputs.remove(&self.id); + self.state.root.update_extents(); self.data.connected.set(false); self.state.outputs.remove(&self.id); on.lock_surface.take(); diff --git a/src/utils.rs b/src/utils.rs index 6dde581e..44c9a5ce 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -23,6 +23,7 @@ pub mod mmap; pub mod nonblock; pub mod num_cpus; pub mod numcell; +pub mod on_change; pub mod once; pub mod opaque; pub mod opaque_cell; diff --git a/src/utils/on_change.rs b/src/utils/on_change.rs new file mode 100644 index 00000000..feb94408 --- /dev/null +++ b/src/utils/on_change.rs @@ -0,0 +1,39 @@ +use { + crate::utils::{clonecell::CloneCell, syncqueue::SyncQueue}, + std::{ + fmt::{Debug, Formatter}, + rc::Rc, + }, +}; + +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"), + } + } +}