1
0
Fork 0
forked from wry/wry

it: test window gains kb focus when mapped

This commit is contained in:
Julian Orth 2022-05-04 14:36:18 +02:00
parent cbf539cbcc
commit c827a93dbb
19 changed files with 672 additions and 39 deletions

View file

@ -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<State>,
pub test_future: TestFuture,
pub default_connector: Rc<TestConnector>,
pub default_mouse: Rc<TestBackendMouse>,
pub default_kb: Rc<TestBackendKb>,
}
impl TestBackend {
pub fn new(state: &Rc<State>, 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<bool>,
pub events: SyncQueue<InputEvent>,
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
pub capabilities: CopyHashMap<InputDeviceCapability, ()>,
pub struct TestBackendMouse {
pub common: TestInputDeviceCommon,
pub transform_matrix: Cell<TransformMatrix>,
pub name: Rc<String>,
pub accel_speed: Cell<f64>,
pub accel_profile: Cell<InputDeviceAccelProfile>,
pub left_handed: Cell<bool>,
}
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<InputEvent> {
self.events.pop()
}
fn on_change(&self, cb: Rc<dyn Fn()>) {
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<String> {
self.name.clone()
pub struct TestInputDeviceCommon {
pub id: InputDeviceId,
pub removed: Cell<bool>,
pub events: SyncQueue<InputEvent>,
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
pub capabilities: CopyHashMap<InputDeviceCapability, ()>,
pub name: Rc<String>,
}
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<T: TestInputDevice> InputDevice for T {
fn id(&self) -> InputDeviceId {
self.common().id
}
fn removed(&self) -> bool {
self.common().removed.get()
}
fn event(&self) -> Option<InputEvent> {
self.common().events.pop()
}
fn on_change(&self, cb: Rc<dyn Fn()>) {
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) {
<Self as TestInputDevice>::set_left_handed(self, left_handed)
}
fn set_accel_profile(&self, profile: InputDeviceAccelProfile) {
<Self as TestInputDevice>::set_accel_profile(self, profile)
}
fn set_accel_speed(&self, speed: f64) {
<Self as TestInputDevice>::set_accel_speed(self, speed)
}
fn set_transform_matrix(&self, matrix: TransformMatrix) {
<Self as TestInputDevice>::set_transform_matrix(self, matrix)
}
fn name(&self) -> Rc<String> {
self.common().name.clone()
}
}

View file

@ -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<TestXdgWmBase>,
}
pub struct DefaultSeat {
pub seat: Rc<TestSeat>,
pub kb: Rc<TestKeyboard>,
pub pointer: Rc<TestPointer>,
}
impl TestClient {
#[allow(dead_code)]
pub fn error(&self, msg: &str) {
self.tran.error(msg)
}
pub async fn get_default_seat(&self) -> TestResult<DefaultSeat> {
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
}

View file

@ -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() {

View file

@ -6,6 +6,8 @@ use {
},
};
pub type TestResult<T = ()> = Result<T, TestError>;
pub struct TestError {
error: Box<dyn Error + 'static>,
source: Option<Box<TestError>>,

View file

@ -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;

View file

@ -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<u32>,
}
pub struct TestKeyboard {
pub id: WlKeyboardId,
pub tran: Rc<TestTransport>,
pub server: CloneCell<Option<Rc<WlKeyboard>>>,
pub destroyed: Once,
pub enter: TEEH<TestEnterEvent>,
}
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 {}

View file

@ -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<TestTransport>,
pub server: CloneCell<Option<Rc<WlPointer>>>,
pub destroyed: Cell<bool>,
}
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 {}

View file

@ -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<TestTransport>,
pub server: CloneCell<Option<Rc<WlSeat>>>,
pub destroyed: Once,
pub caps: Cell<u32>,
pub name: CloneCell<Option<Rc<String>>>,
}
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<Rc<TestKeyboard>> {
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<Rc<TestPointer>> {
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 {}

View file

@ -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)

View file

@ -172,7 +172,7 @@ impl TestTransport {
Ok(())
}
pub fn get_object<I: Into<ObjectId>, T: 'static>(&self, id: I) -> Result<Rc<T>, TestError> {
pub fn get_server_obj<I: Into<ObjectId>, T: 'static>(&self, id: I) -> Result<Rc<T>, TestError> {
let client = self.get_client()?;
client.objects.get_obj(id.into())?.downcast()
}

View file

@ -1,2 +1,3 @@
pub mod test_expected_event;
pub mod test_object_ext;
pub mod test_window;

View file

@ -0,0 +1,71 @@
use {
crate::{it::test_error::TestResult, utils::clonecell::CloneCell},
std::{cell::RefCell, collections::VecDeque, rc::Rc},
};
pub struct TestExpectedEvent<T> {
data: Rc<TestExpectedEventData<T>>,
holder: Rc<TestExpectedEventHolder<T>>,
}
impl<T> TestExpectedEvent<T> {
pub fn next(&self) -> TestResult<T> {
match self.data.events.borrow_mut().pop_front() {
Some(t) => Ok(t),
_ => bail!("No event occurred"),
}
}
pub fn last(&self) -> TestResult<T> {
match self.data.events.borrow_mut().pop_back() {
Some(t) => Ok(t),
_ => bail!("No event occurred"),
}
}
}
pub struct TestExpectedEventHolder<T> {
data: CloneCell<Option<Rc<TestExpectedEventData<T>>>>,
}
pub type TEEH<T> = Rc<TestExpectedEventHolder<T>>;
impl<T> Default for TestExpectedEventHolder<T> {
fn default() -> Self {
Self {
data: Default::default(),
}
}
}
impl<T> TestExpectedEventHolder<T> {
pub fn expect(self: &Rc<Self>) -> TestResult<TestExpectedEvent<T>> {
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<T> {
events: RefCell<VecDeque<T>>,
}
impl<T> Drop for TestExpectedEvent<T> {
fn drop(&mut self) {
self.holder.data.set(None);
}
}

View file

@ -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<DefaultSetup, TestError> {
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<TestConnector>,
pub output: Rc<OutputNode>,
pub kb: Rc<TestBackendKb>,
pub mouse: Rc<TestBackendMouse>,
pub seat: Rc<WlSeatGlobal>,
}

View file

@ -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,
}
}

View file

@ -9,6 +9,7 @@ use {
testcase!();
/// Test subsurface positioning
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
run.backend.install_default();

View file

@ -0,0 +1,27 @@
use {
crate::it::{
test_error::{TestError, TestErrorExt},
testrun::TestRun,
},
std::rc::Rc,
};
testcase!();
async fn test(run: Rc<TestRun>) -> 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(())
}

View file

@ -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 {}

View file

@ -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;

26
src/utils/once.rs Normal file
View file

@ -0,0 +1,26 @@
#![allow(dead_code)]
use std::{cell::Cell, future::Future};
#[derive(Default)]
pub struct Once {
done: Cell<bool>,
}
impl Once {
pub fn set(&self) -> bool {
!self.done.replace(true)
}
pub fn exec<F: FnOnce()>(&self, f: F) {
if !self.done.replace(true) {
f();
}
}
pub async fn exec_async<G: Future<Output = ()>, F: FnOnce() -> G>(&self, f: F) {
if !self.done.replace(true) {
f().await;
}
}
}