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, version: Cell, pub tracker: Tracker, have_context_type: Cell, } impl EiHandshake { pub fn new(client: &Rc) -> 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, ) -> 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) -> 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) -> 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) -> 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, ) -> 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), #[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);