ei: add support for libei
This commit is contained in:
parent
084fe50259
commit
40e87f8f91
69 changed files with 4340 additions and 72 deletions
156
src/ei/ei_acceptor.rs
Normal file
156
src/ei/ei_acceptor.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
state::State,
|
||||
utils::{errorfmt::ErrorFmt, oserror::OsError, xrd::xrd},
|
||||
},
|
||||
std::rc::Rc,
|
||||
thiserror::Error,
|
||||
uapi::{c, format_ustr, Errno, OwnedFd, Ustring},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EiAcceptorError {
|
||||
#[error("XDG_RUNTIME_DIR is not set")]
|
||||
XrdNotSet,
|
||||
#[error("XDG_RUNTIME_DIR ({0:?}) is too long to form a unix socket address")]
|
||||
XrdTooLong(String),
|
||||
#[error("Could not create a libei socket")]
|
||||
SocketFailed(#[source] OsError),
|
||||
#[error("Could not stat the existing socket")]
|
||||
SocketStat(#[source] OsError),
|
||||
#[error("Could not start listening for incoming connections")]
|
||||
ListenFailed(#[source] OsError),
|
||||
#[error("Could not open the lock file")]
|
||||
OpenLockFile(#[source] OsError),
|
||||
#[error("Could not lock the lock file")]
|
||||
LockLockFile(#[source] OsError),
|
||||
#[error("Could not bind the socket to an address")]
|
||||
BindFailed(#[source] OsError),
|
||||
#[error("All libei addresses in the range 0..1000 are already in use")]
|
||||
AddressesInUse,
|
||||
}
|
||||
|
||||
pub struct EiAcceptor {
|
||||
socket: EiAllocatedSocket,
|
||||
}
|
||||
|
||||
struct EiAllocatedSocket {
|
||||
// eis-x
|
||||
name: String,
|
||||
// /run/user/1000/eis-x
|
||||
path: Ustring,
|
||||
insecure: Rc<OwnedFd>,
|
||||
// /run/user/1000/eis-x.lock
|
||||
lock_path: Ustring,
|
||||
_lock_fd: OwnedFd,
|
||||
}
|
||||
|
||||
impl Drop for EiAllocatedSocket {
|
||||
fn drop(&mut self) {
|
||||
let _ = uapi::unlink(&self.path);
|
||||
let _ = uapi::unlink(&self.lock_path);
|
||||
}
|
||||
}
|
||||
|
||||
fn bind_socket(
|
||||
insecure: &Rc<OwnedFd>,
|
||||
xrd: &str,
|
||||
id: u32,
|
||||
) -> Result<EiAllocatedSocket, EiAcceptorError> {
|
||||
let mut addr: c::sockaddr_un = uapi::pod_zeroed();
|
||||
addr.sun_family = c::AF_UNIX as _;
|
||||
let name = format!("eis-{}", id);
|
||||
let path = format_ustr!("{}/{}", xrd, name);
|
||||
let lock_path = format_ustr!("{}.lock", path.display());
|
||||
if path.len() + 1 > addr.sun_path.len() {
|
||||
return Err(EiAcceptorError::XrdTooLong(xrd.to_string()));
|
||||
}
|
||||
let lock_fd = match uapi::open(&*lock_path, c::O_CREAT | c::O_CLOEXEC | c::O_RDWR, 0o644) {
|
||||
Ok(l) => l,
|
||||
Err(e) => return Err(EiAcceptorError::OpenLockFile(e.into())),
|
||||
};
|
||||
if let Err(e) = uapi::flock(lock_fd.raw(), c::LOCK_EX | c::LOCK_NB) {
|
||||
return Err(EiAcceptorError::LockLockFile(e.into()));
|
||||
}
|
||||
match uapi::lstat(&path) {
|
||||
Ok(_) => {
|
||||
log::info!("Unlinking {}", path.display());
|
||||
let _ = uapi::unlink(&path);
|
||||
}
|
||||
Err(Errno(c::ENOENT)) => {}
|
||||
Err(e) => return Err(EiAcceptorError::SocketStat(e.into())),
|
||||
}
|
||||
let sun_path = uapi::as_bytes_mut(&mut addr.sun_path[..]);
|
||||
sun_path[..path.len()].copy_from_slice(path.as_bytes());
|
||||
sun_path[path.len()] = 0;
|
||||
if let Err(e) = uapi::bind(insecure.raw(), &addr) {
|
||||
return Err(EiAcceptorError::BindFailed(e.into()));
|
||||
}
|
||||
Ok(EiAllocatedSocket {
|
||||
name,
|
||||
path,
|
||||
insecure: insecure.clone(),
|
||||
lock_path,
|
||||
_lock_fd: lock_fd,
|
||||
})
|
||||
}
|
||||
|
||||
fn allocate_socket() -> Result<EiAllocatedSocket, EiAcceptorError> {
|
||||
let xrd = match xrd() {
|
||||
Some(d) => d,
|
||||
_ => return Err(EiAcceptorError::XrdNotSet),
|
||||
};
|
||||
let socket = match uapi::socket(c::AF_UNIX, c::SOCK_STREAM | c::SOCK_CLOEXEC, 0) {
|
||||
Ok(f) => Rc::new(f),
|
||||
Err(e) => return Err(EiAcceptorError::SocketFailed(e.into())),
|
||||
};
|
||||
for i in 1..1000 {
|
||||
match bind_socket(&socket, &xrd, i) {
|
||||
Ok(s) => return Ok(s),
|
||||
Err(e) => {
|
||||
log::warn!("Cannot use the eis-{} socket: {}", i, ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(EiAcceptorError::AddressesInUse)
|
||||
}
|
||||
|
||||
impl EiAcceptor {
|
||||
pub fn spawn(
|
||||
state: &Rc<State>,
|
||||
) -> Result<(Rc<EiAcceptor>, SpawnedFuture<()>), EiAcceptorError> {
|
||||
let socket = allocate_socket()?;
|
||||
log::info!("bound to socket {}", socket.path.display());
|
||||
if let Err(e) = uapi::listen(socket.insecure.raw(), 4096) {
|
||||
return Err(EiAcceptorError::ListenFailed(e.into()));
|
||||
}
|
||||
let acc = Rc::new(EiAcceptor { socket });
|
||||
let future = state
|
||||
.eng
|
||||
.spawn(accept(acc.socket.insecure.clone(), state.clone()));
|
||||
Ok((acc, future))
|
||||
}
|
||||
|
||||
pub fn socket_name(&self) -> &str {
|
||||
&self.socket.name
|
||||
}
|
||||
}
|
||||
|
||||
async fn accept(fd: Rc<OwnedFd>, state: Rc<State>) {
|
||||
loop {
|
||||
let fd = match state.ring.accept(&fd, c::SOCK_CLOEXEC).await {
|
||||
Ok(fd) => fd,
|
||||
Err(e) => {
|
||||
log::error!("Could not accept a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
};
|
||||
let id = state.clients.id();
|
||||
if let Err(e) = state.ei_clients.spawn(id, &state, fd) {
|
||||
log::error!("Could not spawn a client: {}", ErrorFmt(e));
|
||||
break;
|
||||
}
|
||||
}
|
||||
state.ring.stop();
|
||||
}
|
||||
301
src/ei/ei_client.rs
Normal file
301
src/ei/ei_client.rs
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
pub use crate::ei::ei_client::ei_error::{EiClientError, EiParserError};
|
||||
use {
|
||||
crate::{
|
||||
async_engine::SpawnedFuture,
|
||||
client::ClientId,
|
||||
ei::{
|
||||
ei_client::ei_objects::EiObjects,
|
||||
ei_ifs::{ei_connection::EiConnection, ei_handshake::EiHandshake},
|
||||
ei_object::{EiInterface, EiObject, EiObjectId},
|
||||
EiContext, EiInterfaceVersion,
|
||||
},
|
||||
ifs::wl_seat::WlSeatGlobal,
|
||||
leaks::Tracker,
|
||||
state::State,
|
||||
utils::{
|
||||
asyncevent::AsyncEvent,
|
||||
buffd::{EiMsgFormatter, EiMsgParser, EiMsgParserError, OutBufferSwapchain},
|
||||
clonecell::CloneCell,
|
||||
errorfmt::ErrorFmt,
|
||||
numcell::NumCell,
|
||||
pid_info::{get_pid_info, get_socket_creds, PidInfo},
|
||||
},
|
||||
wire_ei::EiInterfaceVersions,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
std::{
|
||||
cell::{Cell, RefCell},
|
||||
error::Error,
|
||||
fmt::Debug,
|
||||
mem,
|
||||
ops::DerefMut,
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::{c, OwnedFd},
|
||||
};
|
||||
|
||||
mod ei_error;
|
||||
mod ei_objects;
|
||||
mod ei_tasks;
|
||||
|
||||
pub struct EiClients {
|
||||
pub clients: RefCell<AHashMap<ClientId, EiClientHolder>>,
|
||||
shutdown_clients: RefCell<AHashMap<ClientId, EiClientHolder>>,
|
||||
}
|
||||
|
||||
impl EiClients {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
clients: Default::default(),
|
||||
shutdown_clients: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn announce_seat(&self, seat: &Rc<WlSeatGlobal>) {
|
||||
for ei_client in self.clients.borrow().values() {
|
||||
if let Some(connection) = ei_client.data.connection.get() {
|
||||
connection.announce_seat(&seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
mem::take(self.clients.borrow_mut().deref_mut());
|
||||
mem::take(self.shutdown_clients.borrow_mut().deref_mut());
|
||||
}
|
||||
|
||||
pub fn spawn(
|
||||
&self,
|
||||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
) -> Result<(), EiClientError> {
|
||||
let Some((uid, pid)) = get_socket_creds(&socket) else {
|
||||
return Ok(());
|
||||
};
|
||||
self.spawn2(id, global, socket, uid, pid)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn spawn2(
|
||||
&self,
|
||||
id: ClientId,
|
||||
global: &Rc<State>,
|
||||
socket: Rc<OwnedFd>,
|
||||
uid: c::uid_t,
|
||||
pid: c::pid_t,
|
||||
) -> Result<Rc<EiClient>, EiClientError> {
|
||||
let versions = EiInterfaceVersions {
|
||||
ei_button: EiInterfaceVersion::new(1),
|
||||
ei_callback: EiInterfaceVersion::new(1),
|
||||
ei_connection: EiInterfaceVersion::new(1),
|
||||
ei_device: EiInterfaceVersion::new(2),
|
||||
ei_handshake: EiInterfaceVersion::new(1),
|
||||
ei_keyboard: EiInterfaceVersion::new(1),
|
||||
ei_pingpong: EiInterfaceVersion::new(1),
|
||||
ei_pointer: EiInterfaceVersion::new(1),
|
||||
ei_pointer_absolute: EiInterfaceVersion::new(1),
|
||||
ei_scroll: EiInterfaceVersion::new(1),
|
||||
ei_seat: EiInterfaceVersion::new(1),
|
||||
ei_touchscreen: EiInterfaceVersion::new(1),
|
||||
};
|
||||
let data = Rc::new(EiClient {
|
||||
id,
|
||||
state: global.clone(),
|
||||
context: Cell::new(EiContext::Receiver),
|
||||
connection: Default::default(),
|
||||
checking_queue_size: Cell::new(false),
|
||||
socket,
|
||||
objects: EiObjects::new(),
|
||||
swapchain: Default::default(),
|
||||
flush_request: Default::default(),
|
||||
shutdown: Default::default(),
|
||||
tracker: Default::default(),
|
||||
pid_info: get_pid_info(uid, pid),
|
||||
disconnect_announced: Cell::new(false),
|
||||
versions,
|
||||
name: Default::default(),
|
||||
last_serial: Default::default(),
|
||||
});
|
||||
track!(data, data);
|
||||
let handshake = Rc::new(EiHandshake::new(&data));
|
||||
track!(data, handshake);
|
||||
handshake.send_handshake_version();
|
||||
data.objects.add_handshake(&handshake);
|
||||
let client = EiClientHolder {
|
||||
_handler: global.eng.spawn(ei_tasks::ei_client(data.clone())),
|
||||
data: data.clone(),
|
||||
};
|
||||
log::info!(
|
||||
"Client {} connected, pid: {}, uid: {}, fd: {}, comm: {:?}",
|
||||
id,
|
||||
pid,
|
||||
uid,
|
||||
client.data.socket.raw(),
|
||||
data.pid_info.comm,
|
||||
);
|
||||
self.clients.borrow_mut().insert(client.data.id, client);
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub fn kill(&self, client: ClientId) {
|
||||
log::info!("Removing client {}", client);
|
||||
if self.clients.borrow_mut().remove(&client).is_none() {
|
||||
self.shutdown_clients.borrow_mut().remove(&client);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, client_id: ClientId) {
|
||||
if let Some(client) = self.clients.borrow_mut().remove(&client_id) {
|
||||
log::info!("Shutting down client {}", client.data.id);
|
||||
client.data.shutdown.trigger();
|
||||
client.data.flush_request.trigger();
|
||||
self.shutdown_clients.borrow_mut().insert(client_id, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EiClients {
|
||||
fn drop(&mut self) {
|
||||
let _clients1 = mem::take(&mut *self.clients.borrow_mut());
|
||||
let _clients2 = mem::take(&mut *self.shutdown_clients.borrow_mut());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EiClientHolder {
|
||||
pub data: Rc<EiClient>,
|
||||
_handler: SpawnedFuture<()>,
|
||||
}
|
||||
|
||||
impl Drop for EiClientHolder {
|
||||
fn drop(&mut self) {
|
||||
self.data.objects.destroy();
|
||||
self.data.flush_request.clear();
|
||||
self.data.shutdown.clear();
|
||||
self.data.connection.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EiEventFormatter: Debug {
|
||||
fn format(self, fmt: &mut EiMsgFormatter<'_>);
|
||||
fn id(&self) -> EiObjectId;
|
||||
fn interface(&self) -> EiInterface;
|
||||
}
|
||||
|
||||
pub trait EiRequestParser<'a>: Debug + Sized {
|
||||
type Generic<'b>: EiRequestParser<'b>;
|
||||
fn parse(parser: &mut EiMsgParser<'_, 'a>) -> Result<Self, EiMsgParserError>;
|
||||
}
|
||||
|
||||
pub struct EiClient {
|
||||
pub id: ClientId,
|
||||
pub state: Rc<State>,
|
||||
pub context: Cell<EiContext>,
|
||||
pub connection: CloneCell<Option<Rc<EiConnection>>>,
|
||||
checking_queue_size: Cell<bool>,
|
||||
socket: Rc<OwnedFd>,
|
||||
pub objects: EiObjects,
|
||||
swapchain: Rc<RefCell<OutBufferSwapchain>>,
|
||||
flush_request: AsyncEvent,
|
||||
shutdown: AsyncEvent,
|
||||
pub tracker: Tracker<EiClient>,
|
||||
pub pid_info: PidInfo,
|
||||
pub disconnect_announced: Cell<bool>,
|
||||
pub versions: EiInterfaceVersions,
|
||||
pub name: RefCell<Option<String>>,
|
||||
pub last_serial: NumCell<u64>,
|
||||
}
|
||||
|
||||
impl EiClient {
|
||||
pub fn new_id<T: From<EiObjectId>>(&self) -> T {
|
||||
self.objects.id()
|
||||
}
|
||||
|
||||
pub fn serial(&self) -> u32 {
|
||||
(self.last_serial.fetch_add(1) + 1) as u32
|
||||
}
|
||||
|
||||
pub fn last_serial(&self) -> u32 {
|
||||
self.last_serial.get() as u32
|
||||
}
|
||||
|
||||
pub fn error(&self, message: impl Error) {
|
||||
let msg = ErrorFmt(message).to_string();
|
||||
log::error!("Client {}: A fatal error occurred: {}", self.id, msg);
|
||||
match self.connection.get() {
|
||||
Some(d) => {
|
||||
d.send_disconnected(Some(&msg));
|
||||
self.state.clients.shutdown(self.id);
|
||||
}
|
||||
_ => {
|
||||
self.state.clients.kill(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse<'a, R: EiRequestParser<'a>>(
|
||||
&self,
|
||||
obj: &impl EiObject,
|
||||
mut parser: EiMsgParser<'_, 'a>,
|
||||
) -> Result<R, EiMsgParserError> {
|
||||
let res = R::parse(&mut parser)?;
|
||||
parser.eof()?;
|
||||
log::trace!(
|
||||
"Client {} -> {}@{:x}.{:?}",
|
||||
self.id,
|
||||
obj.interface().name(),
|
||||
obj.id(),
|
||||
res
|
||||
);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
pub fn event<T: EiEventFormatter>(self: &Rc<Self>, event: T) {
|
||||
log::trace!(
|
||||
"Client {} <= {}@{:x}.{:?}",
|
||||
self.id,
|
||||
event.interface().name(),
|
||||
event.id(),
|
||||
event,
|
||||
);
|
||||
let mut fds = vec![];
|
||||
let mut swapchain = self.swapchain.borrow_mut();
|
||||
let mut fmt = EiMsgFormatter::new(&mut swapchain.cur, &mut fds);
|
||||
event.format(&mut fmt);
|
||||
fmt.write_len();
|
||||
if swapchain.cur.is_full() {
|
||||
swapchain.commit();
|
||||
if swapchain.exceeds_limit() {
|
||||
if !self.checking_queue_size.replace(true) {
|
||||
self.state.slow_ei_clients.push(self.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.flush_request.trigger();
|
||||
}
|
||||
|
||||
pub async fn check_queue_size(&self) {
|
||||
if self.swapchain.borrow_mut().exceeds_limit() {
|
||||
self.state.eng.yield_now().await;
|
||||
if self.swapchain.borrow_mut().exceeds_limit() {
|
||||
log::error!("Client {} is too slow at fetching events", self.id);
|
||||
self.state.ei_clients.kill(self.id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.checking_queue_size.set(false);
|
||||
}
|
||||
|
||||
pub fn add_client_obj<T: EiObject>(&self, obj: &Rc<T>) -> Result<(), EiClientError> {
|
||||
self.objects.add_client_object(obj.clone())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_server_obj<T: EiObject>(&self, obj: &Rc<T>) {
|
||||
self.objects.add_server_object(obj.clone());
|
||||
}
|
||||
|
||||
pub fn remove_obj<T: EiObject>(self: &Rc<Self>, obj: &T) -> Result<(), EiClientError> {
|
||||
self.objects.remove_obj(obj.id())
|
||||
}
|
||||
}
|
||||
45
src/ei/ei_client/ei_error.rs
Normal file
45
src/ei/ei_client/ei_error.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use {
|
||||
crate::{
|
||||
ei::ei_object::{EiInterface, EiObjectId},
|
||||
utils::buffd::{BufFdError, EiMsgParserError},
|
||||
},
|
||||
std::error::Error,
|
||||
thiserror::Error,
|
||||
};
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum EiClientError {
|
||||
#[error("An error occurred reading from/writing to the client")]
|
||||
Io(#[from] BufFdError),
|
||||
#[error("An error occurred while processing a request")]
|
||||
RequestError(#[source] Box<EiClientError>),
|
||||
#[error("Client tried to invoke a non-existent method")]
|
||||
InvalidMethod,
|
||||
#[error("The message size is < 16")]
|
||||
MessageSizeTooSmall,
|
||||
#[error("The message size is > 2^16")]
|
||||
MessageSizeTooLarge,
|
||||
#[error("The size of the message is not a multiple of 4")]
|
||||
UnalignedMessage,
|
||||
#[error("The object id is unknown")]
|
||||
UnknownId,
|
||||
#[error("Client tried to access non-existent object {0}")]
|
||||
InvalidObject(EiObjectId),
|
||||
#[error("The id is already in use")]
|
||||
IdAlreadyInUse,
|
||||
#[error("The client object id is out of bounds")]
|
||||
ClientIdOutOfBounds,
|
||||
#[error("Could not process a `{}.{}` request", .interface.name(), .method)]
|
||||
MethodError {
|
||||
interface: EiInterface,
|
||||
method: &'static str,
|
||||
#[source]
|
||||
error: Box<dyn Error + 'static>,
|
||||
},
|
||||
#[error("Could not add object {0} to the client")]
|
||||
AddObjectError(EiObjectId, #[source] Box<EiClientError>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
#[error("Parsing failed")]
|
||||
pub struct EiParserError(#[source] pub EiMsgParserError);
|
||||
83
src/ei/ei_client/ei_objects.rs
Normal file
83
src/ei/ei_client/ei_objects.rs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
use {
|
||||
crate::{
|
||||
ei::{
|
||||
ei_client::ei_error::EiClientError,
|
||||
ei_ifs::ei_handshake::EiHandshake,
|
||||
ei_object::{EiObject, EiObjectId},
|
||||
},
|
||||
utils::{copyhashmap::CopyHashMap, numcell::NumCell},
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct EiObjects {
|
||||
registry: CopyHashMap<EiObjectId, Rc<dyn EiObject>>,
|
||||
next_sever_id: NumCell<u64>,
|
||||
}
|
||||
|
||||
pub const MIN_SERVER_ID: u64 = 0xff00_0000_0000_0000;
|
||||
|
||||
impl EiObjects {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
registry: Default::default(),
|
||||
next_sever_id: NumCell::new(MIN_SERVER_ID),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(&self) {
|
||||
for obj in self.registry.lock().values_mut() {
|
||||
obj.break_loops();
|
||||
}
|
||||
self.registry.clear();
|
||||
}
|
||||
|
||||
pub fn id<T>(&self) -> T
|
||||
where
|
||||
EiObjectId: Into<T>,
|
||||
{
|
||||
EiObjectId::from_raw(self.next_sever_id.fetch_add(1)).into()
|
||||
}
|
||||
|
||||
pub fn get_obj(&self, id: EiObjectId) -> Option<Rc<dyn EiObject>> {
|
||||
self.registry.get(&id)
|
||||
}
|
||||
|
||||
pub fn add_server_object(&self, obj: Rc<dyn EiObject>) {
|
||||
let id = obj.id();
|
||||
assert!(id.raw() >= MIN_SERVER_ID);
|
||||
assert!(!self.registry.contains(&id));
|
||||
self.registry.set(id, obj.clone());
|
||||
}
|
||||
|
||||
pub fn add_handshake(&self, obj: &Rc<EiHandshake>) {
|
||||
assert_eq!(obj.id.raw(), 0);
|
||||
assert!(self.registry.is_empty());
|
||||
self.registry.set(obj.id.into(), obj.clone());
|
||||
}
|
||||
|
||||
pub fn add_client_object(&self, obj: Rc<dyn EiObject>) -> Result<(), EiClientError> {
|
||||
let id = obj.id();
|
||||
let res = (|| {
|
||||
if id.raw() == 0 || id.raw() >= MIN_SERVER_ID {
|
||||
return Err(EiClientError::ClientIdOutOfBounds);
|
||||
}
|
||||
if self.registry.contains(&id) {
|
||||
return Err(EiClientError::IdAlreadyInUse);
|
||||
}
|
||||
self.registry.set(id, obj.clone());
|
||||
Ok(())
|
||||
})();
|
||||
if let Err(e) = res {
|
||||
return Err(EiClientError::AddObjectError(id, Box::new(e)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_obj(&self, id: EiObjectId) -> Result<(), EiClientError> {
|
||||
match self.registry.remove(&id) {
|
||||
Some(_) => Ok(()),
|
||||
_ => Err(EiClientError::UnknownId),
|
||||
}
|
||||
}
|
||||
}
|
||||
141
src/ei/ei_client/ei_tasks.rs
Normal file
141
src/ei/ei_client/ei_tasks.rs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::Phase,
|
||||
ei::{
|
||||
ei_client::{ei_error::EiClientError, EiClient},
|
||||
ei_object::EiObjectId,
|
||||
},
|
||||
utils::{
|
||||
buffd::{BufFdIn, BufFdOut, EiMsgParser},
|
||||
errorfmt::ErrorFmt,
|
||||
vec_ext::VecExt,
|
||||
},
|
||||
},
|
||||
futures_util::{select, FutureExt},
|
||||
std::{collections::VecDeque, mem, rc::Rc, time::Duration},
|
||||
};
|
||||
|
||||
pub async fn ei_client(data: Rc<EiClient>) {
|
||||
let mut recv = data.state.eng.spawn(receive(data.clone())).fuse();
|
||||
let mut shutdown = data.shutdown.triggered().fuse();
|
||||
let _send = data.state.eng.spawn2(Phase::PostLayout, send(data.clone()));
|
||||
select! {
|
||||
_ = recv => { },
|
||||
_ = shutdown => { },
|
||||
}
|
||||
drop(recv);
|
||||
data.flush_request.trigger();
|
||||
match data.state.wheel.timeout(5000).await {
|
||||
Ok(_) => {
|
||||
log::error!("Could not shut down client {} within 5 seconds", data.id);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Could not create a timeout: {}", ErrorFmt(e));
|
||||
}
|
||||
}
|
||||
data.state.ei_clients.kill(data.id);
|
||||
}
|
||||
|
||||
async fn receive(data: Rc<EiClient>) {
|
||||
let recv = async {
|
||||
let mut buf = BufFdIn::new(&data.socket, &data.state.ring);
|
||||
let mut data_buf = Vec::<u32>::new();
|
||||
loop {
|
||||
let mut hdr = [0u32; 4];
|
||||
buf.read_full(&mut hdr[..]).await?;
|
||||
#[cfg(target_endian = "little")]
|
||||
let obj_id = (hdr[0] as u64) | ((hdr[1] as u64) << 32);
|
||||
#[cfg(target_endian = "big")]
|
||||
let obj_id = (hdr[1] as u64) | ((hdr[0] as u64) << 32);
|
||||
let obj_id = EiObjectId::from_raw(obj_id);
|
||||
let len = hdr[2] as usize;
|
||||
let request = hdr[3];
|
||||
if len < 16 {
|
||||
return Err(EiClientError::MessageSizeTooSmall);
|
||||
}
|
||||
if len > (u16::MAX as usize) {
|
||||
return Err(EiClientError::MessageSizeTooLarge);
|
||||
}
|
||||
if len % 4 != 0 {
|
||||
return Err(EiClientError::UnalignedMessage);
|
||||
}
|
||||
let len = (len - 16) / 4;
|
||||
data_buf.clear();
|
||||
data_buf.reserve(len);
|
||||
let unused = data_buf.split_at_spare_mut_ext().1;
|
||||
buf.read_full(&mut unused[..len]).await?;
|
||||
unsafe {
|
||||
data_buf.set_len(len);
|
||||
}
|
||||
let obj = match data.objects.get_obj(obj_id) {
|
||||
Some(obj) => obj,
|
||||
_ => match data.connection.get() {
|
||||
None => {
|
||||
return Err(EiClientError::InvalidObject(obj_id));
|
||||
}
|
||||
Some(c) => {
|
||||
c.send_invalid_object(obj_id);
|
||||
continue;
|
||||
}
|
||||
},
|
||||
};
|
||||
let parser = EiMsgParser::new(&mut buf, &data_buf[..]);
|
||||
if let Err(e) = obj.handle_request(&data, request, parser) {
|
||||
return Err(EiClientError::RequestError(Box::new(e)));
|
||||
}
|
||||
}
|
||||
};
|
||||
let res: Result<(), EiClientError> = recv.await;
|
||||
if let Err(e) = res {
|
||||
if data.disconnect_announced.get() {
|
||||
log::info!("Client {} terminated the connection", data.id);
|
||||
data.state.ei_clients.kill(data.id);
|
||||
} else {
|
||||
let e = ErrorFmt(e);
|
||||
log::error!(
|
||||
"An error occurred while trying to handle a message from client {}: {}",
|
||||
data.id,
|
||||
e
|
||||
);
|
||||
if let Some(c) = data.connection.get() {
|
||||
c.send_disconnected(Some(&e.to_string()));
|
||||
data.state.ei_clients.shutdown(data.id);
|
||||
} else {
|
||||
data.state.ei_clients.kill(data.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send(data: Rc<EiClient>) {
|
||||
let send = async {
|
||||
let mut out = BufFdOut::new(&data.socket, &data.state.ring);
|
||||
let mut buffers = VecDeque::new();
|
||||
loop {
|
||||
data.flush_request.triggered().await;
|
||||
{
|
||||
let mut swapchain = data.swapchain.borrow_mut();
|
||||
swapchain.commit();
|
||||
mem::swap(&mut swapchain.pending, &mut buffers);
|
||||
}
|
||||
let timeout = data.state.now() + Duration::from_millis(5000);
|
||||
while let Some(mut cur) = buffers.pop_front() {
|
||||
out.flush(&mut cur, timeout).await?;
|
||||
data.swapchain.borrow_mut().free.push(cur);
|
||||
}
|
||||
}
|
||||
};
|
||||
let res: Result<(), EiClientError> = send.await;
|
||||
if let Err(e) = res {
|
||||
if data.disconnect_announced.get() {
|
||||
log::info!("Client {} terminated the connection", data.id);
|
||||
} else {
|
||||
log::error!(
|
||||
"An error occurred while sending data to client {}: {}",
|
||||
data.id,
|
||||
ErrorFmt(e)
|
||||
);
|
||||
}
|
||||
}
|
||||
data.state.ei_clients.kill(data.id);
|
||||
}
|
||||
44
src/ei/ei_ifs.rs
Normal file
44
src/ei/ei_ifs.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
macro_rules! ei_device_interface {
|
||||
($camel:ident, $snake:ident, $field:ident) => {
|
||||
impl EiDeviceInterface for $camel {
|
||||
fn new(device: &Rc<EiDevice>, version: EiVersion) -> Rc<Self> {
|
||||
let v = Rc::new(Self {
|
||||
id: device.client.new_id(),
|
||||
client: device.client.clone(),
|
||||
tracker: Default::default(),
|
||||
version,
|
||||
device: device.clone(),
|
||||
});
|
||||
track!(v.client, v);
|
||||
v
|
||||
}
|
||||
|
||||
fn destroy(&self) -> Result<(), EiClientError> {
|
||||
self.send_destroyed(self.client.serial());
|
||||
self.client.remove_obj(self)?;
|
||||
self.device.seat.$field.take();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn send_destroyed(&self, serial: u32) {
|
||||
self.client.event(crate::wire_ei::$snake::Destroyed {
|
||||
self_id: self.id,
|
||||
serial,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod ei_button;
|
||||
pub mod ei_callback;
|
||||
pub mod ei_connection;
|
||||
pub mod ei_device;
|
||||
pub mod ei_handshake;
|
||||
pub mod ei_keyboard;
|
||||
pub mod ei_pingpong;
|
||||
pub mod ei_pointer;
|
||||
pub mod ei_pointer_absolute;
|
||||
pub mod ei_scroll;
|
||||
pub mod ei_seat;
|
||||
pub mod ei_touchscreen;
|
||||
72
src/ei/ei_ifs/ei_button.rs
Normal file
72
src/ei/ei_ifs/ei_button.rs
Normal 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);
|
||||
49
src/ei/ei_ifs/ei_callback.rs
Normal file
49
src/ei/ei_ifs/ei_callback.rs
Normal 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);
|
||||
160
src/ei/ei_ifs/ei_connection.rs
Normal file
160
src/ei/ei_ifs/ei_connection.rs
Normal 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
249
src/ei/ei_ifs/ei_device.rs
Normal 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);
|
||||
189
src/ei/ei_ifs/ei_handshake.rs
Normal file
189
src/ei/ei_ifs/ei_handshake.rs
Normal 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);
|
||||
97
src/ei/ei_ifs/ei_keyboard.rs
Normal file
97
src/ei/ei_ifs/ei_keyboard.rs
Normal 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);
|
||||
45
src/ei/ei_ifs/ei_pingpong.rs
Normal file
45
src/ei/ei_ifs/ei_pingpong.rs
Normal 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);
|
||||
71
src/ei/ei_ifs/ei_pointer.rs
Normal file
71
src/ei/ei_ifs/ei_pointer.rs
Normal 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);
|
||||
72
src/ei/ei_ifs/ei_pointer_absolute.rs
Normal file
72
src/ei/ei_ifs/ei_pointer_absolute.rs
Normal 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
106
src/ei/ei_ifs/ei_scroll.rs
Normal 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
425
src/ei/ei_ifs/ei_seat.rs
Normal 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);
|
||||
108
src/ei/ei_ifs/ei_touchscreen.rs
Normal file
108
src/ei/ei_ifs/ei_touchscreen.rs
Normal 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);
|
||||
94
src/ei/ei_object.rs
Normal file
94
src/ei/ei_object.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
use {
|
||||
crate::{
|
||||
ei::{
|
||||
ei_client::{EiClient, EiClientError},
|
||||
EiContext,
|
||||
},
|
||||
utils::buffd::EiMsgParser,
|
||||
wire_ei::EiHandshakeId,
|
||||
},
|
||||
std::{
|
||||
cmp::Ordering,
|
||||
fmt::{Display, Formatter, LowerHex},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub const EI_HANDSHAKE_ID: EiHandshakeId = EiHandshakeId::from_raw(0);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
|
||||
pub struct EiObjectId(u64);
|
||||
|
||||
impl EiObjectId {
|
||||
#[allow(dead_code)]
|
||||
pub const NONE: Self = EiObjectId(0);
|
||||
|
||||
pub fn from_raw(raw: u64) -> Self {
|
||||
Self(raw)
|
||||
}
|
||||
|
||||
pub fn raw(self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for EiObjectId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl LowerHex for EiObjectId {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
LowerHex::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EiObjectBase {
|
||||
fn id(&self) -> EiObjectId;
|
||||
fn version(&self) -> EiVersion;
|
||||
fn client(&self) -> &EiClient;
|
||||
fn handle_request(
|
||||
self: Rc<Self>,
|
||||
client: &EiClient,
|
||||
request: u32,
|
||||
parser: EiMsgParser<'_, '_>,
|
||||
) -> Result<(), EiClientError>;
|
||||
fn interface(&self) -> EiInterface;
|
||||
}
|
||||
|
||||
pub trait EiObject: EiObjectBase + 'static {
|
||||
fn break_loops(&self) {}
|
||||
|
||||
fn context(&self) -> EiContext {
|
||||
self.client().context.get()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct EiInterface(pub &'static str);
|
||||
|
||||
impl EiInterface {
|
||||
pub fn name(self) -> &'static str {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct EiVersion(pub u32);
|
||||
|
||||
impl EiVersion {
|
||||
// pub const ALL: EiVersion = EiVersion(0);
|
||||
}
|
||||
|
||||
impl PartialEq<u32> for EiVersion {
|
||||
fn eq(&self, other: &u32) -> bool {
|
||||
self.0 == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<u32> for EiVersion {
|
||||
fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue