189 lines
5.8 KiB
Rust
189 lines
5.8 KiB
Rust
use {
|
|
crate::{
|
|
ei::{
|
|
EiContext,
|
|
ei_client::{EiClient, EiClientError},
|
|
ei_ifs::ei_connection::EiConnection,
|
|
ei_object::{EI_HANDSHAKE_ID, EiInterface, EiObject, EiVersion},
|
|
},
|
|
leaks::Tracker,
|
|
wire_ei::{
|
|
EiHandshake, EiHandshakeId,
|
|
ei_handshake::{
|
|
ClientHandshakeVersion, ClientInterfaceVersion, Connection, ContextType,
|
|
EiHandshakeRequestHandler, Finish, Name, ServerHandshakeVersion,
|
|
ServerInterfaceVersion,
|
|
},
|
|
},
|
|
},
|
|
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);
|