From c827a93dbbaa11ec2affc3a0796dfe523e74a157 Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Wed, 4 May 2022 14:36:18 +0200 Subject: [PATCH] it: test window gains kb focus when mapped --- src/it/test_backend.rs | 185 +++++++++++++++++++---- src/it/test_client.rs | 44 +++++- src/it/test_config.rs | 14 +- src/it/test_error.rs | 2 + src/it/test_ifs.rs | 3 + src/it/test_ifs/test_keyboard.rs | 89 +++++++++++ src/it/test_ifs/test_pointer.rs | 95 ++++++++++++ src/it/test_ifs/test_seat.rs | 99 ++++++++++++ src/it/test_ifs/test_subcompositor.rs | 2 +- src/it/test_transport.rs | 2 +- src/it/test_utils.rs | 1 + src/it/test_utils/test_expected_event.rs | 71 +++++++++ src/it/testrun.rs | 33 +++- src/it/tests.rs | 2 + src/it/tests/t0007_subsurface.rs | 1 + src/it/tests/t0008_map_focus.rs | 27 ++++ src/time.rs | 14 ++ src/utils.rs | 1 + src/utils/once.rs | 26 ++++ 19 files changed, 672 insertions(+), 39 deletions(-) create mode 100644 src/it/test_ifs/test_keyboard.rs create mode 100644 src/it/test_ifs/test_pointer.rs create mode 100644 src/it/test_ifs/test_seat.rs create mode 100644 src/it/test_utils/test_expected_event.rs create mode 100644 src/it/tests/t0008_map_focus.rs create mode 100644 src/utils/once.rs diff --git a/src/it/test_backend.rs b/src/it/test_backend.rs index 5763cbdb..17b4d07d 100644 --- a/src/it/test_backend.rs +++ b/src/it/test_backend.rs @@ -7,8 +7,10 @@ use { Mode, MonitorInfo, TransformMatrix, }, compositor::TestFuture, + fixed::Fixed, render::{RenderContext, RenderError}, state::State, + time::Time, utils::{ clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue, }, @@ -36,11 +38,13 @@ pub struct TestBackend { pub state: Rc, pub test_future: TestFuture, pub default_connector: Rc, + pub default_mouse: Rc, + pub default_kb: Rc, } impl TestBackend { pub fn new(state: &Rc, future: TestFuture) -> Self { - let connector = Rc::new(TestConnector { + let default_connector = Rc::new(TestConnector { id: state.connector_ids.next(), kernel_id: ConnectorKernelId { ty: ConnectorType::VGA, @@ -49,10 +53,44 @@ impl TestBackend { events: Default::default(), on_change: Default::default(), }); + let default_mouse = Rc::new(TestBackendMouse { + common: TestInputDeviceCommon { + id: state.input_device_ids.next(), + removed: Cell::new(false), + events: Default::default(), + on_change: Default::default(), + capabilities: { + let chm = CopyHashMap::new(); + chm.set(InputDeviceCapability::Pointer, ()); + chm + }, + name: Rc::new("default-mouse".to_string()), + }, + transform_matrix: Cell::new([[1.0, 0.0], [0.0, 1.0]]), + accel_speed: Cell::new(1.0), + accel_profile: Cell::new(InputDeviceAccelProfile::Flat), + left_handed: Cell::new(false), + }); + let default_kb = Rc::new(TestBackendKb { + common: TestInputDeviceCommon { + id: state.input_device_ids.next(), + removed: Cell::new(false), + events: Default::default(), + on_change: Default::default(), + capabilities: { + let chm = CopyHashMap::new(); + chm.set(InputDeviceCapability::Keyboard, ()); + chm + }, + name: Rc::new("default-keyboard".to_string()), + }, + }); Self { state: state.clone(), test_future: future, - default_connector: connector, + default_connector, + default_mouse, + default_kb, } } @@ -76,6 +114,12 @@ impl TestBackend { width_mm: 80, height_mm: 60, })); + self.state + .backend_events + .push(BackendEvent::NewInputDevice(self.default_kb.clone())); + self.state + .backend_events + .push(BackendEvent::NewInputDevice(self.default_mouse.clone())); } fn create_render_context(&self) -> Result<(), TestBackendError> { @@ -189,50 +233,47 @@ impl Connector for TestConnector { } } -pub struct TestInputDevice { - pub id: InputDeviceId, - pub remove: Cell, - pub events: SyncQueue, - pub on_change: CloneCell>>, - pub capabilities: CopyHashMap, +pub struct TestBackendMouse { + pub common: TestInputDeviceCommon, pub transform_matrix: Cell, - pub name: Rc, pub accel_speed: Cell, pub accel_profile: Cell, pub left_handed: Cell, } -impl InputDevice for TestInputDevice { - fn id(&self) -> InputDeviceId { - self.id +impl TestBackendMouse { + pub fn rel(&self, dx: f64, dy: f64) { + self.common.event(InputEvent::Motion { + time_usec: Time::now_unchecked().usec(), + dx: Fixed::from_f64(dx * self.accel_speed.get()), + dy: Fixed::from_f64(dy * self.accel_speed.get()), + dx_unaccelerated: Fixed::from_f64(dx), + dy_unaccelerated: Fixed::from_f64(dy), + }) } +} - fn removed(&self) -> bool { - self.remove.get() +pub struct TestBackendKb { + pub common: TestInputDeviceCommon, +} + +impl TestInputDevice for TestBackendKb { + fn common(&self) -> &TestInputDeviceCommon { + &self.common } +} - fn event(&self) -> Option { - self.events.pop() - } - - fn on_change(&self, cb: Rc) { - self.on_change.set(Some(cb)); - } - - fn grab(&self, _grab: bool) { - // nothing - } - - fn has_capability(&self, cap: InputDeviceCapability) -> bool { - self.capabilities.contains(&cap) +impl TestInputDevice for TestBackendMouse { + fn common(&self) -> &TestInputDeviceCommon { + &self.common } fn set_left_handed(&self, left_handed: bool) { - self.left_handed.set(left_handed); + self.left_handed.set(left_handed) } fn set_accel_profile(&self, profile: InputDeviceAccelProfile) { - self.accel_profile.set(profile); + self.accel_profile.set(profile) } fn set_accel_speed(&self, speed: f64) { @@ -242,8 +283,88 @@ impl InputDevice for TestInputDevice { fn set_transform_matrix(&self, matrix: TransformMatrix) { self.transform_matrix.set(matrix); } +} - fn name(&self) -> Rc { - self.name.clone() +pub struct TestInputDeviceCommon { + pub id: InputDeviceId, + pub removed: Cell, + pub events: SyncQueue, + pub on_change: CloneCell>>, + pub capabilities: CopyHashMap, + pub name: Rc, +} + +impl TestInputDeviceCommon { + pub fn event(&self, e: InputEvent) { + self.events.push(e); + if let Some(oc) = self.on_change.get() { + oc(); + } + } +} + +trait TestInputDevice: InputDevice { + fn common(&self) -> &TestInputDeviceCommon; + + fn set_left_handed(&self, left_handed: bool) { + let _ = left_handed; + } + + fn set_accel_profile(&self, profile: InputDeviceAccelProfile) { + let _ = profile; + } + + fn set_accel_speed(&self, speed: f64) { + let _ = speed; + } + + fn set_transform_matrix(&self, matrix: TransformMatrix) { + let _ = matrix; + } +} + +impl InputDevice for T { + fn id(&self) -> InputDeviceId { + self.common().id + } + + fn removed(&self) -> bool { + self.common().removed.get() + } + + fn event(&self) -> Option { + self.common().events.pop() + } + + fn on_change(&self, cb: Rc) { + self.common().on_change.set(Some(cb)); + } + + fn grab(&self, _grab: bool) { + // nothing + } + + fn has_capability(&self, cap: InputDeviceCapability) -> bool { + self.common().capabilities.contains(&cap) + } + + fn set_left_handed(&self, left_handed: bool) { + ::set_left_handed(self, left_handed) + } + + fn set_accel_profile(&self, profile: InputDeviceAccelProfile) { + ::set_accel_profile(self, profile) + } + + fn set_accel_speed(&self, speed: f64) { + ::set_accel_speed(self, speed) + } + + fn set_transform_matrix(&self, matrix: TransformMatrix) { + ::set_transform_matrix(self, matrix) + } + + fn name(&self) -> Rc { + self.common().name.clone() } } diff --git a/src/it/test_client.rs b/src/it/test_client.rs index 132c42bd..f80272fd 100644 --- a/src/it/test_client.rs +++ b/src/it/test_client.rs @@ -3,11 +3,13 @@ use { cli::screenshot::buf_to_qoi, client::Client, format::ARGB8888, + globals::GlobalBase, it::{ - test_error::TestError, + test_error::{TestError, TestResult}, test_ifs::{ test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor, - test_registry::TestRegistry, test_shm::TestShm, + test_keyboard::TestKeyboard, test_pointer::TestPointer, + test_registry::TestRegistry, test_seat::TestSeat, test_shm::TestShm, test_subcompositor::TestSubcompositor, test_xdg_base::TestXdgWmBase, }, test_transport::TestTransport, @@ -32,12 +34,50 @@ pub struct TestClient { pub xdg: Rc, } +pub struct DefaultSeat { + pub seat: Rc, + pub kb: Rc, + pub pointer: Rc, +} + impl TestClient { #[allow(dead_code)] pub fn error(&self, msg: &str) { self.tran.error(msg) } + pub async fn get_default_seat(&self) -> TestResult { + self.tran.sync().await; + let seat = 'get_seat: { + for seat in self.tran.run.state.globals.seats.lock().values() { + if seat.seat_name() == "default" { + break 'get_seat seat.clone(); + } + } + bail!("Default seat not found"); + }; + let id = self.tran.id(); + let tseat = Rc::new(TestSeat { + id, + tran: self.tran.clone(), + server: Default::default(), + destroyed: Default::default(), + caps: Cell::new(0), + name: Default::default(), + }); + self.registry.bind(&tseat, seat.name().raw(), 7)?; + self.tran.sync().await; + let server = self.tran.get_server_obj(tseat.id)?; + tseat.server.set(Some(server)); + let pointer = tseat.get_pointer().await?; + let tkb = tseat.get_keyboard().await?; + Ok(DefaultSeat { + seat: tseat, + kb: tkb, + pointer, + }) + } + pub async fn sync(&self) { self.tran.sync().await } diff --git a/src/it/test_config.rs b/src/it/test_config.rs index bec6f24c..4d9437fb 100644 --- a/src/it/test_config.rs +++ b/src/it/test_config.rs @@ -1,5 +1,8 @@ use { - crate::{ifs::wl_seat::SeatId, it::test_error::TestError, utils::stack::Stack}, + crate::{ + backend::InputDeviceId, ifs::wl_seat::SeatId, it::test_error::TestError, + utils::stack::Stack, + }, isnt::std_1::primitive::IsntConstPtrExt, jay_config::{ _private::{ @@ -7,7 +10,7 @@ use { ipc::{ClientMessage, Response, ServerMessage}, ConfigEntry, VERSION, }, - input::Seat, + input::{InputDevice, Seat}, }, std::{cell::Cell, ops::Deref, ptr, rc::Rc}, }; @@ -161,6 +164,13 @@ impl TestConfig { }) } + pub fn set_input_device_seat(&self, id: InputDeviceId, seat: SeatId) -> Result<(), TestError> { + self.send(ClientMessage::SetSeat { + device: InputDevice(id.raw() as _), + seat: Seat(seat.raw() as _), + }) + } + fn clear(&self) { unsafe { if let Some(srv) = self.srv.take() { diff --git a/src/it/test_error.rs b/src/it/test_error.rs index d73d37fc..1e5fc018 100644 --- a/src/it/test_error.rs +++ b/src/it/test_error.rs @@ -6,6 +6,8 @@ use { }, }; +pub type TestResult = Result; + pub struct TestError { error: Box, source: Option>, diff --git a/src/it/test_ifs.rs b/src/it/test_ifs.rs index f724d6b4..58ca7d15 100644 --- a/src/it/test_ifs.rs +++ b/src/it/test_ifs.rs @@ -2,9 +2,12 @@ pub mod test_callback; pub mod test_compositor; pub mod test_display; pub mod test_jay_compositor; +pub mod test_keyboard; +pub mod test_pointer; pub mod test_region; pub mod test_registry; pub mod test_screenshot; +pub mod test_seat; pub mod test_shm; pub mod test_shm_buffer; pub mod test_shm_pool; diff --git a/src/it/test_ifs/test_keyboard.rs b/src/it/test_ifs/test_keyboard.rs new file mode 100644 index 00000000..3218d538 --- /dev/null +++ b/src/it/test_ifs/test_keyboard.rs @@ -0,0 +1,89 @@ +use { + crate::{ + ifs::wl_seat::wl_keyboard::WlKeyboard, + it::{ + test_error::TestResult, test_object::TestObject, test_transport::TestTransport, + test_utils::test_expected_event::TEEH, testrun::ParseFull, + }, + utils::{buffd::MsgParser, clonecell::CloneCell, once::Once}, + wire::{wl_keyboard::*, WlKeyboardId, WlSurfaceId}, + }, + std::rc::Rc, +}; + +pub struct TestEnterEvent { + pub serial: u32, + pub surface: WlSurfaceId, + pub keys: Vec, +} + +pub struct TestKeyboard { + pub id: WlKeyboardId, + pub tran: Rc, + pub server: CloneCell>>, + pub destroyed: Once, + pub enter: TEEH, +} + +impl TestKeyboard { + pub fn destroy(&self) -> TestResult { + if self.destroyed.set() { + self.tran.send(Release { self_id: self.id })?; + } + Ok(()) + } + + fn handle_keymap(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Keymap::parse_full(parser)?; + Ok(()) + } + + fn handle_enter(&self, parser: MsgParser<'_, '_>) -> TestResult { + let ev = Enter::parse_full(parser)?; + self.enter.push(TestEnterEvent { + serial: ev.serial, + surface: ev.surface, + keys: ev.keys.to_vec(), + }); + Ok(()) + } + + fn handle_leave(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Leave::parse_full(parser)?; + Ok(()) + } + + fn handle_key(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Key::parse_full(parser)?; + Ok(()) + } + + fn handle_modifiers(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Modifiers::parse_full(parser)?; + Ok(()) + } + + fn handle_repeat_info(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = RepeatInfo::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestKeyboard { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestKeyboard, WlKeyboard; + + KEYMAP => handle_keymap, + ENTER => handle_enter, + LEAVE => handle_leave, + KEY => handle_key, + MODIFIERS => handle_modifiers, + REPEAT_INFO => handle_repeat_info, +} + +impl TestObject for TestKeyboard {} diff --git a/src/it/test_ifs/test_pointer.rs b/src/it/test_ifs/test_pointer.rs new file mode 100644 index 00000000..66dd63cd --- /dev/null +++ b/src/it/test_ifs/test_pointer.rs @@ -0,0 +1,95 @@ +use { + crate::{ + ifs::wl_seat::wl_pointer::WlPointer, + it::{ + test_error::TestResult, test_object::TestObject, test_transport::TestTransport, + testrun::ParseFull, + }, + utils::{buffd::MsgParser, clonecell::CloneCell}, + wire::{wl_pointer::*, WlPointerId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestPointer { + pub id: WlPointerId, + pub tran: Rc, + pub server: CloneCell>>, + pub destroyed: Cell, +} + +impl TestPointer { + pub fn destroy(&self) -> TestResult { + if !self.destroyed.replace(true) { + self.tran.send(Release { self_id: self.id })?; + } + 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_button(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Button::parse_full(parser)?; + Ok(()) + } + + fn handle_axis(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Axis::parse_full(parser)?; + Ok(()) + } + + fn handle_frame(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = Frame::parse_full(parser)?; + Ok(()) + } + + fn handle_axis_source(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = AxisSource::parse_full(parser)?; + Ok(()) + } + + fn handle_axis_stop(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = AxisStop::parse_full(parser)?; + Ok(()) + } + + fn handle_axis_discrete(&self, parser: MsgParser<'_, '_>) -> TestResult { + let _ev = AxisDiscrete::parse_full(parser)?; + Ok(()) + } +} + +impl Drop for TestPointer { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestPointer, WlPointer; + + ENTER => handle_enter, + LEAVE => handle_leave, + MOTION => handle_motion, + BUTTON => handle_button, + AXIS => handle_axis, + FRAME => handle_frame, + AXIS_SOURCE => handle_axis_source, + AXIS_STOP => handle_axis_stop, + AXIS_DISCRETE => handle_axis_discrete, +} + +impl TestObject for TestPointer {} diff --git a/src/it/test_ifs/test_seat.rs b/src/it/test_ifs/test_seat.rs new file mode 100644 index 00000000..7fb6871f --- /dev/null +++ b/src/it/test_ifs/test_seat.rs @@ -0,0 +1,99 @@ +use { + crate::{ + ifs::wl_seat::WlSeat, + it::{ + test_error::{TestError, TestResult}, + test_ifs::{test_keyboard::TestKeyboard, test_pointer::TestPointer}, + test_object::TestObject, + test_transport::TestTransport, + testrun::ParseFull, + }, + utils::{buffd::MsgParser, clonecell::CloneCell, once::Once}, + wire::{wl_seat::*, WlSeatId}, + }, + std::{cell::Cell, rc::Rc}, +}; + +pub struct TestSeat { + pub id: WlSeatId, + pub tran: Rc, + pub server: CloneCell>>, + pub destroyed: Once, + pub caps: Cell, + pub name: CloneCell>>, +} + +impl TestSeat { + pub fn destroy(&self) -> Result<(), TestError> { + if self.destroyed.set() { + self.tran.send(Release { self_id: self.id })?; + } + Ok(()) + } + + pub async fn get_keyboard(&self) -> TestResult> { + let id = self.tran.id(); + self.tran.send(GetKeyboard { + self_id: self.id, + id, + })?; + let kb = Rc::new(TestKeyboard { + id, + tran: self.tran.clone(), + server: Default::default(), + destroyed: Default::default(), + enter: Default::default(), + }); + self.tran.add_obj(kb.clone())?; + self.tran.sync().await; + let server = self.tran.get_server_obj(id)?; + kb.server.set(Some(server)); + Ok(kb) + } + + pub async fn get_pointer(&self) -> TestResult> { + let id = self.tran.id(); + self.tran.send(GetPointer { + self_id: self.id, + id, + })?; + let pointer = Rc::new(TestPointer { + id, + tran: self.tran.clone(), + server: Default::default(), + destroyed: Default::default(), + }); + self.tran.add_obj(pointer.clone())?; + self.tran.sync().await; + let server = self.tran.get_server_obj(id)?; + pointer.server.set(Some(server)); + Ok(pointer) + } + + fn handle_capabilities(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Capabilities::parse_full(parser)?; + self.caps.set(ev.capabilities); + Ok(()) + } + + fn handle_name(&self, parser: MsgParser<'_, '_>) -> Result<(), TestError> { + let ev = Name::parse_full(parser)?; + self.name.set(Some(Rc::new(ev.name.to_string()))); + Ok(()) + } +} + +impl Drop for TestSeat { + fn drop(&mut self) { + let _ = self.destroy(); + } +} + +test_object! { + TestSeat, WlSeat; + + CAPABILITIES => handle_capabilities, + NAME => handle_name, +} + +impl TestObject for TestSeat {} diff --git a/src/it/test_ifs/test_subcompositor.rs b/src/it/test_ifs/test_subcompositor.rs index 7c18b730..c3d9cf80 100644 --- a/src/it/test_ifs/test_subcompositor.rs +++ b/src/it/test_ifs/test_subcompositor.rs @@ -40,7 +40,7 @@ impl TestSubcompositor { id, tran: self.tran.clone(), destroyed: Cell::new(false), - server: self.tran.get_object(id)?, + server: self.tran.get_server_obj(id)?, }); self.tran.add_obj(ss.clone())?; Ok(ss) diff --git a/src/it/test_transport.rs b/src/it/test_transport.rs index 5b607394..7c155c8e 100644 --- a/src/it/test_transport.rs +++ b/src/it/test_transport.rs @@ -172,7 +172,7 @@ impl TestTransport { Ok(()) } - pub fn get_object, T: 'static>(&self, id: I) -> Result, TestError> { + pub fn get_server_obj, T: 'static>(&self, id: I) -> Result, TestError> { let client = self.get_client()?; client.objects.get_obj(id.into())?.downcast() } diff --git a/src/it/test_utils.rs b/src/it/test_utils.rs index ced27f4e..e3a3396b 100644 --- a/src/it/test_utils.rs +++ b/src/it/test_utils.rs @@ -1,2 +1,3 @@ +pub mod test_expected_event; pub mod test_object_ext; pub mod test_window; diff --git a/src/it/test_utils/test_expected_event.rs b/src/it/test_utils/test_expected_event.rs new file mode 100644 index 00000000..106d6f9d --- /dev/null +++ b/src/it/test_utils/test_expected_event.rs @@ -0,0 +1,71 @@ +use { + crate::{it::test_error::TestResult, utils::clonecell::CloneCell}, + std::{cell::RefCell, collections::VecDeque, rc::Rc}, +}; + +pub struct TestExpectedEvent { + data: Rc>, + holder: Rc>, +} + +impl TestExpectedEvent { + pub fn next(&self) -> TestResult { + match self.data.events.borrow_mut().pop_front() { + Some(t) => Ok(t), + _ => bail!("No event occurred"), + } + } + + pub fn last(&self) -> TestResult { + match self.data.events.borrow_mut().pop_back() { + Some(t) => Ok(t), + _ => bail!("No event occurred"), + } + } +} + +pub struct TestExpectedEventHolder { + data: CloneCell>>>, +} + +pub type TEEH = Rc>; + +impl Default for TestExpectedEventHolder { + fn default() -> Self { + Self { + data: Default::default(), + } + } +} + +impl TestExpectedEventHolder { + pub fn expect(self: &Rc) -> TestResult> { + if self.data.get().is_some() { + bail!("There is already an expected event data"); + } + let data = Rc::new(TestExpectedEventData { + events: Default::default(), + }); + self.data.set(Some(data.clone())); + Ok(TestExpectedEvent { + data, + holder: self.clone(), + }) + } + + pub fn push(&self, t: T) { + if let Some(data) = self.data.get() { + data.events.borrow_mut().push_back(t); + } + } +} + +struct TestExpectedEventData { + events: RefCell>, +} + +impl Drop for TestExpectedEvent { + fn drop(&mut self) { + self.holder.data.set(None); + } +} diff --git a/src/it/testrun.rs b/src/it/testrun.rs index 4a19e12b..2fa875c9 100644 --- a/src/it/testrun.rs +++ b/src/it/testrun.rs @@ -3,7 +3,7 @@ use { client::{ClientId, RequestParser}, ifs::wl_seat::WlSeatGlobal, it::{ - test_backend::TestBackend, + test_backend::{TestBackend, TestBackendKb, TestBackendMouse, TestConnector}, test_client::TestClient, test_config::TestConfig, test_error::{TestError, TestErrorExt}, @@ -12,6 +12,7 @@ use { }, object::WL_DISPLAY_ID, state::State, + tree::OutputNode, utils::{bitfield::Bitfield, buffd::MsgParser, oserror::OsErrorExt, stack::Stack}, }, std::{ @@ -102,6 +103,28 @@ impl TestRun { } bail!("Seat {} does not exist", id) } + + pub async fn create_default_setup(&self) -> Result { + self.backend.install_default(); + let seat = self.get_seat("default")?; + self.state.eng.yield_now().await; + let output = match self.state.outputs.lock().values().next() { + None => bail!("No output"), + Some(d) => d.node.clone(), + }; + self.cfg + .set_input_device_seat(self.backend.default_kb.common.id, seat.id())?; + self.cfg + .set_input_device_seat(self.backend.default_mouse.common.id, seat.id())?; + self.state.eng.yield_now().await; + Ok(DefaultSetup { + connector: self.backend.default_connector.clone(), + output, + kb: self.backend.default_kb.clone(), + mouse: self.backend.default_mouse.clone(), + seat, + }) + } } pub trait ParseFull<'a>: Sized { @@ -115,3 +138,11 @@ impl<'a, T: RequestParser<'a>> ParseFull<'a> for T { Ok(res) } } + +pub struct DefaultSetup { + pub connector: Rc, + pub output: Rc, + pub kb: Rc, + pub mouse: Rc, + pub seat: Rc, +} diff --git a/src/it/tests.rs b/src/it/tests.rs index 2e393ab0..c55b422c 100644 --- a/src/it/tests.rs +++ b/src/it/tests.rs @@ -33,6 +33,7 @@ mod t0004_quit; mod t0005_create_seat; mod t0006_region; mod t0007_subsurface; +mod t0008_map_focus; pub trait TestCase: Sync { fn name(&self) -> &'static str; @@ -58,5 +59,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> { t0005_create_seat, t0006_region, t0007_subsurface, + t0008_map_focus, } } diff --git a/src/it/tests/t0007_subsurface.rs b/src/it/tests/t0007_subsurface.rs index 3fbdbd19..f2ba6a33 100644 --- a/src/it/tests/t0007_subsurface.rs +++ b/src/it/tests/t0007_subsurface.rs @@ -9,6 +9,7 @@ use { testcase!(); +/// Test subsurface positioning async fn test(run: Rc) -> Result<(), TestError> { run.backend.install_default(); diff --git a/src/it/tests/t0008_map_focus.rs b/src/it/tests/t0008_map_focus.rs new file mode 100644 index 00000000..3cef906d --- /dev/null +++ b/src/it/tests/t0008_map_focus.rs @@ -0,0 +1,27 @@ +use { + crate::it::{ + test_error::{TestError, TestErrorExt}, + testrun::TestRun, + }, + std::rc::Rc, +}; + +testcase!(); + +async fn test(run: Rc) -> Result<(), TestError> { + let ds = run.create_default_setup().await?; + ds.mouse.rel(1.0, 1.0); + + let client = run.create_client().await?; + let default_seat = client.get_default_seat().await?; + + let enter = default_seat.kb.enter.expect()?; + + let window = client.create_window().await?; + window.map().await?; + + let enter = enter.next().with_context(|| "Did not enter")?; + tassert_eq!(enter.surface, window.surface.id); + + Ok(()) +} diff --git a/src/time.rs b/src/time.rs index 56a89109..7811640a 100644 --- a/src/time.rs +++ b/src/time.rs @@ -36,6 +36,13 @@ impl Time { Ok(Self(time)) } + #[allow(dead_code)] + pub fn now_unchecked() -> Time { + let mut time = uapi::pod_zeroed(); + let _ = uapi::clock_gettime(c::CLOCK_MONOTONIC, &mut time); + Self(time) + } + pub fn round_to_ms(self) -> Time { if self.0.tv_nsec > 999_000_000 { Time(c::timespec { @@ -49,6 +56,13 @@ impl Time { }) } } + + #[allow(dead_code)] + pub fn usec(self) -> u64 { + let sec = self.0.tv_sec as u64 * 1_000_000; + let nsec = self.0.tv_nsec as u64 / 1_000; + sec + nsec + } } impl Eq for Time {} diff --git a/src/utils.rs b/src/utils.rs index 9f700ab8..f53053a9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,6 +15,7 @@ pub mod log_on_drop; pub mod nonblock; pub mod num_cpus; pub mod numcell; +pub mod once; pub mod oserror; pub mod ptr_ext; pub mod queue; diff --git a/src/utils/once.rs b/src/utils/once.rs new file mode 100644 index 00000000..98bd572d --- /dev/null +++ b/src/utils/once.rs @@ -0,0 +1,26 @@ +#![allow(dead_code)] + +use std::{cell::Cell, future::Future}; + +#[derive(Default)] +pub struct Once { + done: Cell, +} + +impl Once { + pub fn set(&self) -> bool { + !self.done.replace(true) + } + + pub fn exec(&self, f: F) { + if !self.done.replace(true) { + f(); + } + } + + pub async fn exec_async, F: FnOnce() -> G>(&self, f: F) { + if !self.done.replace(true) { + f().await; + } + } +}