From 04580c4aeb1b4302af76b425dedacf0fc9ca59cb Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Sun, 1 May 2022 21:44:09 +0200 Subject: [PATCH] autocommit 2022-05-01 21:44:09 CEST --- src/cli.rs | 2 +- src/cli/screenshot.rs | 2 +- src/clientmem.rs | 28 +++--- src/compositor.rs | 11 +-- src/ifs/wl_seat.rs | 21 +++- src/ifs/wl_seat/event_handling.rs | 7 ++ src/ifs/wl_seat/kb_owner.rs | 4 + src/ifs/wl_seat/pointer_owner.rs | 4 + src/it.rs | 12 ++- src/it/test_backend.rs | 127 +++++++++++++++++++++++-- src/it/test_client.rs | 23 ++++- src/it/test_ifs.rs | 5 + src/it/test_ifs/test_callback.rs | 2 +- src/it/test_ifs/test_compositor.rs | 32 ++++++- src/it/test_ifs/test_display.rs | 16 ++-- src/it/test_ifs/test_jay_compositor.rs | 30 +++++- src/it/test_ifs/test_registry.rs | 77 +++++++++------ src/it/test_ifs/test_screenshot.rs | 36 +++++++ src/it/test_ifs/test_shm.rs | 12 +-- src/it/test_ifs/test_shm_buffer.rs | 17 +++- src/it/test_ifs/test_shm_pool.rs | 14 +-- src/it/test_ifs/test_surface.rs | 65 +++++++++++++ src/it/test_ifs/test_xdg_base.rs | 68 +++++++++++++ src/it/test_ifs/test_xdg_surface.rs | 78 +++++++++++++++ src/it/test_ifs/test_xdg_toplevel.rs | 72 ++++++++++++++ src/it/test_mem.rs | 3 + src/it/test_object.rs | 2 +- src/it/test_transport.rs | 15 ++- src/it/testrun.rs | 14 +-- src/it/tests.rs | 21 +++- src/it/tests/t0002_window.rs | 56 +++++++++++ src/leaks.rs | 2 +- src/render/renderer/renderer.rs | 2 +- src/state.rs | 16 ++++ src/theme.rs | 31 +++--- src/utils.rs | 1 + src/utils/log_on_drop.rs | 7 ++ src/video/drm.rs | 4 + 38 files changed, 815 insertions(+), 124 deletions(-) create mode 100644 src/it/test_ifs/test_screenshot.rs create mode 100644 src/it/test_ifs/test_surface.rs create mode 100644 src/it/test_ifs/test_xdg_base.rs create mode 100644 src/it/test_ifs/test_xdg_surface.rs create mode 100644 src/it/test_ifs/test_xdg_toplevel.rs create mode 100644 src/it/tests/t0002_window.rs create mode 100644 src/utils/log_on_drop.rs diff --git a/src/cli.rs b/src/cli.rs index b75582ab..277adb3d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -2,7 +2,7 @@ mod generate; mod idle; mod log; mod quit; -mod screenshot; +pub mod screenshot; mod set_log_level; use { diff --git a/src/cli/screenshot.rs b/src/cli/screenshot.rs index e48b71ea..c269455f 100644 --- a/src/cli/screenshot.rs +++ b/src/cli/screenshot.rs @@ -67,7 +67,7 @@ async fn run(screenshot: Rc) { } } -fn buf_to_qoi(buf: &Dmabuf) -> Vec { +pub fn buf_to_qoi(buf: &Dmabuf) -> Vec { let drm = match Drm::reopen(buf.drm_dev.raw(), false) { Ok(drm) => drm, Err(e) => { diff --git a/src/clientmem.rs b/src/clientmem.rs index 009128d2..a751f5ed 100644 --- a/src/clientmem.rs +++ b/src/clientmem.rs @@ -42,19 +42,23 @@ impl ClientMem { } } } - let data = unsafe { - let data = c::mmap64( - ptr::null_mut(), - len, - c::PROT_READ | c::PROT_WRITE, - c::MAP_SHARED, - fd, - 0, - ); - if data == c::MAP_FAILED { - return Err(ClientMemError::MmapFailed(uapi::Errno::default().into())); + let data = if len == 0 { + &mut [][..] + } else { + unsafe { + let data = c::mmap64( + ptr::null_mut(), + len, + c::PROT_READ | c::PROT_WRITE, + c::MAP_SHARED, + fd, + 0, + ); + if data == c::MAP_FAILED { + return Err(ClientMemError::MmapFailed(uapi::Errno::default().into())); + } + std::slice::from_raw_parts_mut(data as *mut Cell, len) } - std::slice::from_raw_parts_mut(data as *mut Cell, len) }; Ok(Self { failed: Cell::new(false), diff --git a/src/compositor.rs b/src/compositor.rs index 6a7116aa..a6a2857e 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -185,10 +185,10 @@ fn start_compositor2( for (key, val) in STATIC_VARS { forker.setenv(key.as_bytes(), val.as_bytes()); } - let _compositor = engine.spawn(start_compositor3(state.clone(), test_future)); + let compositor = engine.spawn(start_compositor3(state.clone(), test_future)); el.run()?; - state.xwayland.handler.borrow_mut().take(); - state.clients.clear(); + drop(compositor); + state.clear(); for (_, seat) in state.globals.seats.lock().deref() { seat.clear(); } @@ -254,10 +254,7 @@ async fn create_backend( ) -> Option> { #[cfg(feature = "it")] if let Some(tf) = test_future { - return Some(Rc::new(TestBackend { - state: state.clone(), - test_future: tf, - })); + return Some(Rc::new(TestBackend::new(state, tf))); } let mut backends = &state.run_args.backends[..]; if backends.is_empty() { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index e5aabd67..36682d0d 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -591,9 +591,28 @@ impl WlSeatGlobal { self.cursor.get() } - pub fn clear(&self) { + pub fn clear(self: &Rc) { mem::take(self.pointer_stack.borrow_mut().deref_mut()); mem::take(self.found_tree.borrow_mut().deref_mut()); + self.keyboard_node.set(self.state.root.clone()); + self.state + .root + .clone() + .node_visit(&mut generic_node_visitor(|node| { + node.node_seat_state().on_seat_remove(self); + })); + self.bindings.borrow_mut().clear(); + self.data_devices.borrow_mut().clear(); + self.primary_selection_devices.borrow_mut().clear(); + self.cursor.set(None); + self.selection.set(None); + self.primary_selection.set(None); + self.pointer_owner.clear(); + self.kb_owner.clear(); + *self.dropped_dnd.borrow_mut() = None; + self.queue_link.set(None); + self.tree_changed_handler.set(None); + self.output.set(self.state.dummy_output.get().unwrap()); } pub fn id(&self) -> SeatId { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 111ec5ca..c7575d83 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -149,6 +149,13 @@ impl NodeSeatState { self.destroy_node2(node, false); } } + + pub fn on_seat_remove(&self, seat: &WlSeatGlobal) { + self.kb_foci.remove(&seat.id); + self.pointer_foci.remove(&seat.id); + self.dnd_targets.remove(&seat.id); + self.pointer_grabs.remove(&seat.id); + } } impl WlSeatGlobal { diff --git a/src/ifs/wl_seat/kb_owner.rs b/src/ifs/wl_seat/kb_owner.rs index 67f34d3f..cda1eeb5 100644 --- a/src/ifs/wl_seat/kb_owner.rs +++ b/src/ifs/wl_seat/kb_owner.rs @@ -32,6 +32,10 @@ impl KbOwnerHolder { pub fn set_kb_node(&self, seat: &Rc, node: Rc) { self.owner.get().set_kb_node(seat, node); } + + pub fn clear(&self) { + self.owner.set(self.default.clone()); + } } struct DefaultKbOwner; diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 4a2de702..6b7de5d4 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -115,6 +115,10 @@ impl PointerOwnerHolder { pub fn remove_dnd_icon(&self) { self.owner.get().remove_dnd_icon() } + + pub fn clear(&self) { + self.owner.set(self.default.clone()); + } } trait PointerOwner { diff --git a/src/it.rs b/src/it.rs index 5dc531fe..887962fa 100644 --- a/src/it.rs +++ b/src/it.rs @@ -1,5 +1,8 @@ use { - crate::it::{test_backend::TestBackend, testrun::TestRun, tests::TestCase}, + crate::{ + it::{test_backend::TestBackend, testrun::TestRun, tests::TestCase}, + utils::errorfmt::ErrorFmt, + }, ahash::AHashMap, isnt::std_1::collections::IsntHashMapExt, log::Level, @@ -86,6 +89,7 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase) { backend, errors: Default::default(), server_addr, + dir: dir.clone(), }); let errors = errors2.clone(); Box::new(async move { @@ -98,7 +102,10 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase) { pending().await }) })); - let errors = errors.take(); + let mut errors = errors.take(); + if let Err(e) = res { + errors.push(format!("The compositor failed: {}", ErrorFmt(e))); + } if errors.len() > 0 { log::error!("The following errors occurred:"); for e in &errors { @@ -107,5 +114,4 @@ fn run_test(it_run: &ItRun, test: &'static dyn TestCase) { it_run.failed.borrow_mut().insert(test.name(), errors); } test_logger::unset_file(); - let _ = res; } diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 2b0fe542..5763cbdb 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -2,26 +2,139 @@ use { crate::{ async_engine::SpawnedFuture, backend::{ - Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, InputDevice, - InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, - TransformMatrix, + Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, + InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, + Mode, MonitorInfo, TransformMatrix, }, compositor::TestFuture, + render::{RenderContext, RenderError}, state::State, - utils::{clonecell::CloneCell, copyhashmap::CopyHashMap, syncqueue::SyncQueue}, + utils::{ + clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue, + }, + video::drm::{ConnectorType, Drm}, }, - std::{any::Any, cell::Cell, error::Error, pin::Pin, rc::Rc}, + bstr::ByteSlice, + std::{any::Any, cell::Cell, io, os::unix::ffi::OsStrExt, pin::Pin, rc::Rc}, + thiserror::Error, + uapi::c, }; +#[derive(Debug, Error)] +pub enum TestBackendError { + #[error("Could not read /dev/dri")] + ReadDri(#[source] io::Error), + #[error("There are no drm nodes in /dev/dri")] + NoDrmNode, + #[error("Could not open drm node {0}")] + OpenDrmNode(String, #[source] OsError), + #[error("Could not create a render context")] + RenderContext(#[source] RenderError), +} + pub struct TestBackend { pub state: Rc, pub test_future: TestFuture, + pub default_connector: Rc, +} + +impl TestBackend { + pub fn new(state: &Rc, future: TestFuture) -> Self { + let connector = Rc::new(TestConnector { + id: state.connector_ids.next(), + kernel_id: ConnectorKernelId { + ty: ConnectorType::VGA, + idx: 1, + }, + events: Default::default(), + on_change: Default::default(), + }); + Self { + state: state.clone(), + test_future: future, + default_connector: connector, + } + } + + pub fn install_default(&self) { + 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, + })); + } + + fn create_render_context(&self) -> Result<(), TestBackendError> { + let dri = match std::fs::read_dir("/dev/dri") { + Ok(d) => d, + Err(e) => return Err(TestBackendError::ReadDri(e)), + }; + let mut files = vec![]; + for f in dri { + let f = match f { + Ok(f) => f, + Err(e) => return Err(TestBackendError::ReadDri(e)), + }; + files.push(f.path()); + } + let node = 'node: { + for f in &files { + if let Some(file) = f.file_name() { + if file.as_bytes().starts_with_str("renderD") { + break 'node f; + } + } + } + for f in &files { + if let Some(file) = f.file_name() { + if file.as_bytes().starts_with_str("card") { + break 'node f; + } + } + } + return Err(TestBackendError::NoDrmNode); + }; + let file = match uapi::open(node.as_path(), c::O_RDWR | c::O_CLOEXEC, 0) { + Ok(f) => f, + Err(e) => { + return Err(TestBackendError::OpenDrmNode( + node.as_os_str().as_bytes().as_bstr().to_string(), + e.into(), + )) + } + }; + let drm = Drm::open_existing(file); + let ctx = match RenderContext::from_drm_device(&drm) { + Ok(ctx) => ctx, + Err(e) => return Err(TestBackendError::RenderContext(e)), + }; + self.state.set_render_ctx(&Rc::new(ctx)); + Ok(()) + } } impl Backend for TestBackend { - fn run(self: Rc) -> SpawnedFuture>> { + fn run(self: Rc) -> SpawnedFuture>> { let future = (self.test_future)(&self.state); + let slf = self.clone(); self.state.eng.spawn(async move { + if let Err(e) = slf.create_render_context() { + return Err(Box::new(e) as Box<_>); + } let future: Pin<_> = future.into(); future.await; Ok(()) @@ -72,7 +185,7 @@ impl Connector for TestConnector { } fn damage(&self) { - todo!() + // nothing } } diff --git a/src/it/test_client.rs b/src/it/test_client.rs index 5e42b1e9..8daa4f14 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -1,10 +1,12 @@ use { crate::{ + cli::screenshot::buf_to_qoi, client::Client, it::{ + test_error::TestError, test_ifs::{ test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_registry::TestRegistry, test_shm::TestShm, + test_registry::TestRegistry, test_shm::TestShm, test_xdg_base::TestXdgWmBase, }, test_transport::TestTransport, testrun::TestRun, @@ -16,19 +18,32 @@ use { pub struct TestClient { pub run: Rc, pub server: Rc, - pub transport: Rc, + pub tran: Rc, pub registry: Rc, pub jc: Rc, pub comp: Rc, pub shm: Rc, + pub xdg: Rc, } impl TestClient { pub fn error(&self, msg: &str) { - self.transport.error(msg) + self.tran.error(msg) } pub async fn sync(self: &Rc) { - self.transport.sync().await + self.tran.sync().await + } + + pub async fn take_screenshot(&self) -> Result, TestError> { + let dmabuf = self.jc.take_screenshot().await?; + let qoi = buf_to_qoi(&dmabuf); + Ok(qoi) + } +} + +impl Drop for TestClient { + fn drop(&mut self) { + self.tran.kill(); } } diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index 089ed624..b48cd469 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -3,6 +3,11 @@ pub mod test_compositor; pub mod test_display; pub mod test_jay_compositor; pub mod test_registry; +pub mod test_screenshot; pub mod test_shm; pub mod test_shm_buffer; pub mod test_shm_pool; +pub mod test_surface; +pub mod test_xdg_base; +pub mod test_xdg_surface; +pub mod test_xdg_toplevel; diff --git a/src/it/test_ifs/test_callback.rs b/src/it/test_ifs/test_callback.rs index 1a1f88c2..0fa1163a 100644 --- a/src/it/test_ifs/test_callback.rs +++ b/src/it/test_ifs/test_callback.rs @@ -12,7 +12,7 @@ use { pub struct TestCallback { pub id: WlCallbackId, - pub transport: Rc, + pub tran: Rc, pub handler: Cell>>, pub done: Cell, } diff --git a/src/it/test_ifs/test_compositor.rs b/src/it/test_ifs/test_compositor.rs index c6a57971..0a560b09 100644 --- a/src/it/test_ifs/test_compositor.rs +++ b/src/it/test_ifs/test_compositor.rs @@ -1,14 +1,38 @@ use { crate::{ - it::{test_object::TestObject, test_transport::TestTransport}, - wire::WlCompositorId, + it::{ + test_error::TestError, test_ifs::test_surface::TestSurface, test_object::TestObject, + test_transport::TestTransport, + }, + wire::{wl_compositor::CreateSurface, WlCompositorId}, }, - std::rc::Rc, + std::{cell::Cell, rc::Rc}, }; pub struct TestCompositor { pub id: WlCompositorId, - pub transport: Rc, + pub tran: Rc, +} + +impl TestCompositor { + pub async fn create_surface(&self) -> Result, TestError> { + let id = self.tran.id(); + self.tran.send(CreateSurface { + self_id: self.id, + id, + }); + self.tran.sync().await; + let client = self.tran.get_client()?; + let server = client.lookup(id)?; + let surface = Rc::new(TestSurface { + id, + tran: self.tran.clone(), + server, + destroyed: Cell::new(false), + }); + self.tran.add_obj(surface.clone())?; + Ok(surface) + } } test_object! { diff --git a/src/it/test_ifs/test_display.rs b/src/it/test_ifs/test_display.rs index 11c4e510..ec9085c0 100644 --- a/src/it/test_ifs/test_display.rs +++ b/src/it/test_ifs/test_display.rs @@ -12,7 +12,7 @@ use { }; pub struct TestDisplay { - pub transport: Rc, + pub tran: Rc, pub id: WlDisplayId, } @@ -20,25 +20,25 @@ impl TestDisplay { fn handle_error(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = Error::parse_full(parser)?; let msg = format!("Compositor sent an error: {}", ev.message); - self.transport.error(&msg); - self.transport.kill(); + self.tran.error(&msg); + self.tran.kill(); Ok(()) } fn handle_delete_id(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = DeleteId::parse_full(parser)?; - match self.transport.objects.remove(&ObjectId::from_raw(ev.id)) { + match self.tran.objects.remove(&ObjectId::from_raw(ev.id)) { None => { let msg = format!( "Compositor sent delete_id for object {} which does not exist", ev.id ); - self.transport.error(&msg); - self.transport.kill(); + self.tran.error(&msg); + self.tran.kill(); } Some(obj) => { - obj.on_remove(&self.transport); - self.transport.obj_ids.borrow_mut().release(ev.id); + obj.on_remove(&self.tran); + self.tran.obj_ids.borrow_mut().release(ev.id); } } Ok(()) diff --git a/src/it/test_ifs/test_jay_compositor.rs b/src/it/test_ifs/test_jay_compositor.rs index 0e28c2ce..0be762e9 100644 --- a/src/it/test_ifs/test_jay_compositor.rs +++ b/src/it/test_ifs/test_jay_compositor.rs @@ -2,12 +2,13 @@ use { crate::{ client::ClientId, it::{ - test_error::TestError, test_object::TestObject, test_transport::TestTransport, - testrun::ParseFull, + test_error::TestError, test_ifs::test_screenshot::TestJayScreenshot, + test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, }, utils::buffd::MsgParser, wire::{ jay_compositor::{self, *}, + jay_screenshot::Dmabuf, JayCompositorId, }, }, @@ -16,25 +17,44 @@ use { pub struct TestJayCompositor { pub id: JayCompositorId, - pub transport: Rc, + pub tran: Rc, pub client_id: Cell>, } impl TestJayCompositor { pub async fn get_client_id(&self) -> Result { if self.client_id.get().is_none() { - self.transport.send(GetClientId { self_id: self.id }); + self.tran.send(GetClientId { self_id: self.id }); } - self.transport.sync().await; + self.tran.sync().await; match self.client_id.get() { Some(c) => Ok(c), _ => bail!("Compositor did not send a client id"), } } + pub async fn take_screenshot(&self) -> Result { + let js = Rc::new(TestJayScreenshot { + id: self.tran.id(), + result: Cell::new(None), + }); + self.tran.send(TakeScreenshot { + self_id: self.id, + id: js.id, + }); + self.tran.add_obj(js.clone())?; + self.tran.sync().await; + match js.result.take() { + Some(Ok(res)) => Ok(res), + Some(Err(res)) => bail!("Compositor could not take a screenshot: {}", res), + None => bail!("Compositor did not send a screenshot"), + } + } + fn handle_client_id(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = jay_compositor::ClientId::parse_full(parser)?; self.client_id.set(Some(ClientId::from_raw(ev.client_id))); + self.tran.client_id.set(ClientId::from_raw(ev.client_id)); Ok(()) } } diff --git a/src/it/test_ifs/test_registry.rs b/src/it/test_ifs/test_registry.rs index d31fb5f9..6139916f 100644 --- a/src/it/test_ifs/test_registry.rs +++ b/src/it/test_ifs/test_registry.rs @@ -4,7 +4,7 @@ use { test_error::TestError, test_ifs::{ test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_shm::TestShm, + test_shm::TestShm, test_xdg_base::TestXdgWmBase, }, test_object::TestObject, test_transport::TestTransport, @@ -26,16 +26,18 @@ pub struct TestRegistrySingletons { pub jay_compositor: u32, pub wl_compositor: u32, pub wl_shm: u32, + pub xdg_wm_base: u32, } pub struct TestRegistry { pub id: WlRegistryId, - pub transport: Rc, + pub tran: Rc, pub globals: CopyHashMap>, pub singletons: CloneCell>>, pub jay_compositor: CloneCell>>, pub compositor: CloneCell>>, pub shm: CloneCell>>, + pub xdg: CloneCell>>, } macro_rules! singleton { @@ -49,22 +51,22 @@ macro_rules! singleton { impl TestRegistry { pub async fn get_singletons(&self) -> Result, TestError> { singleton!(self.singletons); - self.transport.sync().await; + self.tran.sync().await; singleton!(self.singletons); - let mut jay_compositor = 0; - let mut wl_compositor = 0; - let mut wl_shm = 0; - for global in self.globals.lock().values() { - match global.interface.as_str() { - "jay_compositor" => jay_compositor = global.name, - "wl_compositor" => wl_compositor = global.name, - "wl_shm" => wl_shm = global.name, - _ => {} - } - } macro_rules! singleton { - ($($name:ident,)*) => { - TestRegistrySingletons { + ($($name:ident,)*) => {{ + $( + let mut $name = 0; + )* + for global in self.globals.lock().values() { + match global.interface.as_str() { + $( + stringify!($name) => $name = global.name, + )* + _ => {} + } + } + Rc::new(TestRegistrySingletons { $( $name: { if $name == 0 { @@ -73,14 +75,15 @@ impl TestRegistry { $name }, )* - } - } + }) + }} } - let singletons = Rc::new(singleton! { + let singletons = singleton! { jay_compositor, wl_compositor, wl_shm, - }); + xdg_wm_base, + }; self.singletons.set(Some(singletons.clone())); Ok(singletons) } @@ -90,8 +93,8 @@ impl TestRegistry { let singletons = self.get_singletons().await?; singleton!(self.jay_compositor); let jc = Rc::new(TestJayCompositor { - id: self.transport.id(), - transport: self.transport.clone(), + id: self.tran.id(), + tran: self.tran.clone(), client_id: Default::default(), }); self.bind(&jc, singletons.jay_compositor, 1)?; @@ -104,8 +107,8 @@ impl TestRegistry { let singletons = self.get_singletons().await?; singleton!(self.compositor); let jc = Rc::new(TestCompositor { - id: self.transport.id(), - transport: self.transport.clone(), + id: self.tran.id(), + tran: self.tran.clone(), }); self.bind(&jc, singletons.wl_compositor, 4)?; self.compositor.set(Some(jc.clone())); @@ -117,8 +120,8 @@ impl TestRegistry { let singletons = self.get_singletons().await?; singleton!(self.shm); let jc = Rc::new(TestShm { - id: self.transport.id(), - transport: self.transport.clone(), + id: self.tran.id(), + tran: self.tran.clone(), formats: Default::default(), formats_awaited: Cell::new(false), }); @@ -127,20 +130,34 @@ impl TestRegistry { Ok(jc) } + pub async fn get_xdg(&self) -> Result, TestError> { + singleton!(self.xdg); + let singletons = self.get_singletons().await?; + singleton!(self.xdg); + let jc = Rc::new(TestXdgWmBase { + id: self.tran.id(), + tran: self.tran.clone(), + destroyed: Cell::new(false), + }); + self.bind(&jc, singletons.xdg_wm_base, 3)?; + self.xdg.set(Some(jc.clone())); + Ok(jc) + } + pub fn bind( &self, obj: &Rc, name: u32, version: u32, ) -> Result<(), TestError> { - self.transport.send(Bind { + self.tran.send(Bind { self_id: self.id, name, interface: obj.interface().name(), version, id: obj.id().into(), }); - self.transport.add_obj(obj.clone())?; + self.tran.add_obj(obj.clone())?; Ok(()) } @@ -155,7 +172,7 @@ impl TestRegistry { }), ); if prev.is_some() { - self.transport.error(&format!( + self.tran.error(&format!( "Compositor sent global {} multiple times", ev.name )); @@ -166,7 +183,7 @@ impl TestRegistry { fn handle_global_remove(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { let ev = GlobalRemove::parse_full(parser)?; if self.globals.remove(&ev.name).is_none() { - self.transport.error(&format!( + self.tran.error(&format!( "Compositor sent global_remove for {} which does not exist", ev.name )); diff --git a/src/it/test_ifs/test_screenshot.rs b/src/it/test_ifs/test_screenshot.rs new file mode 100644 index 00000000..e3d21d32 --- /dev/null +++ b/src/it/test_ifs/test_screenshot.rs @@ -0,0 +1,36 @@ +use { + crate::{ + it::{test_error::TestError, test_object::TestObject, testrun::ParseFull}, + utils::buffd::MsgParser, + wire::{jay_screenshot::*, JayScreenshotId}, + }, + std::cell::Cell, +}; + +pub struct TestJayScreenshot { + pub id: JayScreenshotId, + pub result: Cell>>, +} + +impl TestJayScreenshot { + fn handle_dmabuf(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Dmabuf::parse_full(parser)?; + self.result.set(Some(Ok(ev))); + Ok(()) + } + + fn handle_error(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Error::parse_full(parser)?; + self.result.set(Some(Err(ev.msg.to_string()))); + Ok(()) + } +} + +test_object! { + TestJayScreenshot, JayScreenshot; + + DMABUF => handle_dmabuf, + ERROR => handle_error, +} + +impl TestObject for TestJayScreenshot {} diff --git a/src/it/test_ifs/test_shm.rs b/src/it/test_ifs/test_shm.rs index 8c4f5f77..95562a7b 100644 --- a/src/it/test_ifs/test_shm.rs +++ b/src/it/test_ifs/test_shm.rs @@ -12,7 +12,7 @@ use { pub struct TestShm { pub id: WlShmId, - pub transport: Rc, + pub tran: Rc, pub formats: CopyHashMap, pub formats_awaited: Cell, } @@ -20,7 +20,7 @@ pub struct TestShm { impl TestShm { pub async fn formats(&self) -> &CopyHashMap { if !self.formats_awaited.replace(true) { - self.transport.sync().await; + self.tran.sync().await; } &self.formats } @@ -28,18 +28,18 @@ impl TestShm { pub fn create_pool(&self, size: usize) -> Result, TestError> { let mem = TestMem::new(size)?; let pool = Rc::new(TestShmPool { - id: self.transport.id(), - transport: self.transport.clone(), + id: self.tran.id(), + tran: self.tran.clone(), mem: CloneCell::new(mem.clone()), destroyed: Cell::new(false), }); - self.transport.send(CreatePool { + self.tran.send(CreatePool { self_id: self.id, id: pool.id, fd: mem.fd.clone(), size: size as _, }); - self.transport.add_obj(pool.clone())?; + self.tran.add_obj(pool.clone())?; Ok(pool) } diff --git a/src/it/test_ifs/test_shm_buffer.rs b/src/it/test_ifs/test_shm_buffer.rs index 54510df1..88d0c92e 100644 --- a/src/it/test_ifs/test_shm_buffer.rs +++ b/src/it/test_ifs/test_shm_buffer.rs @@ -4,7 +4,8 @@ use { test_error::TestError, test_mem::TestMem, test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, }, - utils::buffd::MsgParser, + theme::Color, + utils::{buffd::MsgParser, windows::WindowsExt}, wire::{wl_buffer::*, WlBufferId}, }, std::{ @@ -16,7 +17,7 @@ use { pub struct TestShmBuffer { pub id: WlBufferId, - pub transport: Rc, + pub tran: Rc, pub range: Range, pub mem: Rc, pub released: Cell, @@ -24,11 +25,21 @@ pub struct TestShmBuffer { } impl TestShmBuffer { + pub fn fill(&self, color: Color) { + let [cr, cg, cb, ca] = color.to_rgba_premultiplied(); + for [b, g, r, a] in self.deref().array_chunks_ext::<4>() { + r.set(cr); + g.set(cg); + b.set(cb); + a.set(ca); + } + } + pub fn destroy(&self) { if self.destroyed.replace(true) { return; } - self.transport.send(Destroy { self_id: self.id }); + self.tran.send(Destroy { self_id: self.id }); } fn handle_release(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { diff --git a/src/it/test_ifs/test_shm_pool.rs b/src/it/test_ifs/test_shm_pool.rs index a58dbb81..268b8808 100644 --- a/src/it/test_ifs/test_shm_pool.rs +++ b/src/it/test_ifs/test_shm_pool.rs @@ -13,7 +13,7 @@ use { pub struct TestShmPool { pub id: WlShmPoolId, - pub transport: Rc, + pub tran: Rc, pub mem: CloneCell>, pub destroyed: Cell, } @@ -35,15 +35,15 @@ impl TestShmPool { bail!("Out-of-bounds buffer"); } let buffer = Rc::new(TestShmBuffer { - id: self.transport.id(), - transport: self.transport.clone(), + id: self.tran.id(), + tran: self.tran.clone(), range: start..end, mem, released: Cell::new(true), destroyed: Cell::new(false), }); - self.transport.add_obj(buffer.clone())?; - self.transport.send(CreateBuffer { + self.tran.add_obj(buffer.clone())?; + self.tran.send(CreateBuffer { self_id: self.id, id: buffer.id, offset, @@ -58,7 +58,7 @@ impl TestShmPool { pub fn resize(&self, size: usize) -> Result<(), TestError> { let mem = self.mem.get().grow(size)?; self.mem.set(mem); - self.transport.send(Resize { + self.tran.send(Resize { self_id: self.id, size: size as _, }); @@ -69,7 +69,7 @@ impl TestShmPool { if self.destroyed.replace(true) { return; } - self.transport.send(Destroy { self_id: self.id }); + self.tran.send(Destroy { self_id: self.id }); } } diff --git a/src/it/test_ifs/test_surface.rs b/src/it/test_ifs/test_surface.rs new file mode 100644 index 00000000..bc1ba81d --- /dev/null +++ b/src/it/test_ifs/test_surface.rs @@ -0,0 +1,65 @@ +use { + crate::{ + ifs::wl_surface::WlSurface, + it::{ + test_error::TestError, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{wl_surface::*, WlBufferId, WlSurfaceId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestSurface { + pub id: WlSurfaceId, + pub tran: Rc, + pub server: Rc, + pub destroyed: Cell, +} + +impl TestSurface { + pub fn destroy(&self) { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id }); + } + } + + pub fn attach(&self, buffer_id: WlBufferId) { + self.tran.send(Attach { + self_id: self.id, + buffer: buffer_id, + x: 0, + y: 0, + }); + } + + pub fn commit(&self) { + self.tran.send(Commit { self_id: self.id }); + } + + fn handle_enter(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Enter::parse_full(parser)?; + Ok(()) + } + + fn handle_leave(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Leave::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestSurface { + fn drop(&mut self) { + self.destroy(); + } +} + +test_object! { + TestSurface, WlSurface; + + ENTER => handle_enter, + LEAVE => handle_leave, +} + +impl TestObject for TestSurface {} diff --git a/src/it/test_ifs/test_xdg_base.rs b/src/it/test_ifs/test_xdg_base.rs new file mode 100644 index 00000000..a9468d1b --- /dev/null +++ b/src/it/test_ifs/test_xdg_base.rs @@ -0,0 +1,68 @@ +use { + crate::{ + it::{ + test_error::TestError, test_ifs::test_xdg_surface::TestXdgSurface, + test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{xdg_wm_base::*, WlSurfaceId, XdgWmBaseId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestXdgWmBase { + pub id: XdgWmBaseId, + pub tran: Rc, + pub destroyed: Cell, +} + +impl TestXdgWmBase { + pub fn destroy(&self) { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id }); + } + } + + pub async fn create_xdg_surface( + &self, + surface: WlSurfaceId, + ) -> Result, TestError> { + let id = self.tran.id(); + self.tran.send(GetXdgSurface { + self_id: self.id, + id, + surface, + }); + self.tran.sync().await; + let client = self.tran.get_client()?; + let server = client.lookup(id)?; + let xdg = Rc::new(TestXdgSurface { + id, + tran: self.tran.clone(), + server, + destroyed: Cell::new(false), + last_serial: Cell::new(0), + }); + self.tran.add_obj(xdg.clone())?; + Ok(xdg) + } + + fn handle_ping(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Ping::parse_full(parser)?; + Ok(()) + } +} + +test_object! { + TestXdgWmBase, XdgWmBase; + + PING => handle_ping, +} + +impl TestObject for TestXdgWmBase {} + +impl Drop for TestXdgWmBase { + fn drop(&mut self) { + self.destroy(); + } +} diff --git a/src/it/test_ifs/test_xdg_surface.rs b/src/it/test_ifs/test_xdg_surface.rs new file mode 100644 index 00000000..4222e2d6 --- /dev/null +++ b/src/it/test_ifs/test_xdg_surface.rs @@ -0,0 +1,78 @@ +use { + crate::{ + ifs::wl_surface::xdg_surface::XdgSurface, + it::{ + test_error::TestError, test_ifs::test_xdg_toplevel::TestXdgToplevel, + test_object::TestObject, test_transport::TestTransport, testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{xdg_surface::*, XdgSurfaceId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestXdgSurface { + pub id: XdgSurfaceId, + pub tran: Rc, + pub server: Rc, + pub destroyed: Cell, + pub last_serial: Cell, +} + +impl TestXdgSurface { + pub fn destroy(&self) { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id }); + } + } + + pub async fn create_toplevel(&self) -> Result, TestError> { + let id = self.tran.id(); + self.tran.send(GetToplevel { + self_id: self.id, + id, + }); + self.tran.sync().await; + let client = self.tran.get_client()?; + let server = client.lookup(id)?; + let tl = Rc::new(TestXdgToplevel { + id, + tran: self.tran.clone(), + destroyed: Cell::new(false), + server, + width: Cell::new(0), + height: Cell::new(0), + states: Default::default(), + close_requested: Cell::new(false), + }); + self.tran.add_obj(tl.clone())?; + Ok(tl) + } + + pub fn ack_configure(&self, serial: u32) { + self.tran.send(AckConfigure { + self_id: self.id, + serial, + }); + } + + fn handle_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Configure::parse_full(parser)?; + self.last_serial.set(ev.serial); + Ok(()) + } +} + +impl Drop for TestXdgSurface { + fn drop(&mut self) { + self.destroy(); + } +} + +test_object! { + TestXdgSurface, XdgSurface; + + CONFIGURE => handle_configure, +} + +impl TestObject for TestXdgSurface {} diff --git a/src/it/test_ifs/test_xdg_toplevel.rs b/src/it/test_ifs/test_xdg_toplevel.rs new file mode 100644 index 00000000..0b2fe57a --- /dev/null +++ b/src/it/test_ifs/test_xdg_toplevel.rs @@ -0,0 +1,72 @@ +use { + crate::{ + ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel, + it::{ + test_error::TestError, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::buffd::MsgParser, + wire::{xdg_toplevel::*, XdgToplevelId}, + }, + ahash::AHashSet, + std::{ + cell::{Cell, RefCell}, + rc::Rc, + }, +}; + +pub struct TestXdgToplevel { + pub id: XdgToplevelId, + pub tran: Rc, + pub destroyed: Cell, + pub server: Rc, + + pub width: Cell, + pub height: Cell, + pub states: RefCell>, + + pub close_requested: Cell, +} + +impl TestXdgToplevel { + pub fn destroy(&self) { + if !self.destroyed.replace(true) { + self.tran.send(Destroy { self_id: self.id }); + } + } + + fn handle_configure(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Configure::parse_full(parser)?; + self.width.set(ev.width); + self.height.set(ev.height); + *self.states.borrow_mut() = ev.states.iter().copied().collect(); + Ok(()) + } + + fn handle_close(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = Close::parse_full(parser)?; + self.close_requested.set(true); + Ok(()) + } + + fn handle_configure_bounds(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let _ev = ConfigureBounds::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestXdgToplevel { + fn drop(&mut self) { + self.destroy(); + } +} + +test_object! { + TestXdgToplevel, XdgToplevel; + + CONFIGURE => handle_configure, + CLOSE => handle_close, + CONFIGURE_BOUNDS => handle_configure_bounds, +} + +impl TestObject for TestXdgToplevel {} diff --git a/src/it/test_mem.rs b/src/it/test_mem.rs index e7e608d2..236808b3 100644 --- a/src/it/test_mem.rs +++ b/src/it/test_mem.rs @@ -46,6 +46,9 @@ impl Deref for TestMem { } fn map(fd: c::c_int, size: usize) -> Result<*const [Cell], TestError> { + if size == 0 { + return Ok(&[]); + } unsafe { let res = c::mmap( ptr::null_mut(), diff --git a/src/it/test_object.rs b/src/it/test_object.rs index 95c5038a..a19d32bd 100644 --- a/src/it/test_object.rs +++ b/src/it/test_object.rs @@ -25,7 +25,7 @@ macro_rules! test_object { $( $code => $oname::$f(&self, parser).with_context(|| format!("While handling a `{}` event", stringify!($f))), )* - _ => Err(crate::it::test_error::TestError::new("Unknown event {}")), + _ => Err(crate::it::test_error::TestError::new(format!("Unknown event {}", request))), }; res.with_context(|| format!("In object {} of type `{}`", self.id(), self.interface().name())) } diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index d842096e..6dbc1334 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -1,7 +1,7 @@ use { crate::{ async_engine::{AsyncFd, SpawnedFuture}, - client::{ClientId, EventFormatter}, + client::{Client, ClientId, EventFormatter}, it::{ test_error::{StdError, TestError}, test_ifs::{test_callback::TestCallback, test_registry::TestRegistry}, @@ -47,12 +47,13 @@ impl TestTransport { pub fn get_registry(self: &Rc) -> Rc { let reg = Rc::new(TestRegistry { id: self.id(), - transport: self.clone(), + tran: self.clone(), globals: Default::default(), singletons: Default::default(), jay_compositor: Default::default(), compositor: Default::default(), shm: Default::default(), + xdg: Default::default(), }); self.send(wl_display::GetRegistry { self_id: WL_DISPLAY_ID, @@ -62,6 +63,14 @@ impl TestTransport { reg } + pub fn get_client(&self) -> Result, TestError> { + self.run + .state + .clients + .get(self.client_id.get()) + .map_err(|e| e.into()) + } + pub fn add_obj(&self, obj: Rc) -> Result<(), TestError> { if self.killed.get() { bail!("Transport has already been killed"); @@ -84,7 +93,7 @@ impl TestTransport { pub fn sync(self: &Rc) -> impl Future { let cb = Rc::new(TestCallback { id: self.id(), - transport: self.clone(), + tran: self.clone(), handler: Cell::new(None), done: Cell::new(self.killed.get()), }); diff --git a/src/it/testrun.rs b/src/it/testrun.rs index b1b5bd94..6c013e9e 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -24,6 +24,7 @@ pub struct TestRun { pub backend: Rc, pub errors: Stack, pub server_addr: c::sockaddr_un, + pub dir: String, } impl TestRun { @@ -53,7 +54,7 @@ impl TestRun { let mut obj_ids = Bitfield::default(); obj_ids.take(0); obj_ids.take(1); - let transport = Rc::new(TestTransport { + let tran = Rc::new(TestTransport { run: self.clone(), fd, client_id: Cell::new(ClientId::from_raw(0)), @@ -66,22 +67,23 @@ impl TestRun { obj_ids: RefCell::new(obj_ids), killed: Cell::new(false), }); - transport.add_obj(Rc::new(TestDisplay { - transport: transport.clone(), + tran.add_obj(Rc::new(TestDisplay { + tran: tran.clone(), id: WL_DISPLAY_ID, }))?; - transport.init(); - let registry = transport.get_registry(); + tran.init(); + let registry = tran.get_registry(); let jc = registry.get_jay_compositor().await?; let client_id = jc.get_client_id().await?; let client = self.state.clients.get(client_id)?; Ok(Rc::new(TestClient { run: self.clone(), server: client, - transport, + tran, jc, comp: registry.get_compositor().await?, shm: registry.get_shm().await?, + xdg: registry.get_xdg().await?, registry, })) } diff --git a/src/it/tests.rs b/src/it/tests.rs index 016b0384..578ccca6 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -35,7 +35,26 @@ 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!() + ); + } + }}; +} + mod t0001_shm_formats; +mod t0002_window; pub trait TestCase { fn name(&self) -> &'static str; @@ -43,5 +62,5 @@ pub trait TestCase { } pub fn tests() -> Vec<&'static dyn TestCase> { - vec![&t0001_shm_formats::Test] + vec![&t0001_shm_formats::Test, &t0002_window::Test] } diff --git a/src/it/tests/t0002_window.rs b/src/it/tests/t0002_window.rs new file mode 100644 index 00000000..3df698b2 --- /dev/null +++ b/src/it/tests/t0002_window.rs @@ -0,0 +1,56 @@ +use { + crate::{ + format::ARGB8888, + it::{test_error::TestError, testrun::TestRun}, + theme::Color, + }, + std::rc::Rc, +}; + +testcase!(); + +/// Create and map a single surface +async fn test(run: Rc) -> Result<(), TestError> { + run.backend.install_default(); + + let client = run.create_client().await?; + let surface = client.comp.create_surface().await?; + let xdg_surface = client.xdg.create_xdg_surface(surface.id).await?; + let xdg_toplevel = xdg_surface.create_toplevel().await?; + surface.commit(); + client.sync().await; + + { + let pool = client.shm.create_pool(0)?; + let buffer = pool.create_buffer(0, 0, 0, 0, ARGB8888)?; + xdg_surface.ack_configure(xdg_surface.last_serial.get()); + surface.attach(buffer.id); + surface.commit(); + client.sync().await; + } + + tassert_eq!(xdg_toplevel.width.get(), 800); + tassert!(xdg_toplevel.height.get() >= 500); + + { + let pool = client + .shm + .create_pool((xdg_toplevel.width.get() * xdg_toplevel.height.get() * 4) as _)?; + let buffer = pool.create_buffer( + 0, + xdg_toplevel.width.get(), + xdg_toplevel.height.get(), + xdg_toplevel.width.get() * 4, + ARGB8888, + )?; + buffer.fill(Color::from_rgba_straight(255, 0, 0, 100)); + xdg_surface.ack_configure(xdg_surface.last_serial.get()); + surface.attach(buffer.id); + surface.commit(); + } + + let screenshot = client.take_screenshot().await?; + std::fs::write(format!("{}/screenshot.qoi", run.dir), screenshot)?; + + Ok(()) +} diff --git a/src/leaks.rs b/src/leaks.rs index 07780e2e..dbbb8e6d 100644 --- a/src/leaks.rs +++ b/src/leaks.rs @@ -230,7 +230,7 @@ mod leaks { res } - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { if INITIALIZED { ALLOCATIONS.deref_mut().remove(&ptr); } diff --git a/src/render/renderer/renderer.rs b/src/render/renderer/renderer.rs index 6e6e01ad..6345dfa7 100644 --- a/src/render/renderer/renderer.rs +++ b/src/render/renderer/renderer.rs @@ -196,7 +196,7 @@ impl Renderer<'_> { let pos = placeholder.tl_data().pos.get(); self.fill_boxes( std::slice::from_ref(&pos.at_point(x, y)), - &Color::from_rgba(20, 20, 20, 255), + &Color::from_rgba_straight(20, 20, 20, 255), ); if let Some(tex) = placeholder.texture() { let x = x + (pos.width() - tex.width()) / 2; diff --git a/src/state.rs b/src/state.rs index 8bea1092..8da3c926 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,6 +6,7 @@ use { Backend, BackendEvent, Connector, ConnectorId, ConnectorIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo, }, + backends::dummy::DummyBackend, cli::RunArgs, client::{Client, Clients, SerialRange, NUM_CACHED_SERIAL_RANGES}, config::ConfigProxy, @@ -43,7 +44,9 @@ use { std::{ cell::{Cell, RefCell}, fmt::{Debug, Formatter}, + mem, num::Wrapping, + ops::DerefMut, rc::Rc, sync::Arc, time::Duration, @@ -397,4 +400,17 @@ impl State { } } } + + pub fn clear(&self) { + self.xwayland.handler.borrow_mut().take(); + self.clients.clear(); + self.config.set(None); + self.backend.set(Rc::new(DummyBackend)); + { + let seats = mem::take(self.globals.seats.lock().deref_mut()); + for seat in seats.values() { + seat.clear(); + } + } + } } diff --git a/src/theme.rs b/src/theme.rs index 78302fcc..102b2e9b 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -28,15 +28,24 @@ fn to_f32(c: u8) -> f32 { c as f32 / 255f32 } +fn to_u8(c: f32) -> u8 { + (c * 255f32) as u8 +} + impl Color { - pub fn from_rgba(r: u8, g: u8, b: u8, a: u8) -> Self { + pub fn from_rgba_straight(r: u8, g: u8, b: u8, a: u8) -> Self { + let alpha = to_f32(a); Self { - r: to_f32(r), - g: to_f32(g), - b: to_f32(b), - a: to_f32(a), + r: to_f32(r) * alpha, + g: to_f32(g) * alpha, + b: to_f32(b) * alpha, + a: alpha, } } + + pub fn to_rgba_premultiplied(self) -> [u8; 4] { + [to_u8(self.r), to_u8(self.g), to_u8(self.b), to_u8(self.a)] + } } impl From for Color { @@ -65,12 +74,12 @@ pub struct Theme { impl Default for Theme { fn default() -> Self { Self { - background_color: Cell::new(Color::from_rgba(0x00, 0x10, 0x19, 255)), - last_active_color: Cell::new(Color::from_rgba(0x5f, 0x67, 0x6a, 255)), - title_color: Cell::new(Color::from_rgba(0x22, 0x22, 0x22, 255)), - active_title_color: Cell::new(Color::from_rgba(0x28, 0x55, 0x77, 255)), - underline_color: Cell::new(Color::from_rgba(0x33, 0x33, 0x33, 255)), - border_color: Cell::new(Color::from_rgba(0x3f, 0x47, 0x4a, 255)), + background_color: Cell::new(Color::from_rgba_straight(0x00, 0x10, 0x19, 255)), + last_active_color: Cell::new(Color::from_rgba_straight(0x5f, 0x67, 0x6a, 255)), + title_color: Cell::new(Color::from_rgba_straight(0x22, 0x22, 0x22, 255)), + active_title_color: Cell::new(Color::from_rgba_straight(0x28, 0x55, 0x77, 255)), + underline_color: Cell::new(Color::from_rgba_straight(0x33, 0x33, 0x33, 255)), + border_color: Cell::new(Color::from_rgba_straight(0x3f, 0x47, 0x4a, 255)), title_height: Cell::new(17), border_width: Cell::new(4), font: RefCell::new("monospace 8".to_string()), diff --git a/src/utils.rs b/src/utils.rs index 58ff2993..13345c86 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -11,6 +11,7 @@ pub mod errorfmt; pub mod fdcloser; pub mod hex; pub mod linkedlist; +pub mod log_on_drop; pub mod nonblock; pub mod numcell; pub mod oserror; diff --git a/src/utils/log_on_drop.rs b/src/utils/log_on_drop.rs new file mode 100644 index 00000000..ea3abfac --- /dev/null +++ b/src/utils/log_on_drop.rs @@ -0,0 +1,7 @@ +pub struct LogOnDrop(pub &'static str); + +impl Drop for LogOnDrop { + fn drop(&mut self) { + log::info!("{}", self.0); + } +} diff --git a/src/video/drm.rs b/src/video/drm.rs index 0b2f9bfb..026abb6b 100644 --- a/src/video/drm.rs +++ b/src/video/drm.rs @@ -135,6 +135,10 @@ pub struct Drm { } impl Drm { + pub fn open_existing(fd: OwnedFd) -> Self { + Self { fd: Rc::new(fd) } + } + pub fn reopen(fd: c::c_int, need_primary: bool) -> Result { Ok(Self { fd: reopen(fd, need_primary)?,