This should not have any impact on existing configs since bincode claims compatibility of the wire format between 1.3.3 and 2.0.0.
260 lines
7.5 KiB
Rust
260 lines
7.5 KiB
Rust
use {
|
|
crate::{
|
|
backend::InputDeviceId,
|
|
ifs::wl_seat::SeatId,
|
|
it::test_error::{TestError, TestResult},
|
|
utils::{copyhashmap::CopyHashMap, stack::Stack},
|
|
},
|
|
bincode::Options,
|
|
isnt::std_1::primitive::IsntConstPtrExt,
|
|
jay_config::{
|
|
_private::{
|
|
bincode_ops,
|
|
ipc::{ClientMessage, Response, ServerMessage},
|
|
ConfigEntry, VERSION,
|
|
},
|
|
input::{InputDevice, Seat},
|
|
keyboard::{Keymap, ModifiedKeySym},
|
|
Axis, Direction,
|
|
},
|
|
std::{cell::Cell, ops::Deref, ptr, rc::Rc},
|
|
};
|
|
|
|
pub static TEST_CONFIG_ENTRY: ConfigEntry = ConfigEntry {
|
|
version: VERSION,
|
|
init,
|
|
unref,
|
|
handle_msg,
|
|
};
|
|
|
|
#[thread_local]
|
|
static mut CONFIG: *const TestConfig = ptr::null();
|
|
|
|
pub fn with_test_config<T, F>(f: F) -> T
|
|
where
|
|
F: FnOnce(Rc<TestConfig>) -> T,
|
|
{
|
|
unsafe {
|
|
let tc = Rc::new(TestConfig {
|
|
srv: Cell::new(None),
|
|
responses: Default::default(),
|
|
invoked_shortcuts: Default::default(),
|
|
graphics_initialized: Cell::new(false),
|
|
});
|
|
let old = CONFIG;
|
|
CONFIG = tc.deref();
|
|
let res = f(tc.clone());
|
|
CONFIG = old;
|
|
res
|
|
}
|
|
}
|
|
|
|
unsafe extern "C" fn init(
|
|
srv_data: *const u8,
|
|
srv_unref: unsafe extern "C" fn(data: *const u8),
|
|
srv_handler: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
|
|
_msg: *const u8,
|
|
_size: usize,
|
|
) -> *const u8 {
|
|
let tc = CONFIG;
|
|
assert!(tc.is_not_null());
|
|
Rc::increment_strong_count(tc);
|
|
{
|
|
let tc = &*tc;
|
|
tc.srv.set(Some(ServerData {
|
|
srv_data,
|
|
srv_unref,
|
|
srv_handler,
|
|
}));
|
|
}
|
|
tc.cast()
|
|
}
|
|
|
|
unsafe extern "C" fn unref(data: *const u8) {
|
|
Rc::decrement_strong_count(data.cast::<TestConfig>());
|
|
}
|
|
|
|
unsafe extern "C" fn handle_msg(data: *const u8, msg: *const u8, size: usize) {
|
|
let tc = &*data.cast::<TestConfig>();
|
|
let msg = std::slice::from_raw_parts(msg, size);
|
|
let res = bincode_ops().deserialize::<ServerMessage>(msg);
|
|
let msg = match res {
|
|
Ok(msg) => msg,
|
|
Err(e) => {
|
|
log::error!("could not deserialize message: {}", e);
|
|
return;
|
|
}
|
|
};
|
|
match msg {
|
|
ServerMessage::Configure { .. } => {}
|
|
ServerMessage::Response { response } => {
|
|
tc.responses.push(response);
|
|
}
|
|
ServerMessage::InvokeShortcut { seat, mods, sym } => {
|
|
tc.invoked_shortcuts
|
|
.set((SeatId::from_raw(seat.0 as _), mods | sym), ());
|
|
}
|
|
ServerMessage::NewInputDevice { .. } => {}
|
|
ServerMessage::DelInputDevice { .. } => {}
|
|
ServerMessage::ConnectorConnect { .. } => {}
|
|
ServerMessage::ConnectorDisconnect { .. } => {}
|
|
ServerMessage::NewConnector { .. } => {}
|
|
ServerMessage::DelConnector { .. } => {}
|
|
ServerMessage::TimerExpired { .. } => {}
|
|
ServerMessage::GraphicsInitialized => tc.graphics_initialized.set(true),
|
|
ServerMessage::Clear => tc.clear(),
|
|
ServerMessage::NewDrmDev { .. } => {}
|
|
ServerMessage::DelDrmDev { .. } => {}
|
|
ServerMessage::Idle => {}
|
|
ServerMessage::DevicesEnumerated => {}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
struct ServerData {
|
|
srv_data: *const u8,
|
|
srv_unref: unsafe extern "C" fn(data: *const u8),
|
|
srv_handler: unsafe extern "C" fn(data: *const u8, msg: *const u8, size: usize),
|
|
}
|
|
|
|
pub struct TestConfig {
|
|
srv: Cell<Option<ServerData>>,
|
|
responses: Stack<Response>,
|
|
pub invoked_shortcuts: CopyHashMap<(SeatId, ModifiedKeySym), ()>,
|
|
pub graphics_initialized: Cell<bool>,
|
|
}
|
|
|
|
macro_rules! get_response {
|
|
($res:expr, $ty:ident { $($field:ident),+ }) => {
|
|
let ($($field,)+) = match $res {
|
|
Response::$ty { $($field,)+ } => ($($field,)+),
|
|
_ => {
|
|
bail!("Server did not send a response to a {} request", stringify!($ty));
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
impl TestConfig {
|
|
fn send(&self, msg: ClientMessage) -> Result<(), TestError> {
|
|
self.send_(&msg)
|
|
}
|
|
|
|
fn send_(&self, msg: &ClientMessage) -> Result<(), TestError> {
|
|
let srv = match self.srv.get() {
|
|
Some(srv) => srv,
|
|
_ => bail!("srv not set"),
|
|
};
|
|
let mut buf = vec![];
|
|
bincode_ops().serialize_into(&mut buf, msg).unwrap();
|
|
unsafe {
|
|
(srv.srv_handler)(srv.srv_data, buf.as_ptr(), buf.len());
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn send_with_reply(&self, msg: ClientMessage) -> Result<Response, TestError> {
|
|
self.send_(&msg)?;
|
|
match self.responses.pop() {
|
|
Some(r) => Ok(r),
|
|
_ => bail!("Compositor did not send a response to {:?}", msg),
|
|
}
|
|
}
|
|
|
|
pub fn quit(&self) -> Result<(), TestError> {
|
|
self.send(ClientMessage::Quit)
|
|
}
|
|
|
|
pub fn get_seat(&self, name: &str) -> Result<SeatId, TestError> {
|
|
let reply = self.send_with_reply(ClientMessage::GetSeat { name })?;
|
|
get_response!(reply, GetSeat { seat });
|
|
Ok(SeatId::from_raw(seat.0 as _))
|
|
}
|
|
|
|
pub fn show_workspace(&self, seat: SeatId, name: &str) -> Result<(), TestError> {
|
|
let reply = self.send_with_reply(ClientMessage::GetWorkspace { name })?;
|
|
get_response!(reply, GetWorkspace { workspace });
|
|
self.send(ClientMessage::ShowWorkspace {
|
|
seat: Seat(seat.raw() as _),
|
|
workspace,
|
|
})
|
|
}
|
|
|
|
pub fn parse_keymap(&self, keymap: &str) -> Result<Keymap, TestError> {
|
|
let reply = self.send_with_reply(ClientMessage::ParseKeymap { keymap })?;
|
|
get_response!(reply, ParseKeymap { keymap });
|
|
if keymap.is_invalid() {
|
|
bail!("Could not parse the keymap");
|
|
}
|
|
Ok(keymap)
|
|
}
|
|
|
|
pub fn set_keymap(&self, seat: SeatId, keymap: Keymap) -> TestResult {
|
|
self.send(ClientMessage::SeatSetKeymap {
|
|
seat: Seat(seat.raw() as _),
|
|
keymap,
|
|
})
|
|
}
|
|
|
|
pub fn create_split(&self, seat: SeatId, axis: Axis) -> TestResult {
|
|
self.send(ClientMessage::CreateSplit {
|
|
seat: Seat(seat.raw() as _),
|
|
axis,
|
|
})
|
|
}
|
|
|
|
pub fn set_mono(&self, seat: SeatId, mono: bool) -> TestResult {
|
|
self.send(ClientMessage::SetMono {
|
|
seat: Seat(seat.raw() as _),
|
|
mono,
|
|
})
|
|
}
|
|
|
|
pub fn add_shortcut<T: Into<ModifiedKeySym>>(
|
|
&self,
|
|
seat: SeatId,
|
|
key: T,
|
|
) -> Result<(), TestError> {
|
|
let key = key.into();
|
|
self.send(ClientMessage::AddShortcut {
|
|
seat: Seat(seat.raw() as _),
|
|
mods: key.mods,
|
|
sym: key.sym,
|
|
})
|
|
}
|
|
|
|
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 _),
|
|
})
|
|
}
|
|
|
|
pub fn focus(&self, seat: SeatId, direction: Direction) -> TestResult {
|
|
self.send(ClientMessage::Focus {
|
|
seat: Seat(seat.raw() as _),
|
|
direction,
|
|
})
|
|
}
|
|
|
|
pub fn set_fullscreen(&self, seat: SeatId, fs: bool) -> TestResult {
|
|
self.send(ClientMessage::SetFullscreen {
|
|
seat: Seat(seat.raw() as _),
|
|
fullscreen: fs,
|
|
})
|
|
}
|
|
|
|
fn clear(&self) {
|
|
unsafe {
|
|
if let Some(srv) = self.srv.take() {
|
|
(srv.srv_unref)(srv.srv_data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drop for TestConfig {
|
|
fn drop(&mut self) {
|
|
self.clear();
|
|
}
|
|
}
|