use std::{panic::UnwindSafe, time::Duration}; use pinnacle::{backend::dummy::setup_dummy, state::State}; use smithay::reexports::calloop::{ self, channel::{Event, Sender}, }; #[allow(clippy::type_complexity)] pub fn with_state( sender: &Sender<Box<dyn FnOnce(&mut State) + Send>>, with_state: impl FnOnce(&mut State) + Send + 'static, ) { sender.send(Box::new(with_state)).unwrap(); } pub fn sleep_secs(secs: u64) { std::thread::sleep(Duration::from_secs(secs)); } pub fn test_api( test: impl FnOnce(Sender<Box<dyn FnOnce(&mut State) + Send>>) + Send + UnwindSafe + 'static, ) -> anyhow::Result<()> { let (mut state, mut event_loop) = setup_dummy(true, None)?; let (sender, recv) = calloop::channel::channel::<Box<dyn FnOnce(&mut State) + Send>>(); event_loop .handle() .insert_source(recv, |event, _, state| match event { Event::Msg(f) => f(state), Event::Closed => (), }) .map_err(|_| anyhow::anyhow!("failed to insert source"))?; let tempdir = tempfile::tempdir()?; state.start_grpc_server(tempdir.path())?; let loop_signal = event_loop.get_signal(); let join_handle = std::thread::spawn(move || { let res = std::panic::catch_unwind(|| { test(sender); }); loop_signal.stop(); if let Err(err) = res { std::panic::resume_unwind(err); } }); event_loop.run(None, &mut state, |state| { state.fixup_z_layering(); state.space.refresh(); state.popup_manager.cleanup(); state .display_handle .flush_clients() .expect("failed to flush client buffers"); // TODO: couple these or something, this is really error-prone assert_eq!( state.windows.len(), state.z_index_stack.len(), "Length of `windows` and `z_index_stack` are different. \ If you see this, report it to the developer." ); })?; if let Err(err) = join_handle.join() { panic!("{err:?}"); } Ok(()) }