it: test window gains kb focus when mapped
This commit is contained in:
parent
cbf539cbcc
commit
c827a93dbb
19 changed files with 672 additions and 39 deletions
|
|
@ -7,8 +7,10 @@ use {
|
||||||
Mode, MonitorInfo, TransformMatrix,
|
Mode, MonitorInfo, TransformMatrix,
|
||||||
},
|
},
|
||||||
compositor::TestFuture,
|
compositor::TestFuture,
|
||||||
|
fixed::Fixed,
|
||||||
render::{RenderContext, RenderError},
|
render::{RenderContext, RenderError},
|
||||||
state::State,
|
state::State,
|
||||||
|
time::Time,
|
||||||
utils::{
|
utils::{
|
||||||
clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue,
|
clonecell::CloneCell, copyhashmap::CopyHashMap, oserror::OsError, syncqueue::SyncQueue,
|
||||||
},
|
},
|
||||||
|
|
@ -36,11 +38,13 @@ pub struct TestBackend {
|
||||||
pub state: Rc<State>,
|
pub state: Rc<State>,
|
||||||
pub test_future: TestFuture,
|
pub test_future: TestFuture,
|
||||||
pub default_connector: Rc<TestConnector>,
|
pub default_connector: Rc<TestConnector>,
|
||||||
|
pub default_mouse: Rc<TestBackendMouse>,
|
||||||
|
pub default_kb: Rc<TestBackendKb>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestBackend {
|
impl TestBackend {
|
||||||
pub fn new(state: &Rc<State>, future: TestFuture) -> Self {
|
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(),
|
id: state.connector_ids.next(),
|
||||||
kernel_id: ConnectorKernelId {
|
kernel_id: ConnectorKernelId {
|
||||||
ty: ConnectorType::VGA,
|
ty: ConnectorType::VGA,
|
||||||
|
|
@ -49,10 +53,44 @@ impl TestBackend {
|
||||||
events: Default::default(),
|
events: Default::default(),
|
||||||
on_change: 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 {
|
Self {
|
||||||
state: state.clone(),
|
state: state.clone(),
|
||||||
test_future: future,
|
test_future: future,
|
||||||
default_connector: connector,
|
default_connector,
|
||||||
|
default_mouse,
|
||||||
|
default_kb,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +114,12 @@ impl TestBackend {
|
||||||
width_mm: 80,
|
width_mm: 80,
|
||||||
height_mm: 60,
|
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> {
|
fn create_render_context(&self) -> Result<(), TestBackendError> {
|
||||||
|
|
@ -189,50 +233,47 @@ impl Connector for TestConnector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestInputDevice {
|
pub struct TestBackendMouse {
|
||||||
pub id: InputDeviceId,
|
pub common: TestInputDeviceCommon,
|
||||||
pub remove: Cell<bool>,
|
|
||||||
pub events: SyncQueue<InputEvent>,
|
|
||||||
pub on_change: CloneCell<Option<Rc<dyn Fn()>>>,
|
|
||||||
pub capabilities: CopyHashMap<InputDeviceCapability, ()>,
|
|
||||||
pub transform_matrix: Cell<TransformMatrix>,
|
pub transform_matrix: Cell<TransformMatrix>,
|
||||||
pub name: Rc<String>,
|
|
||||||
pub accel_speed: Cell<f64>,
|
pub accel_speed: Cell<f64>,
|
||||||
pub accel_profile: Cell<InputDeviceAccelProfile>,
|
pub accel_profile: Cell<InputDeviceAccelProfile>,
|
||||||
pub left_handed: Cell<bool>,
|
pub left_handed: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InputDevice for TestInputDevice {
|
impl TestBackendMouse {
|
||||||
fn id(&self) -> InputDeviceId {
|
pub fn rel(&self, dx: f64, dy: f64) {
|
||||||
self.id
|
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 {
|
pub struct TestBackendKb {
|
||||||
self.remove.get()
|
pub common: TestInputDeviceCommon,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestInputDevice for TestBackendKb {
|
||||||
|
fn common(&self) -> &TestInputDeviceCommon {
|
||||||
|
&self.common
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn event(&self) -> Option<InputEvent> {
|
impl TestInputDevice for TestBackendMouse {
|
||||||
self.events.pop()
|
fn common(&self) -> &TestInputDeviceCommon {
|
||||||
}
|
&self.common
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_left_handed(&self, left_handed: bool) {
|
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) {
|
fn set_accel_profile(&self, profile: InputDeviceAccelProfile) {
|
||||||
self.accel_profile.set(profile);
|
self.accel_profile.set(profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_accel_speed(&self, speed: f64) {
|
fn set_accel_speed(&self, speed: f64) {
|
||||||
|
|
@ -242,8 +283,88 @@ impl InputDevice for TestInputDevice {
|
||||||
fn set_transform_matrix(&self, matrix: TransformMatrix) {
|
fn set_transform_matrix(&self, matrix: TransformMatrix) {
|
||||||
self.transform_matrix.set(matrix);
|
self.transform_matrix.set(matrix);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn name(&self) -> Rc<String> {
|
pub struct TestInputDeviceCommon {
|
||||||
self.name.clone()
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@ use {
|
||||||
cli::screenshot::buf_to_qoi,
|
cli::screenshot::buf_to_qoi,
|
||||||
client::Client,
|
client::Client,
|
||||||
format::ARGB8888,
|
format::ARGB8888,
|
||||||
|
globals::GlobalBase,
|
||||||
it::{
|
it::{
|
||||||
test_error::TestError,
|
test_error::{TestError, TestResult},
|
||||||
test_ifs::{
|
test_ifs::{
|
||||||
test_compositor::TestCompositor, test_jay_compositor::TestJayCompositor,
|
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_subcompositor::TestSubcompositor, test_xdg_base::TestXdgWmBase,
|
||||||
},
|
},
|
||||||
test_transport::TestTransport,
|
test_transport::TestTransport,
|
||||||
|
|
@ -32,12 +34,50 @@ pub struct TestClient {
|
||||||
pub xdg: Rc<TestXdgWmBase>,
|
pub xdg: Rc<TestXdgWmBase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct DefaultSeat {
|
||||||
|
pub seat: Rc<TestSeat>,
|
||||||
|
pub kb: Rc<TestKeyboard>,
|
||||||
|
pub pointer: Rc<TestPointer>,
|
||||||
|
}
|
||||||
|
|
||||||
impl TestClient {
|
impl TestClient {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn error(&self, msg: &str) {
|
pub fn error(&self, msg: &str) {
|
||||||
self.tran.error(msg)
|
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) {
|
pub async fn sync(&self) {
|
||||||
self.tran.sync().await
|
self.tran.sync().await
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
use {
|
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,
|
isnt::std_1::primitive::IsntConstPtrExt,
|
||||||
jay_config::{
|
jay_config::{
|
||||||
_private::{
|
_private::{
|
||||||
|
|
@ -7,7 +10,7 @@ use {
|
||||||
ipc::{ClientMessage, Response, ServerMessage},
|
ipc::{ClientMessage, Response, ServerMessage},
|
||||||
ConfigEntry, VERSION,
|
ConfigEntry, VERSION,
|
||||||
},
|
},
|
||||||
input::Seat,
|
input::{InputDevice, Seat},
|
||||||
},
|
},
|
||||||
std::{cell::Cell, ops::Deref, ptr, rc::Rc},
|
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) {
|
fn clear(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
if let Some(srv) = self.srv.take() {
|
if let Some(srv) = self.srv.take() {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ use {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub type TestResult<T = ()> = Result<T, TestError>;
|
||||||
|
|
||||||
pub struct TestError {
|
pub struct TestError {
|
||||||
error: Box<dyn Error + 'static>,
|
error: Box<dyn Error + 'static>,
|
||||||
source: Option<Box<TestError>>,
|
source: Option<Box<TestError>>,
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ pub mod test_callback;
|
||||||
pub mod test_compositor;
|
pub mod test_compositor;
|
||||||
pub mod test_display;
|
pub mod test_display;
|
||||||
pub mod test_jay_compositor;
|
pub mod test_jay_compositor;
|
||||||
|
pub mod test_keyboard;
|
||||||
|
pub mod test_pointer;
|
||||||
pub mod test_region;
|
pub mod test_region;
|
||||||
pub mod test_registry;
|
pub mod test_registry;
|
||||||
pub mod test_screenshot;
|
pub mod test_screenshot;
|
||||||
|
pub mod test_seat;
|
||||||
pub mod test_shm;
|
pub mod test_shm;
|
||||||
pub mod test_shm_buffer;
|
pub mod test_shm_buffer;
|
||||||
pub mod test_shm_pool;
|
pub mod test_shm_pool;
|
||||||
|
|
|
||||||
89
src/it/test_ifs/test_keyboard.rs
Normal file
89
src/it/test_ifs/test_keyboard.rs
Normal 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 {}
|
||||||
95
src/it/test_ifs/test_pointer.rs
Normal file
95
src/it/test_ifs/test_pointer.rs
Normal 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 {}
|
||||||
99
src/it/test_ifs/test_seat.rs
Normal file
99
src/it/test_ifs/test_seat.rs
Normal 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 {}
|
||||||
|
|
@ -40,7 +40,7 @@ impl TestSubcompositor {
|
||||||
id,
|
id,
|
||||||
tran: self.tran.clone(),
|
tran: self.tran.clone(),
|
||||||
destroyed: Cell::new(false),
|
destroyed: Cell::new(false),
|
||||||
server: self.tran.get_object(id)?,
|
server: self.tran.get_server_obj(id)?,
|
||||||
});
|
});
|
||||||
self.tran.add_obj(ss.clone())?;
|
self.tran.add_obj(ss.clone())?;
|
||||||
Ok(ss)
|
Ok(ss)
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ impl TestTransport {
|
||||||
Ok(())
|
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()?;
|
let client = self.get_client()?;
|
||||||
client.objects.get_obj(id.into())?.downcast()
|
client.objects.get_obj(id.into())?.downcast()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
|
pub mod test_expected_event;
|
||||||
pub mod test_object_ext;
|
pub mod test_object_ext;
|
||||||
pub mod test_window;
|
pub mod test_window;
|
||||||
|
|
|
||||||
71
src/it/test_utils/test_expected_event.rs
Normal file
71
src/it/test_utils/test_expected_event.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
client::{ClientId, RequestParser},
|
client::{ClientId, RequestParser},
|
||||||
ifs::wl_seat::WlSeatGlobal,
|
ifs::wl_seat::WlSeatGlobal,
|
||||||
it::{
|
it::{
|
||||||
test_backend::TestBackend,
|
test_backend::{TestBackend, TestBackendKb, TestBackendMouse, TestConnector},
|
||||||
test_client::TestClient,
|
test_client::TestClient,
|
||||||
test_config::TestConfig,
|
test_config::TestConfig,
|
||||||
test_error::{TestError, TestErrorExt},
|
test_error::{TestError, TestErrorExt},
|
||||||
|
|
@ -12,6 +12,7 @@ use {
|
||||||
},
|
},
|
||||||
object::WL_DISPLAY_ID,
|
object::WL_DISPLAY_ID,
|
||||||
state::State,
|
state::State,
|
||||||
|
tree::OutputNode,
|
||||||
utils::{bitfield::Bitfield, buffd::MsgParser, oserror::OsErrorExt, stack::Stack},
|
utils::{bitfield::Bitfield, buffd::MsgParser, oserror::OsErrorExt, stack::Stack},
|
||||||
},
|
},
|
||||||
std::{
|
std::{
|
||||||
|
|
@ -102,6 +103,28 @@ impl TestRun {
|
||||||
}
|
}
|
||||||
bail!("Seat {} does not exist", id)
|
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 {
|
pub trait ParseFull<'a>: Sized {
|
||||||
|
|
@ -115,3 +138,11 @@ impl<'a, T: RequestParser<'a>> ParseFull<'a> for T {
|
||||||
Ok(res)
|
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>,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ mod t0004_quit;
|
||||||
mod t0005_create_seat;
|
mod t0005_create_seat;
|
||||||
mod t0006_region;
|
mod t0006_region;
|
||||||
mod t0007_subsurface;
|
mod t0007_subsurface;
|
||||||
|
mod t0008_map_focus;
|
||||||
|
|
||||||
pub trait TestCase: Sync {
|
pub trait TestCase: Sync {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
@ -58,5 +59,6 @@ pub fn tests() -> Vec<&'static dyn TestCase> {
|
||||||
t0005_create_seat,
|
t0005_create_seat,
|
||||||
t0006_region,
|
t0006_region,
|
||||||
t0007_subsurface,
|
t0007_subsurface,
|
||||||
|
t0008_map_focus,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ use {
|
||||||
|
|
||||||
testcase!();
|
testcase!();
|
||||||
|
|
||||||
|
/// Test subsurface positioning
|
||||||
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
async fn test(run: Rc<TestRun>) -> Result<(), TestError> {
|
||||||
run.backend.install_default();
|
run.backend.install_default();
|
||||||
|
|
||||||
|
|
|
||||||
27
src/it/tests/t0008_map_focus.rs
Normal file
27
src/it/tests/t0008_map_focus.rs
Normal 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(())
|
||||||
|
}
|
||||||
14
src/time.rs
14
src/time.rs
|
|
@ -36,6 +36,13 @@ impl Time {
|
||||||
Ok(Self(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 {
|
pub fn round_to_ms(self) -> Time {
|
||||||
if self.0.tv_nsec > 999_000_000 {
|
if self.0.tv_nsec > 999_000_000 {
|
||||||
Time(c::timespec {
|
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 {}
|
impl Eq for Time {}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ pub mod log_on_drop;
|
||||||
pub mod nonblock;
|
pub mod nonblock;
|
||||||
pub mod num_cpus;
|
pub mod num_cpus;
|
||||||
pub mod numcell;
|
pub mod numcell;
|
||||||
|
pub mod once;
|
||||||
pub mod oserror;
|
pub mod oserror;
|
||||||
pub mod ptr_ext;
|
pub mod ptr_ext;
|
||||||
pub mod queue;
|
pub mod queue;
|
||||||
|
|
|
||||||
26
src/utils/once.rs
Normal file
26
src/utils/once.rs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue