1
0
Fork 0
forked from wry/wry

ei: add support for libei

This commit is contained in:
Julian Orth 2024-07-24 01:38:05 +02:00
parent 084fe50259
commit 40e87f8f91
69 changed files with 4340 additions and 72 deletions

View file

@ -0,0 +1,72 @@
use {
crate::{
backend::KeyState,
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
leaks::Tracker,
wire_ei::{
ei_button::{ClientButton, EiButtonRequestHandler, Release, ServerButton},
EiButtonId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiButton {
pub id: EiButtonId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
ei_device_interface!(EiButton, ei_button, button);
impl EiButton {
pub fn send_button(&self, button: u32, state: KeyState) {
self.client.event(ServerButton {
self_id: self.id,
button,
state: state as _,
});
}
}
impl EiButtonRequestHandler for EiButton {
type Error = EiButtonError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_button(&self, req: ClientButton, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let pressed = match req.state {
0 => KeyState::Released,
1 => KeyState::Pressed,
_ => return Err(EiButtonError::InvalidButtonState(req.state)),
};
self.device.button_changes.push((req.button, pressed));
Ok(())
}
}
ei_object_base! {
self = EiButton;
version = self.version;
}
impl EiObject for EiButton {}
#[derive(Debug, Error)]
pub enum EiButtonError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
#[error("Invalid button state {0}")]
InvalidButtonState(u32),
}
efrom!(EiButtonError, EiClientError);

View file

@ -0,0 +1,49 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_object::{EiObject, EiVersion},
},
leaks::Tracker,
wire_ei::{
ei_callback::{Done, EiCallbackRequestHandler},
EiCallbackId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiCallback {
pub id: EiCallbackId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
}
impl EiCallback {
pub fn send_done(&self, callback_data: u64) {
self.client.event(Done {
self_id: self.id,
callback_data,
});
}
}
impl EiCallbackRequestHandler for EiCallback {
type Error = EiCallbackError;
}
ei_object_base! {
self = EiCallback;
version = self.version;
}
impl EiObject for EiCallback {}
#[derive(Debug, Error)]
pub enum EiCallbackError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiCallbackError, EiClientError);

View file

@ -0,0 +1,160 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::{
ei_callback::EiCallback,
ei_seat::{
EiSeat, EI_CAP_BUTTON, EI_CAP_KEYBOARD, EI_CAP_POINTER,
EI_CAP_POINTER_ABSOLUTE, EI_CAP_SCROLL, EI_CAP_TOUCHSCREEN,
},
},
ei_object::{EiObject, EiObjectId, EiVersion},
EiContext,
},
ifs::wl_seat::WlSeatGlobal,
leaks::Tracker,
wire_ei::{
ei_connection::{
Disconnect, Disconnected, EiConnectionRequestHandler, InvalidObject, Seat,
},
EiButton, EiConnectionId, EiKeyboard, EiPointer, EiPointerAbsolute, EiScroll,
EiTouchscreen,
},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};
pub struct EiConnection {
pub id: EiConnectionId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
}
impl EiConnection {
pub fn send_invalid_object(&self, id: EiObjectId) {
self.client.event(InvalidObject {
self_id: self.id,
last_serial: self.client.last_serial(),
invalid_id: id,
});
}
pub fn send_disconnected(&self, error: Option<&str>) {
self.client.event(Disconnected {
self_id: self.id,
last_serial: self.client.last_serial(),
reason: error.is_some() as _,
explanation: error,
});
}
pub fn send_seat(&self, seat: &EiSeat) {
self.client.event(Seat {
self_id: self.id,
seat: seat.id,
version: seat.version.0,
});
}
pub fn announce_seat(&self, seat: &Rc<WlSeatGlobal>) {
let version = self.client.versions.ei_seat.version.get();
if version == EiVersion(0) {
return;
}
let xkb_state_id = match self.context() {
EiContext::Sender => seat.seat_xkb_state().borrow().id,
EiContext::Receiver => seat.latest_xkb_state().borrow().id,
};
let seat = Rc::new(EiSeat {
id: self.client.new_id(),
client: self.client.clone(),
tracker: Default::default(),
version,
seat: seat.clone(),
capabilities: Cell::new(0),
kb_state_id: Cell::new(xkb_state_id),
device: Default::default(),
pointer: Default::default(),
pointer_absolute: Default::default(),
keyboard: Default::default(),
button: Default::default(),
scroll: Default::default(),
touchscreen: Default::default(),
});
track!(self.client, seat);
self.client.add_server_obj(&seat);
self.send_seat(&seat);
let v = &self.client.versions;
let caps = [
(EI_CAP_POINTER, EiPointer, &v.ei_pointer),
(
EI_CAP_POINTER_ABSOLUTE,
EiPointerAbsolute,
&v.ei_pointer_absolute,
),
(EI_CAP_SCROLL, EiScroll, &v.ei_scroll),
(EI_CAP_BUTTON, EiButton, &v.ei_button),
(EI_CAP_KEYBOARD, EiKeyboard, &v.ei_keyboard),
(EI_CAP_TOUCHSCREEN, EiTouchscreen, &v.ei_touchscreen),
];
for (mask, interface, version) in caps {
if version.version.get() > EiVersion(0) {
seat.send_capability(interface, mask);
}
}
seat.send_name(&seat.seat.seat_name());
seat.send_done();
seat.seat.add_ei_seat(&seat);
}
}
impl EiConnectionRequestHandler for EiConnection {
type Error = EiConnectionError;
fn sync(
&self,
req: crate::wire_ei::ei_connection::Sync,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let version = EiVersion(req.version);
if version > self.client.versions.ei_callback.version.get() {
return Err(EiConnectionError::CallbackVersion(req.version));
}
let cb = Rc::new(EiCallback {
id: req.callback,
client: self.client.clone(),
tracker: Default::default(),
version,
});
track!(self.client, cb);
self.client.add_client_obj(&cb)?;
cb.send_done(0);
self.client.remove_obj(&*cb)?;
Ok(())
}
fn disconnect(&self, _req: Disconnect, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.client.disconnect_announced.set(true);
self.client.state.ei_clients.shutdown(self.client.id);
Ok(())
}
}
ei_object_base! {
self = EiConnection;
version = self.version;
}
impl EiObject for EiConnection {}
#[derive(Debug, Error)]
pub enum EiConnectionError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
#[error("The callback version is too large: {0}")]
CallbackVersion(u32),
}
efrom!(EiConnectionError, EiClientError);

249
src/ei/ei_ifs/ei_device.rs Normal file
View file

@ -0,0 +1,249 @@
use {
crate::{
backend::{KeyState, ScrollAxis},
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::{ei_seat::EiSeat, ei_touchscreen::TouchChange},
ei_object::{EiObject, EiVersion},
},
fixed::Fixed,
ifs::wl_seat::PX_PER_SCROLL,
leaks::Tracker,
rect::Rect,
scale::Scale,
utils::syncqueue::SyncQueue,
wire_ei::{
ei_device::{
ClientFrame, ClientStartEmulating, ClientStopEmulating, Destroyed, DeviceType,
Done, EiDeviceRequestHandler, Interface, Paused, Region, Release, Resumed,
ServerFrame, ServerStartEmulating,
},
EiDeviceId,
},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};
pub const EI_DEVICE_TYPE_VIRTUAL: u32 = 1;
#[allow(dead_code)]
pub const EI_DEVICE_TYPE_PHYSICAL: u32 = 2;
pub struct EiDevice {
pub id: EiDeviceId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub seat: Rc<EiSeat>,
pub button_changes: SyncQueue<(u32, KeyState)>,
pub touch_changes: SyncQueue<(u32, TouchChange)>,
pub scroll_px: [Cell<Option<f32>>; 2],
pub scroll_v120: [Cell<Option<i32>>; 2],
pub scroll_stop: [Cell<Option<bool>>; 2],
pub absolute_motion: Cell<Option<(f32, f32)>>,
pub relative_motion: Cell<Option<(f32, f32)>>,
pub key_changes: SyncQueue<(u32, KeyState)>,
}
pub trait EiDeviceInterface: EiObject {
fn new(device: &Rc<EiDevice>, version: EiVersion) -> Rc<Self>;
fn destroy(&self) -> Result<(), EiClientError>;
fn send_destroyed(&self, serial: u32);
}
impl EiDevice {
pub fn send_interface<T>(&self, interface: &T)
where
T: EiDeviceInterface,
{
self.client.event(Interface {
self_id: self.id,
object: interface.id(),
interface_name: interface.interface().name(),
version: interface.version().0,
});
}
pub fn send_device_type(&self, ty: u32) {
self.client.event(DeviceType {
self_id: self.id,
device_type: ty,
});
}
pub fn send_resumed(&self, serial: u32) {
self.client.event(Resumed {
self_id: self.id,
serial,
});
}
pub fn send_start_emulating(&self, serial: u32, sequence: u32) {
self.client.event(ServerStartEmulating {
self_id: self.id,
serial,
sequence,
});
}
pub fn send_region(&self, rect: Rect, scale: Scale) {
self.client.event(Region {
self_id: self.id,
offset_x: rect.x1() as u32,
offset_y: rect.y1() as u32,
width: rect.width() as u32,
hight: rect.height() as u32,
scale: scale.to_f64() as f32,
});
}
#[allow(dead_code)]
pub fn send_paused(&self, serial: u32) {
self.client.event(Paused {
self_id: self.id,
serial,
});
}
pub fn send_done(&self) {
self.client.event(Done { self_id: self.id });
}
pub fn send_frame(&self, serial: u32, timestamp: u64) {
self.client.event(ServerFrame {
self_id: self.id,
serial,
timestamp,
});
}
pub fn send_destroyed(&self, serial: u32) {
self.client.event(Destroyed {
self_id: self.id,
serial,
});
}
pub fn destroy(&self) -> Result<(), EiClientError> {
macro_rules! destroy_interface {
($name:ident) => {
if let Some(interface) = self.seat.$name.take() {
interface.destroy()?;
}
};
}
destroy_interface!(pointer);
destroy_interface!(pointer_absolute);
destroy_interface!(scroll);
destroy_interface!(button);
destroy_interface!(keyboard);
destroy_interface!(touchscreen);
self.send_destroyed(self.client.serial());
self.client.remove_obj(self)?;
Ok(())
}
}
impl EiDeviceRequestHandler for EiDevice {
type Error = EiDeviceError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_start_emulating(
&self,
_req: ClientStartEmulating,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Ok(())
}
fn client_stop_emulating(
&self,
_req: ClientStopEmulating,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
Ok(())
}
fn client_frame(&self, req: ClientFrame, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let seat = &self.seat.seat;
let time = req.timestamp;
while let Some((button, pressed)) = self.button_changes.pop() {
seat.button_event(time, button, pressed);
}
while let Some((button, pressed)) = self.key_changes.pop() {
seat.key_event_with_seat_state(time, button, pressed);
}
if let Some((x, y)) = self.relative_motion.take() {
let x = Fixed::from_f32(x);
let y = Fixed::from_f32(y);
seat.motion_event(time, x, y, x, y);
}
if let Some((x, y)) = self.absolute_motion.take() {
let x = Fixed::from_f32(x);
let y = Fixed::from_f32(y);
seat.motion_event_abs(time, x, y);
}
{
let mut need_frame = false;
for axis in [ScrollAxis::Horizontal, ScrollAxis::Vertical] {
let idx = axis as usize;
if let Some(v120) = self.scroll_v120[idx].take() {
need_frame = true;
seat.axis_120(v120, axis, false);
}
if let Some(px) = self.scroll_px[idx].take() {
need_frame = true;
seat.axis_px(Fixed::from_f32(px), axis, false);
}
if self.scroll_stop[idx].take() == Some(true) {
need_frame = true;
seat.axis_stop(axis);
}
}
if need_frame {
seat.axis_frame(PX_PER_SCROLL, time);
}
}
if self.touch_changes.is_not_empty() {
while let Some((touch_id, change)) = self.touch_changes.pop() {
let id = touch_id as i32;
match change {
TouchChange::Down(x, y) => {
let x = Fixed::from_f32(x);
let y = Fixed::from_f32(y);
seat.touch_down_at(time, id, x, y);
}
TouchChange::Motion(x, y) => {
let x = Fixed::from_f32(x);
let y = Fixed::from_f32(y);
seat.touch_motion_at(time, id, x, y);
}
TouchChange::Up => seat.touch_up(time, id),
}
}
seat.touch_frame(time);
}
Ok(())
}
}
ei_object_base! {
self = EiDevice;
version = self.version;
}
impl EiObject for EiDevice {}
#[derive(Debug, Error)]
pub enum EiDeviceError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiDeviceError, EiClientError);

View file

@ -0,0 +1,189 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_connection::EiConnection,
ei_object::{EiInterface, EiObject, EiVersion, EI_HANDSHAKE_ID},
EiContext,
},
leaks::Tracker,
wire_ei::{
ei_handshake::{
ClientHandshakeVersion, ClientInterfaceVersion, Connection, ContextType,
EiHandshakeRequestHandler, Finish, Name, ServerHandshakeVersion,
ServerInterfaceVersion,
},
EiHandshake, EiHandshakeId,
},
},
std::{cell::Cell, rc::Rc},
thiserror::Error,
};
pub struct EiHandshake {
pub id: EiHandshakeId,
client: Rc<EiClient>,
version: Cell<EiVersion>,
pub tracker: Tracker<Self>,
have_context_type: Cell<bool>,
}
impl EiHandshake {
pub fn new(client: &Rc<EiClient>) -> Self {
Self {
id: EI_HANDSHAKE_ID,
client: client.clone(),
version: Cell::new(EiVersion(1)),
tracker: Default::default(),
have_context_type: Cell::new(false),
}
}
pub fn send_handshake_version(&self) {
self.client.event(ServerHandshakeVersion {
self_id: self.id,
version: 1,
});
}
fn send_interface_version(&self, interface: EiInterface, version: EiVersion) {
self.client.event(ServerInterfaceVersion {
self_id: self.id,
name: interface.0,
version: version.0,
});
}
fn send_connection(&self, serial: u32, connection: &EiConnection) {
self.client.event(Connection {
self_id: self.id,
serial,
connection: connection.id,
version: connection.version.0,
});
}
}
impl EiHandshakeRequestHandler for EiHandshake {
type Error = EiHandshakeError;
fn client_handshake_version(
&self,
req: ClientHandshakeVersion,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
let version = EiVersion(req.version);
if version > self.client.versions.ei_handshake.server_max_version {
return Err(EiHandshakeError::UnknownHandshakeVersion);
}
self.client
.versions
.ei_handshake
.set_client_version(version);
Ok(())
}
fn finish(&self, _req: Finish, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if !self.have_context_type.get() {
return Err(EiHandshakeError::NoContextType);
}
if self.client.name.borrow().is_none() {
return Err(EiHandshakeError::NoName);
}
if self.client.versions.ei_connection.version.get() == EiVersion(0) {
return Err(EiHandshakeError::NoConnectionVersion);
}
if self.client.versions.ei_callback.version.get() == EiVersion(0) {
return Err(EiHandshakeError::NoCallbackVersion);
}
self.client.versions.for_each(|interface, version| {
let version = version.version.get();
if version > EiVersion(0) && interface != EiHandshake {
self.send_interface_version(interface, version);
}
});
let connection = Rc::new(EiConnection {
id: self.client.new_id(),
client: self.client.clone(),
tracker: Default::default(),
version: self.client.versions.ei_connection.version.get(),
});
self.client.add_server_obj(&connection);
track!(self.client, connection);
self.client.connection.set(Some(connection.clone()));
self.send_connection(self.client.serial(), &connection);
self.client.remove_obj(self)?;
for seat in self.client.state.globals.seats.lock().values() {
connection.announce_seat(seat);
}
Ok(())
}
fn context_type(&self, req: ContextType, _slf: &Rc<Self>) -> Result<(), Self::Error> {
if self.have_context_type.replace(true) {
return Err(EiHandshakeError::ContextTypeSet);
}
let ty = match req.context_type {
1 => EiContext::Receiver,
2 => EiContext::Sender,
_ => return Err(EiHandshakeError::UnknownContextType(req.context_type)),
};
self.client.context.set(ty);
self.have_context_type.set(true);
Ok(())
}
fn name(&self, req: Name<'_>, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let name = &mut *self.client.name.borrow_mut();
if name.is_some() {
return Err(EiHandshakeError::NameSet);
}
*name = Some(req.name.to_string());
Ok(())
}
fn client_interface_version(
&self,
req: ClientInterfaceVersion<'_>,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.client.versions.match_(req.name, |v| {
v.set_client_version(EiVersion(req.version));
});
Ok(())
}
}
ei_object_base! {
self = EiHandshake;
version = self.version.get();
}
impl EiObject for EiHandshake {
fn context(&self) -> EiContext {
panic!("context requested for EiHandshake")
}
}
#[derive(Debug, Error)]
pub enum EiHandshakeError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
#[error("ei_handshake version is too large")]
UnknownHandshakeVersion,
#[error("Name is already set")]
NameSet,
#[error("Unknown context type {0}")]
UnknownContextType(u32),
#[error("Context type is already set")]
ContextTypeSet,
#[error("Client did not set connection version")]
NoConnectionVersion,
#[error("Client did not set callback version")]
NoCallbackVersion,
#[error("Client did not set context type")]
NoContextType,
#[error("Client did not set name")]
NoName,
}
efrom!(EiHandshakeError, EiClientError);

View file

@ -0,0 +1,97 @@
use {
crate::{
backend::KeyState,
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
leaks::Tracker,
wire_ei::{
ei_keyboard::{
ClientKey, EiKeyboardRequestHandler, Keymap, Modifiers, Release, ServerKey,
},
EiKeyboardId,
},
xkbcommon::KeyboardState,
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiKeyboard {
pub id: EiKeyboardId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
ei_device_interface!(EiKeyboard, ei_keyboard, keyboard);
const KEYMAP_TYPE_XKB: u32 = 1;
impl EiKeyboard {
pub fn send_keymap(&self, state: &KeyboardState) {
self.client.event(Keymap {
self_id: self.id,
keymap_type: KEYMAP_TYPE_XKB,
size: state.map_len as _,
keymap: state.map.clone(),
});
}
pub fn send_modifiers(&self, state: &KeyboardState) {
self.client.event(Modifiers {
self_id: self.id,
serial: self.client.serial(),
depressed: state.mods.mods_depressed,
locked: state.mods.mods_locked,
latched: state.mods.mods_latched,
group: state.mods.group,
});
}
pub fn send_key(&self, key: u32, state: u32) {
self.client.event(ServerKey {
self_id: self.id,
key,
state,
});
}
}
impl EiKeyboardRequestHandler for EiKeyboard {
type Error = EiKeyboardError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_key(&self, req: ClientKey, _slf: &Rc<Self>) -> Result<(), Self::Error> {
let pressed = match req.state {
0 => KeyState::Released,
1 => KeyState::Pressed,
_ => return Err(EiKeyboardError::InvalidKeyState(req.state)),
};
self.device.key_changes.push((req.key, pressed));
Ok(())
}
}
ei_object_base! {
self = EiKeyboard;
version = self.version;
}
impl EiObject for EiKeyboard {}
#[derive(Debug, Error)]
pub enum EiKeyboardError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
#[error("Invalid key state {0}")]
InvalidKeyState(u32),
}
efrom!(EiKeyboardError, EiClientError);

View file

@ -0,0 +1,45 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_object::{EiObject, EiVersion},
},
leaks::Tracker,
wire_ei::{
ei_pingpong::{Done, EiPingpongRequestHandler},
EiPingpongId,
},
},
std::rc::Rc,
thiserror::Error,
};
#[allow(dead_code)]
pub struct EiPingpong {
pub id: EiPingpongId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
}
impl EiPingpongRequestHandler for EiPingpong {
type Error = EiPingpongError;
fn done(&self, _req: Done, _slf: &Rc<Self>) -> Result<(), Self::Error> {
Ok(())
}
}
ei_object_base! {
self = EiPingpong;
version = self.version;
}
impl EiObject for EiPingpong {}
#[derive(Debug, Error)]
pub enum EiPingpongError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiPingpongError, EiClientError);

View file

@ -0,0 +1,71 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
fixed::Fixed,
leaks::Tracker,
wire_ei::{
ei_pointer::{
ClientMotionRelative, EiPointerRequestHandler, Release, ServerMotionRelative,
},
EiPointerId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiPointer {
pub id: EiPointerId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
ei_device_interface!(EiPointer, ei_pointer, pointer);
impl EiPointer {
pub fn send_motion(&self, dx: Fixed, dy: Fixed) {
self.client.event(ServerMotionRelative {
self_id: self.id,
x: dx.to_f32(),
y: dy.to_f32(),
});
}
}
impl EiPointerRequestHandler for EiPointer {
type Error = EiPointerError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_motion_relative(
&self,
req: ClientMotionRelative,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.device.relative_motion.set(Some((req.x, req.y)));
Ok(())
}
}
ei_object_base! {
self = EiPointer;
version = self.version;
}
impl EiObject for EiPointer {}
#[derive(Debug, Error)]
pub enum EiPointerError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiPointerError, EiClientError);

View file

@ -0,0 +1,72 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
fixed::Fixed,
leaks::Tracker,
wire_ei::{
ei_pointer_absolute::{
ClientMotionAbsolute, EiPointerAbsoluteRequestHandler, Release,
ServerMotionAbsolute,
},
EiPointerAbsoluteId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiPointerAbsolute {
pub id: EiPointerAbsoluteId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
ei_device_interface!(EiPointerAbsolute, ei_pointer_absolute, pointer_absolute);
impl EiPointerAbsolute {
pub fn send_motion_absolute(&self, x: Fixed, y: Fixed) {
self.client.event(ServerMotionAbsolute {
self_id: self.id,
x: x.to_f32(),
y: y.to_f32(),
});
}
}
impl EiPointerAbsoluteRequestHandler for EiPointerAbsolute {
type Error = EiCallbackError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_motion_absolute(
&self,
req: ClientMotionAbsolute,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.device.absolute_motion.set(Some((req.x, req.y)));
Ok(())
}
}
ei_object_base! {
self = EiPointerAbsolute;
version = self.version;
}
impl EiObject for EiPointerAbsolute {}
#[derive(Debug, Error)]
pub enum EiCallbackError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiCallbackError, EiClientError);

106
src/ei/ei_ifs/ei_scroll.rs Normal file
View file

@ -0,0 +1,106 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
fixed::Fixed,
leaks::Tracker,
wire_ei::{
ei_scroll::{
ClientScroll, ClientScrollDiscrete, ClientScrollStop, EiScrollRequestHandler,
Release, ServerScroll, ServerScrollDiscrete, ServerScrollStop,
},
EiScrollId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiScroll {
pub id: EiScrollId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
ei_device_interface!(EiScroll, ei_scroll, scroll);
impl EiScroll {
pub fn send_scroll(&self, x: Fixed, y: Fixed) {
self.client.event(ServerScroll {
self_id: self.id,
x: x.to_f32(),
y: y.to_f32(),
});
}
pub fn send_scroll_discrete(&self, x: i32, y: i32) {
self.client.event(ServerScrollDiscrete {
self_id: self.id,
x,
y,
});
}
pub fn send_scroll_stop(&self, x: bool, y: bool) {
self.client.event(ServerScrollStop {
self_id: self.id,
x: x as _,
y: y as _,
is_cancel: 0,
});
}
}
impl EiScrollRequestHandler for EiScroll {
type Error = EiScrollError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_scroll(&self, req: ClientScroll, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.device.scroll_px[0].set(Some(req.x));
self.device.scroll_px[1].set(Some(req.y));
Ok(())
}
fn client_scroll_discrete(
&self,
req: ClientScrollDiscrete,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.device.scroll_v120[0].set(Some(req.x));
self.device.scroll_v120[1].set(Some(req.y));
Ok(())
}
fn client_scroll_stop(
&self,
req: ClientScrollStop,
_slf: &Rc<Self>,
) -> Result<(), Self::Error> {
self.device.scroll_stop[0].set(Some(req.x != 0));
self.device.scroll_stop[1].set(Some(req.y != 0));
Ok(())
}
}
ei_object_base! {
self = EiScroll;
version = self.version;
}
impl EiObject for EiScroll {}
#[derive(Debug, Error)]
pub enum EiScrollError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiScrollError, EiClientError);

425
src/ei/ei_ifs/ei_seat.rs Normal file
View file

@ -0,0 +1,425 @@
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);

View file

@ -0,0 +1,108 @@
use {
crate::{
ei::{
ei_client::{EiClient, EiClientError},
ei_ifs::ei_device::{EiDevice, EiDeviceInterface},
ei_object::{EiObject, EiVersion},
},
fixed::Fixed,
leaks::Tracker,
wire_ei::{
ei_touchscreen::{
ClientDown, ClientMotion, ClientUp, EiTouchscreenRequestHandler, Release,
ServerDown, ServerMotion, ServerUp,
},
EiTouchscreenId,
},
},
std::rc::Rc,
thiserror::Error,
};
pub struct EiTouchscreen {
pub id: EiTouchscreenId,
pub client: Rc<EiClient>,
pub tracker: Tracker<Self>,
pub version: EiVersion,
pub device: Rc<EiDevice>,
}
#[derive(Copy, Clone, Debug)]
pub enum TouchChange {
Down(f32, f32),
Motion(f32, f32),
Up,
}
ei_device_interface!(EiTouchscreen, ei_touchscreen, touchscreen);
impl EiTouchscreen {
pub fn send_down(&self, touchid: u32, x: Fixed, y: Fixed) {
self.client.event(ServerDown {
self_id: self.id,
touchid,
x: x.to_f32(),
y: y.to_f32(),
});
}
pub fn send_motion(&self, touchid: u32, x: Fixed, y: Fixed) {
self.client.event(ServerMotion {
self_id: self.id,
touchid,
x: x.to_f32(),
y: y.to_f32(),
});
}
pub fn send_up(&self, touchid: u32) {
self.client.event(ServerUp {
self_id: self.id,
touchid,
});
}
}
impl EiTouchscreenRequestHandler for EiTouchscreen {
type Error = EiTouchscreenError;
fn release(&self, _req: Release, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.destroy()?;
Ok(())
}
fn client_down(&self, req: ClientDown, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.device
.touch_changes
.push((req.touchid, TouchChange::Down(req.x, req.y)));
Ok(())
}
fn client_motion(&self, req: ClientMotion, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.device
.touch_changes
.push((req.touchid, TouchChange::Motion(req.x, req.y)));
Ok(())
}
fn client_up(&self, req: ClientUp, _slf: &Rc<Self>) -> Result<(), Self::Error> {
self.device
.touch_changes
.push((req.touchid, TouchChange::Up));
Ok(())
}
}
ei_object_base! {
self = EiTouchscreen;
version = self.version;
}
impl EiObject for EiTouchscreen {}
#[derive(Debug, Error)]
pub enum EiTouchscreenError {
#[error(transparent)]
EiClientError(Box<EiClientError>),
}
efrom!(EiTouchscreenError, EiClientError);