526 lines
17 KiB
Rust
526 lines
17 KiB
Rust
#![allow(clippy::declare_interior_mutable_const, clippy::type_complexity)]
|
|
|
|
use {
|
|
crate::{
|
|
_private::{
|
|
bincode_ops,
|
|
ipc::{ClientMessage, InitMessage, Response, ServerMessage},
|
|
logging, Config, ConfigEntry, ConfigEntryGen, VERSION,
|
|
},
|
|
drm::{
|
|
connector_type::{ConnectorType, CON_UNKNOWN},
|
|
Connector, Mode,
|
|
},
|
|
input::{acceleration::AccelProfile, capability::Capability, InputDevice, Seat},
|
|
keyboard::keymap::Keymap,
|
|
theme::Color,
|
|
Axis, Command, Direction, LogLevel, ModifiedKeySym, Workspace,
|
|
},
|
|
std::{
|
|
cell::{Cell, RefCell},
|
|
collections::{hash_map::Entry, HashMap},
|
|
ops::Deref,
|
|
ptr,
|
|
rc::Rc,
|
|
slice,
|
|
},
|
|
};
|
|
|
|
pub(crate) struct Client {
|
|
configure: extern "C" fn(),
|
|
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),
|
|
key_handlers: RefCell<HashMap<(Seat, ModifiedKeySym), Rc<dyn Fn()>>>,
|
|
response: RefCell<Vec<Response>>,
|
|
on_new_seat: RefCell<Option<Rc<dyn Fn(Seat)>>>,
|
|
on_new_input_device: RefCell<Option<Rc<dyn Fn(InputDevice)>>>,
|
|
on_connector_connected: RefCell<Option<Rc<dyn Fn(Connector)>>>,
|
|
on_new_connector: RefCell<Option<Rc<dyn Fn(Connector)>>>,
|
|
bufs: RefCell<Vec<Vec<u8>>>,
|
|
}
|
|
|
|
impl Drop for Client {
|
|
fn drop(&mut self) {
|
|
unsafe {
|
|
(self.srv_unref)(self.srv_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
thread_local! {
|
|
pub(crate) static CLIENT: std::cell::Cell<*const Client> = const { std::cell::Cell::new(ptr::null()) };
|
|
}
|
|
|
|
unsafe fn with_client<T, F: FnOnce(&Client) -> T>(data: *const u8, f: F) -> T {
|
|
struct Reset<'a> {
|
|
cell: &'a Cell<*const Client>,
|
|
val: *const Client,
|
|
}
|
|
impl Drop for Reset<'_> {
|
|
fn drop(&mut self) {
|
|
self.cell.set(self.val);
|
|
}
|
|
}
|
|
CLIENT.with(|cell| unsafe {
|
|
let client = data as *const Client;
|
|
Rc::increment_strong_count(client);
|
|
let client = Rc::from_raw(client);
|
|
let old = cell.replace(client.deref());
|
|
let _reset = Reset { cell, val: old };
|
|
f(&client)
|
|
})
|
|
}
|
|
|
|
impl<T: Config> ConfigEntryGen<T> {
|
|
pub const ENTRY: ConfigEntry = ConfigEntry {
|
|
version: VERSION,
|
|
init: Self::init,
|
|
unref,
|
|
handle_msg,
|
|
};
|
|
|
|
pub 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),
|
|
init_data: *const u8,
|
|
size: usize,
|
|
) -> *const u8 {
|
|
logging::init();
|
|
init(
|
|
srv_data,
|
|
srv_unref,
|
|
srv_handler,
|
|
init_data,
|
|
size,
|
|
T::configure,
|
|
)
|
|
}
|
|
}
|
|
|
|
pub 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),
|
|
init: *const u8,
|
|
size: usize,
|
|
f: extern "C" fn(),
|
|
) -> *const u8 {
|
|
let client = Rc::new(Client {
|
|
configure: f,
|
|
srv_data,
|
|
srv_unref,
|
|
srv_handler,
|
|
key_handlers: Default::default(),
|
|
response: Default::default(),
|
|
on_new_seat: Default::default(),
|
|
on_new_input_device: Default::default(),
|
|
on_connector_connected: Default::default(),
|
|
on_new_connector: Default::default(),
|
|
bufs: Default::default(),
|
|
});
|
|
let init = slice::from_raw_parts(init, size);
|
|
client.handle_init_msg(init);
|
|
Rc::into_raw(client) as *const u8
|
|
}
|
|
|
|
pub unsafe extern "C" fn unref(data: *const u8) {
|
|
let client = data as *const Client;
|
|
drop(Rc::from_raw(client));
|
|
}
|
|
|
|
pub unsafe extern "C" fn handle_msg(data: *const u8, msg: *const u8, size: usize) {
|
|
with_client(data, |client| {
|
|
let msg = slice::from_raw_parts(msg, size);
|
|
client.handle_msg(msg);
|
|
});
|
|
}
|
|
|
|
macro_rules! get_response {
|
|
($res:expr, $def:expr, $ty:ident, $($field:ident),+) => {
|
|
let ($($field,)+) = match $res {
|
|
Response::$ty { $($field,)+ } => ($($field,)+),
|
|
_ => {
|
|
log::error!("Server did not send a response to a {} request", stringify!($ty));
|
|
return $def;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
impl Client {
|
|
fn send(&self, msg: &ClientMessage) {
|
|
let mut buf = self.bufs.borrow_mut().pop().unwrap_or_default();
|
|
buf.clear();
|
|
bincode::encode_into_std_write(msg, &mut buf, bincode_ops()).unwrap();
|
|
unsafe {
|
|
(self.srv_handler)(self.srv_data, buf.as_ptr(), buf.len());
|
|
}
|
|
self.bufs.borrow_mut().push(buf);
|
|
}
|
|
|
|
fn send_with_response(&self, msg: &ClientMessage) -> Response {
|
|
self.with_response(|| self.send(msg))
|
|
}
|
|
|
|
pub fn spawn(&self, command: &Command) {
|
|
let env = command
|
|
.env
|
|
.iter()
|
|
.map(|(a, b)| (a.to_string(), b.to_string()))
|
|
.collect();
|
|
self.send(&ClientMessage::Run {
|
|
prog: &command.prog,
|
|
args: command.args.clone(),
|
|
env,
|
|
});
|
|
}
|
|
|
|
pub fn grab(&self, kb: InputDevice, grab: bool) {
|
|
self.send(&ClientMessage::GrabKb { kb, grab });
|
|
}
|
|
|
|
pub fn focus(&self, seat: Seat, direction: Direction) {
|
|
self.send(&ClientMessage::Focus { seat, direction });
|
|
}
|
|
|
|
pub fn move_(&self, seat: Seat, direction: Direction) {
|
|
self.send(&ClientMessage::Move { seat, direction });
|
|
}
|
|
|
|
pub fn unbind<T: Into<ModifiedKeySym>>(&self, seat: Seat, mod_sym: T) {
|
|
let mod_sym = mod_sym.into();
|
|
let deregister = self
|
|
.key_handlers
|
|
.borrow_mut()
|
|
.remove(&(seat, mod_sym))
|
|
.is_some();
|
|
if deregister {
|
|
self.send(&ClientMessage::RemoveShortcut {
|
|
seat,
|
|
mods: mod_sym.mods,
|
|
sym: mod_sym.sym,
|
|
})
|
|
}
|
|
}
|
|
|
|
fn with_response<F: FnOnce()>(&self, f: F) -> Response {
|
|
f();
|
|
self.response.borrow_mut().pop().unwrap_or(Response::None)
|
|
}
|
|
|
|
pub fn seats(&self) -> Vec<Seat> {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetSeats));
|
|
get_response!(res, vec![], GetSeats, seats);
|
|
seats
|
|
}
|
|
|
|
pub fn mono(&self, seat: Seat) -> bool {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetMono { seat }));
|
|
get_response!(res, false, GetMono, mono);
|
|
mono
|
|
}
|
|
|
|
pub fn get_workspace(&self, name: &str) -> Workspace {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetWorkspace { name }));
|
|
get_response!(res, Workspace(0), GetWorkspace, workspace);
|
|
workspace
|
|
}
|
|
|
|
pub fn get_connector(&self, ty: ConnectorType, idx: u32) -> Connector {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetConnector { ty, idx }));
|
|
get_response!(res, Connector(0), GetConnector, connector);
|
|
connector
|
|
}
|
|
|
|
pub fn show_workspace(&self, seat: Seat, workspace: Workspace) {
|
|
self.send(&ClientMessage::ShowWorkspace { seat, workspace });
|
|
}
|
|
|
|
pub fn split(&self, seat: Seat) -> Axis {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetSplit { seat }));
|
|
get_response!(res, Axis::Horizontal, GetSplit, axis);
|
|
axis
|
|
}
|
|
|
|
pub fn toggle_floating(&self, seat: Seat) {
|
|
self.send(&ClientMessage::ToggleFloating { seat });
|
|
}
|
|
|
|
pub fn set_title_color(&self, color: Color) {
|
|
self.send(&ClientMessage::SetTitleColor { color });
|
|
}
|
|
|
|
pub fn set_border_color(&self, color: Color) {
|
|
self.send(&ClientMessage::SetBorderColor { color });
|
|
}
|
|
|
|
pub fn set_title_underline_color(&self, color: Color) {
|
|
self.send(&ClientMessage::SetTitleUnderlineColor { color });
|
|
}
|
|
|
|
pub fn set_background_color(&self, color: Color) {
|
|
self.send(&ClientMessage::SetBackgroundColor { color });
|
|
}
|
|
|
|
pub fn get_title_height(&self) -> i32 {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetTitleHeight));
|
|
get_response!(res, 0, GetTitleHeight, height);
|
|
height
|
|
}
|
|
|
|
pub fn get_border_width(&self) -> i32 {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetBorderWidth));
|
|
get_response!(res, 0, GetBorderWidth, width);
|
|
width
|
|
}
|
|
|
|
pub fn set_title_height(&self, height: i32) {
|
|
self.send(&ClientMessage::SetTitleHeight { height })
|
|
}
|
|
|
|
pub fn set_border_width(&self, width: i32) {
|
|
self.send(&ClientMessage::SetBorderWidth { width })
|
|
}
|
|
|
|
pub fn set_mono(&self, seat: Seat, mono: bool) {
|
|
self.send(&ClientMessage::SetMono { seat, mono });
|
|
}
|
|
|
|
pub fn set_split(&self, seat: Seat, axis: Axis) {
|
|
self.send(&ClientMessage::SetSplit { seat, axis });
|
|
}
|
|
|
|
pub fn create_split(&self, seat: Seat, axis: Axis) {
|
|
self.send(&ClientMessage::CreateSplit { seat, axis });
|
|
}
|
|
|
|
pub fn close(&self, seat: Seat) {
|
|
self.send(&ClientMessage::Close { seat });
|
|
}
|
|
|
|
pub fn focus_parent(&self, seat: Seat) {
|
|
self.send(&ClientMessage::FocusParent { seat });
|
|
}
|
|
|
|
pub fn create_seat(&self, name: &str) -> Seat {
|
|
let res = self.with_response(|| self.send(&ClientMessage::CreateSeat { name }));
|
|
get_response!(res, Seat(0), CreateSeat, seat);
|
|
seat
|
|
}
|
|
|
|
pub fn get_input_devices(&self, seat: Option<Seat>) -> Vec<InputDevice> {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetInputDevices { seat }));
|
|
get_response!(res, vec!(), GetInputDevices, devices);
|
|
devices
|
|
}
|
|
|
|
pub fn on_new_seat<F: Fn(Seat) + 'static>(&self, f: F) {
|
|
*self.on_new_seat.borrow_mut() = Some(Rc::new(f));
|
|
}
|
|
|
|
pub fn quit(&self) {
|
|
self.send(&ClientMessage::Quit)
|
|
}
|
|
|
|
pub fn switch_to_vt(&self, vtnr: u32) {
|
|
self.send(&ClientMessage::SwitchTo { vtnr })
|
|
}
|
|
|
|
pub fn on_new_input_device<F: Fn(InputDevice) + 'static>(&self, f: F) {
|
|
*self.on_new_input_device.borrow_mut() = Some(Rc::new(f));
|
|
}
|
|
|
|
pub fn connector_set_position(&self, connector: Connector, x: i32, y: i32) {
|
|
self.send(&ClientMessage::ConnectorSetPosition { connector, x, y });
|
|
}
|
|
|
|
pub fn connector_connected(&self, connector: Connector) -> bool {
|
|
let res = self.send_with_response(&ClientMessage::ConnectorConnected { connector });
|
|
get_response!(res, false, ConnectorConnected, connected);
|
|
connected
|
|
}
|
|
|
|
pub fn connector_type(&self, connector: Connector) -> ConnectorType {
|
|
let res = self.send_with_response(&ClientMessage::ConnectorType { connector });
|
|
get_response!(res, CON_UNKNOWN, ConnectorType, ty);
|
|
ty
|
|
}
|
|
|
|
pub fn connector_mode(&self, connector: Connector) -> Mode {
|
|
let res = self.send_with_response(&ClientMessage::ConnectorMode { connector });
|
|
get_response!(
|
|
res,
|
|
Mode::zeroed(),
|
|
ConnectorMode,
|
|
width,
|
|
height,
|
|
refresh_millihz
|
|
);
|
|
Mode {
|
|
width,
|
|
height,
|
|
refresh_millihz,
|
|
}
|
|
}
|
|
|
|
pub fn on_new_connector<F: Fn(Connector) + 'static>(&self, f: F) {
|
|
*self.on_new_connector.borrow_mut() = Some(Rc::new(f));
|
|
}
|
|
|
|
pub fn on_connector_connected<F: Fn(Connector) + 'static>(&self, f: F) {
|
|
*self.on_connector_connected.borrow_mut() = Some(Rc::new(f));
|
|
}
|
|
|
|
pub fn set_seat(&self, device: InputDevice, seat: Seat) {
|
|
self.send(&ClientMessage::SetSeat { device, seat })
|
|
}
|
|
|
|
pub fn set_left_handed(&self, device: InputDevice, left_handed: bool) {
|
|
self.send(&ClientMessage::SetLeftHanded {
|
|
device,
|
|
left_handed,
|
|
})
|
|
}
|
|
|
|
pub fn set_accel_profile(&self, device: InputDevice, profile: AccelProfile) {
|
|
self.send(&ClientMessage::SetAccelProfile { device, profile })
|
|
}
|
|
|
|
pub fn set_accel_speed(&self, device: InputDevice, speed: f64) {
|
|
self.send(&ClientMessage::SetAccelSpeed { device, speed })
|
|
}
|
|
|
|
pub fn set_transform_matrix(&self, device: InputDevice, matrix: [[f64; 2]; 2]) {
|
|
self.send(&ClientMessage::SetTransformMatrix { device, matrix })
|
|
}
|
|
|
|
pub fn device_name(&self, device: InputDevice) -> String {
|
|
let res = self.with_response(|| self.send(&ClientMessage::GetDeviceName { device }));
|
|
get_response!(res, String::new(), GetDeviceName, name);
|
|
name
|
|
}
|
|
|
|
pub fn has_capability(&self, device: InputDevice, cap: Capability) -> bool {
|
|
let res = self.with_response(|| self.send(&ClientMessage::HasCapability { device, cap }));
|
|
get_response!(res, false, HasCapability, has);
|
|
has
|
|
}
|
|
|
|
pub fn seat_set_keymap(&self, seat: Seat, keymap: Keymap) {
|
|
self.send(&ClientMessage::SeatSetKeymap { seat, keymap })
|
|
}
|
|
|
|
pub fn seat_set_repeat_rate(&self, seat: Seat, rate: i32, delay: i32) {
|
|
self.send(&ClientMessage::SeatSetRepeatRate { seat, rate, delay })
|
|
}
|
|
|
|
pub fn seat_get_repeat_rate(&self, seat: Seat) -> (i32, i32) {
|
|
let res = self.with_response(|| self.send(&ClientMessage::SeatGetRepeatRate { seat }));
|
|
get_response!(res, (25, 250), GetRepeatRate, rate, delay);
|
|
(rate, delay)
|
|
}
|
|
|
|
pub fn parse_keymap(&self, keymap: &str) -> Keymap {
|
|
let res = self.with_response(|| self.send(&ClientMessage::ParseKeymap { keymap }));
|
|
get_response!(res, Keymap(0), ParseKeymap, keymap);
|
|
keymap
|
|
}
|
|
|
|
pub fn bind<T: Into<ModifiedKeySym>, F: Fn() + 'static>(&self, seat: Seat, mod_sym: T, f: F) {
|
|
let mod_sym = mod_sym.into();
|
|
let register = {
|
|
let mut kh = self.key_handlers.borrow_mut();
|
|
let f = Rc::new(f);
|
|
match kh.entry((seat, mod_sym)) {
|
|
Entry::Occupied(mut o) => {
|
|
*o.get_mut() = f;
|
|
false
|
|
}
|
|
Entry::Vacant(v) => {
|
|
v.insert(f);
|
|
true
|
|
}
|
|
}
|
|
};
|
|
if register {
|
|
self.send(&ClientMessage::AddShortcut {
|
|
seat,
|
|
mods: mod_sym.mods,
|
|
sym: mod_sym.sym,
|
|
});
|
|
}
|
|
}
|
|
|
|
pub fn log(&self, level: LogLevel, msg: &str, file: Option<&str>, line: Option<u32>) {
|
|
self.send(&ClientMessage::Log {
|
|
level,
|
|
msg,
|
|
file,
|
|
line,
|
|
})
|
|
}
|
|
|
|
fn handle_msg(&self, msg: &[u8]) {
|
|
let res = bincode::decode_from_slice::<ServerMessage, _>(msg, bincode_ops());
|
|
let (msg, _) = match res {
|
|
Ok(msg) => msg,
|
|
Err(e) => {
|
|
let msg = format!("could not deserialize message: {}", e);
|
|
self.log(LogLevel::Error, &msg, None, None);
|
|
return;
|
|
}
|
|
};
|
|
match msg {
|
|
ServerMessage::Configure => {
|
|
(self.configure)();
|
|
}
|
|
ServerMessage::Response { response } => {
|
|
self.response.borrow_mut().push(response);
|
|
}
|
|
ServerMessage::InvokeShortcut { seat, mods, sym } => {
|
|
let ms = ModifiedKeySym { mods, sym };
|
|
let handler = self.key_handlers.borrow_mut().get(&(seat, ms)).cloned();
|
|
if let Some(handler) = handler {
|
|
handler();
|
|
}
|
|
}
|
|
ServerMessage::NewInputDevice { device } => {
|
|
let handler = self.on_new_input_device.borrow_mut().clone();
|
|
if let Some(handler) = handler {
|
|
handler(device);
|
|
}
|
|
}
|
|
ServerMessage::DelInputDevice { .. } => {}
|
|
ServerMessage::ConnectorConnect { device } => {
|
|
let handler = self.on_connector_connected.borrow_mut().clone();
|
|
if let Some(handler) = handler {
|
|
handler(device);
|
|
}
|
|
}
|
|
ServerMessage::ConnectorDisconnect { .. } => {}
|
|
ServerMessage::NewConnector { device } => {
|
|
let handler = self.on_new_connector.borrow_mut().clone();
|
|
if let Some(handler) = handler {
|
|
handler(device);
|
|
}
|
|
}
|
|
ServerMessage::DelConnector { .. } => {}
|
|
}
|
|
}
|
|
|
|
fn handle_init_msg(&self, msg: &[u8]) {
|
|
let (init, _) = match bincode::decode_from_slice::<InitMessage, _>(msg, bincode_ops()) {
|
|
Ok(m) => m,
|
|
Err(e) => {
|
|
let msg = format!("could not deserialize message: {}", e);
|
|
self.log(LogLevel::Error, &msg, None, None);
|
|
return;
|
|
}
|
|
};
|
|
match init {
|
|
InitMessage::V1(_) => {}
|
|
}
|
|
}
|
|
}
|