1
0
Fork 0
forked from wry/wry
wry/src/ei/ei_ifs/ei_seat.rs
2024-07-25 19:40:29 +02:00

425 lines
13 KiB
Rust

use {
crate::{
backend::KeyState,
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::{
ei_button::EiButton,
ei_device::{EiDevice, EiDeviceInterface, EI_DEVICE_TYPE_VIRTUAL},
ei_keyboard::EiKeyboard,
ei_pointer::EiPointer,
ei_pointer_absolute::EiPointerAbsolute,
ei_scroll::EiScroll,
ei_touchscreen::EiTouchscreen,
},
ei_object::{EiInterface, EiObject, EiVersion},
EiContext,
},
fixed::Fixed,
ifs::wl_seat::{wl_pointer::PendingScroll, WlSeatGlobal},
leaks::Tracker,
tree::Node,
utils::{array, bitflags::BitflagsExt, clonecell::CloneCell},
wire_ei::{
ei_seat::{
Bind, Capability, Destroyed, Device, Done, EiSeatRequestHandler, Name, Release,
},
EiSeatId,
},
xkbcommon::{DynKeyboardState, KeyboardState, KeyboardStateId},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};
pub const EI_CAP_POINTER: u64 = 1 << 0;
pub const EI_CAP_POINTER_ABSOLUTE: u64 = 1 << 1;
pub const EI_CAP_SCROLL: u64 = 1 << 2;
pub const EI_CAP_BUTTON: u64 = 1 << 3;
pub const EI_CAP_KEYBOARD: u64 = 1 << 4;
pub const EI_CAP_TOUCHSCREEN: u64 = 1 << 5;
pub const EI_CAP_ALL: u64 = (1 << 6) - 1;
pub struct EiSeat {
pub id: EiSeatId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub seat: Rc<WlSeatGlobal>,
pub capabilities: Cell<u64>,
pub kb_state_id: Cell<KeyboardStateId>,
pub device: CloneCell<Option<Rc<EiDevice>>>,
pub pointer: CloneCell<Option<Rc<EiPointer>>>,
pub pointer_absolute: CloneCell<Option<Rc<EiPointerAbsolute>>>,
pub keyboard: CloneCell<Option<Rc<EiKeyboard>>>,
pub button: CloneCell<Option<Rc<EiButton>>>,
pub scroll: CloneCell<Option<Rc<EiScroll>>>,
pub touchscreen: CloneCell<Option<Rc<EiTouchscreen>>>,
}
impl EiSeat {
fn is_sender(&self) -> bool {
self.context() == EiContext::Sender
}
pub fn regions_changed(self: &Rc<Self>) {
if self.touchscreen.is_none() && self.pointer_absolute.is_none() {
return;
}
let kb_state = self.get_kb_state();
let kb_state = kb_state.borrow();
if let Err(e) = self.recreate_all(false, &kb_state) {
self.client.error(e);
}
}
pub fn handle_xkb_state_change(self: &Rc<Self>, old_id: KeyboardStateId, new: &KeyboardState) {
if self.keyboard.is_none() {
return;
}
if self.kb_state_id.get() != old_id {
return;
}
self.kb_state_id.set(new.id);
if let Err(e) = self.recreate_all(false, new) {
self.client.error(e);
}
}
pub fn handle_modifiers_changed(self: &Rc<Self>, state: &KeyboardState) {
let old_id = self.kb_state_id.get();
if old_id != state.id {
if self.is_sender() {
return;
}
self.handle_xkb_state_change(old_id, state);
return;
}
if let Some(kb) = self.keyboard.get() {
kb.send_modifiers(state);
}
}
pub fn handle_key(
self: &Rc<Self>,
time_usec: u64,
key: u32,
state: u32,
kb_state: &KeyboardState,
) {
if self.is_sender() {
return;
}
let old_id = self.kb_state_id.get();
if old_id != kb_state.id {
self.handle_xkb_state_change(old_id, kb_state);
}
if let Some(kb) = self.keyboard.get() {
kb.send_key(key, state);
kb.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn handle_motion_abs(&self, time_usec: u64, x: Fixed, y: Fixed) {
if self.is_sender() {
return;
}
if let Some(v) = self.pointer_absolute.get() {
v.send_motion_absolute(x, y);
v.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn handle_motion(&self, time_usec: u64, dx: Fixed, dy: Fixed) {
if self.is_sender() {
return;
}
if let Some(v) = self.pointer.get() {
v.send_motion(dx, dy);
v.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn handle_button(&self, time_usec: u64, button: u32, state: KeyState) {
if self.is_sender() {
return;
}
if let Some(b) = self.button.get() {
b.send_button(button, state);
b.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn handle_pending_scroll(&self, time_usec: u64, ps: &PendingScroll) {
if self.is_sender() {
return;
}
if let Some(b) = self.scroll.get() {
b.send_scroll(
ps.px[0].get().unwrap_or_default(),
ps.px[1].get().unwrap_or_default(),
);
b.send_scroll_discrete(
ps.v120[0].get().unwrap_or_default(),
ps.v120[1].get().unwrap_or_default(),
);
b.send_scroll_stop(ps.stop[0].get(), ps.stop[1].get());
b.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn handle_touch_down(&self, id: u32, x: Fixed, y: Fixed) {
if self.is_sender() {
return;
}
if let Some(b) = self.touchscreen.get() {
b.send_down(id, x, y);
}
}
pub fn handle_touch_motion(&self, id: u32, x: Fixed, y: Fixed) {
if self.is_sender() {
return;
}
if let Some(b) = self.touchscreen.get() {
b.send_motion(id, x, y);
}
}
pub fn handle_touch_up(&self, id: u32) {
if self.is_sender() {
return;
}
if let Some(b) = self.touchscreen.get() {
b.send_up(id);
}
}
pub fn handle_touch_frame(&self, time_usec: u64) {
if self.is_sender() {
return;
}
if let Some(b) = self.touchscreen.get() {
b.device.send_frame(self.client.serial(), time_usec);
}
}
pub fn send_capability(&self, interface: EiInterface, mask: u64) {
self.client.event(Capability {
self_id: self.id,
mask,
interface: interface.0,
});
}
pub fn send_done(&self) {
self.client.event(Done { self_id: self.id });
}
pub fn send_name(&self, name: &str) {
self.client.event(Name {
self_id: self.id,
name,
});
}
pub fn send_device(&self, device: &EiDevice) {
self.client.event(Device {
self_id: self.id,
device: device.id,
version: device.version.0,
});
}
pub fn send_destroyed(&self) {
self.client.event(Destroyed {
self_id: self.id,
serial: self.client.serial(),
});
}
fn create_interface<T>(self: &Rc<Self>, field: &CloneCell<Option<Rc<T>>>, version: EiVersion)
where
T: EiDeviceInterface,
{
if version == EiVersion(0) {
return;
}
let Some(device) = self.device.get() else {
return;
};
let interface = T::new(&device, version);
self.client.add_server_obj(&interface);
device.send_interface(&*interface);
field.set(Some(interface.clone()));
}
fn create_pointer(self: &Rc<Self>) {
self.create_interface(&self.pointer, self.client.versions.ei_pointer());
}
fn create_button(self: &Rc<Self>) {
self.create_interface(&self.button, self.client.versions.ei_button());
}
fn create_keyboard(self: &Rc<Self>) {
self.create_interface(&self.keyboard, self.client.versions.ei_keyboard());
}
fn create_scroll(self: &Rc<Self>) {
self.create_interface(&self.scroll, self.client.versions.ei_scroll());
}
fn create_pointer_absolute(self: &Rc<Self>) {
self.create_interface(
&self.pointer_absolute,
self.client.versions.ei_pointer_absolute(),
);
}
fn create_touchscreen(self: &Rc<Self>) {
self.create_interface(&self.touchscreen, self.client.versions.ei_touchscreen());
}
fn get_kb_state(&self) -> Rc<dyn DynKeyboardState> {
match self.context() {
EiContext::Sender => self.seat.seat_xkb_state(),
EiContext::Receiver => self.seat.latest_xkb_state(),
}
}
fn recreate_all(
self: &Rc<Self>,
create_all: bool,
kb_state: &KeyboardState,
) -> Result<(), EiClientError> {
self.kb_state_id.set(kb_state.id);
let have_outputs = self.client.state.root.outputs.is_not_empty();
let create_pointer = create_all || self.pointer.is_some();
let create_pointer_absolute =
have_outputs && (create_all || self.pointer_absolute.is_some());
let create_scroll = create_all || self.scroll.is_some();
let create_button = create_all || self.button.is_some();
let create_keyboard = create_all || self.keyboard.is_some();
let create_touchscreen = have_outputs && (create_all || self.touchscreen.is_some());
if let Some(device) = self.device.take() {
device.destroy()?;
}
let version = self.client.versions.ei_device();
if version == EiVersion(0) {
return Ok(());
}
let device = Rc::new(EiDevice {
id: self.client.new_id(),
client: self.client.clone(),
tracker: Default::default(),
version,
seat: self.clone(),
button_changes: Default::default(),
touch_changes: Default::default(),
scroll_px: array::from_fn(|_| Default::default()),
scroll_v120: array::from_fn(|_| Default::default()),
scroll_stop: array::from_fn(|_| Default::default()),
absolute_motion: Default::default(),
relative_motion: Default::default(),
key_changes: Default::default(),
});
track!(self.client, device);
self.device.set(Some(device.clone()));
self.client.add_server_obj(&device);
self.send_device(&device);
device.send_device_type(EI_DEVICE_TYPE_VIRTUAL);
let caps = self.capabilities.get();
macro_rules! apply {
($cap:expr, $create:ident) => {
if $create && caps.contains($cap) {
self.$create();
}
};
}
apply!(EI_CAP_POINTER, create_pointer);
apply!(EI_CAP_POINTER_ABSOLUTE, create_pointer_absolute);
apply!(EI_CAP_SCROLL, create_scroll);
apply!(EI_CAP_BUTTON, create_button);
apply!(EI_CAP_KEYBOARD, create_keyboard);
apply!(EI_CAP_TOUCHSCREEN, create_touchscreen);
for output in self.client.state.root.outputs.lock().values() {
device.send_region(
output.node_absolute_position(),
output.global.persistent.scale.get(),
);
}
if let Some(kb) = self.keyboard.get() {
kb.send_keymap(kb_state);
}
device.send_done();
device.send_resumed(self.client.serial());
if self.context() == EiContext::Receiver {
device.send_start_emulating(self.client.serial(), 1);
}
if let Some(kb) = self.keyboard.get() {
kb.send_modifiers(kb_state);
}
Ok(())
}
pub fn is_touch_input(&self) -> bool {
self.capabilities.get().contains(EI_CAP_TOUCHSCREEN) && self.context() == EiContext::Sender
}
}
impl EiSeatRequestHandler for EiSeat {
type Error = EiSeatError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.seat.remove_ei_seat(self);
self.send_destroyed();
if let Some(device) = self.device.take() {
device.destroy()?;
}
self.client.remove_obj(self)?;
Ok(())
}
fn bind(&self, req: Bind, slf: &Rc<Self>) -> Result<(), Self::Error> {
let caps = req.capabilities;
let unknown = caps & !EI_CAP_ALL;
if unknown != 0 {
return Err(EiSeatError::UnknownCapabilities(unknown));
}
self.capabilities.set(caps);
let kb_state = self.get_kb_state();
slf.recreate_all(true, &kb_state.borrow())?;
self.seat.update_capabilities();
Ok(())
}
}
ei_object_base! {
self = EiSeat;
version = self.version;
}
impl EiObject for EiSeat {
fn break_loops(&self) {
self.seat.remove_ei_seat(self);
self.device.take();
self.pointer.take();
self.pointer_absolute.take();
self.keyboard.take();
self.button.take();
self.scroll.take();
self.touchscreen.take();
}
}
#[derive(Debug, Error)]
pub enum EiSeatError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
#[error("Capabilities {0} are unknown")]
UnknownCapabilities(u64),
}
efrom!(EiSeatError, EiClientError);