From ab095b89cf7f8c3c240d1ca573b9f2d8afd9417c Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Fri, 2 May 2025 19:24:43 +0200 Subject: [PATCH] config: add Client --- jay-config/src/_private/client.rs | 43 ++++++++++++++++++++++++------- jay-config/src/_private/ipc.rs | 20 ++++++++++++++ jay-config/src/client.rs | 36 ++++++++++++++++++++++++++ jay-config/src/lib.rs | 1 + src/client.rs | 1 - src/config/handler.rs | 43 +++++++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 jay-config/src/client.rs diff --git a/jay-config/src/_private/client.rs b/jay-config/src/_private/client.rs index fd105478..0b7542f4 100644 --- a/jay-config/src/_private/client.rs +++ b/jay-config/src/_private/client.rs @@ -10,6 +10,7 @@ use { logging, }, Axis, Direction, ModifiedKeySym, PciId, Workspace, + client::Client, exec::Command, input::{ FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile, @@ -81,7 +82,7 @@ struct KeyHandler { latched: Vec>, } -pub(crate) struct Client { +pub(crate) struct ConfigClient { configure: extern "C" fn(), srv_data: *const u8, srv_unref: unsafe extern "C" fn(data: *const u8), @@ -145,7 +146,7 @@ struct Task { waker: Waker, } -impl Drop for Client { +impl Drop for ConfigClient { fn drop(&mut self) { unsafe { (self.srv_unref)(self.srv_data); @@ -154,13 +155,13 @@ impl Drop for Client { } thread_local! { - pub(crate) static CLIENT: Cell<*const Client> = const { Cell::new(ptr::null()) }; + pub(crate) static CLIENT: Cell<*const ConfigClient> = const { Cell::new(ptr::null()) }; } -unsafe fn with_client T>(data: *const u8, f: F) -> T { +unsafe fn with_client T>(data: *const u8, f: F) -> T { struct Reset<'a> { - cell: &'a Cell<*const Client>, - val: *const Client, + cell: &'a Cell<*const ConfigClient>, + val: *const ConfigClient, } impl Drop for Reset<'_> { fn drop(&mut self) { @@ -168,7 +169,7 @@ unsafe fn with_client T>(data: *const u8, f: F) -> T { } } CLIENT.with(|cell| unsafe { - let client = data as *const Client; + let client = data as *const ConfigClient; Rc::increment_strong_count(client); let client = Rc::from_raw(client); let old = cell.replace(client.deref()); @@ -214,7 +215,7 @@ pub unsafe extern "C" fn init( size: usize, f: extern "C" fn(), ) -> *const u8 { - let client = Rc::new(Client { + let client = Rc::new(ConfigClient { configure: f, srv_data, srv_unref, @@ -251,7 +252,7 @@ pub unsafe extern "C" fn init( } pub unsafe extern "C" fn unref(data: *const u8) { - let client = data as *const Client; + let client = data as *const ConfigClient; unsafe { drop(Rc::from_raw(client)); } @@ -278,7 +279,7 @@ macro_rules! get_response { } } -impl Client { +impl ConfigClient { fn send(&self, msg: &ClientMessage) { let mut buf = self.bufs.borrow_mut().pop().unwrap_or_default(); buf.clear(); @@ -1259,6 +1260,28 @@ impl Client { } } + pub fn clients(&self) -> Vec { + let res = self.send_with_response(&ClientMessage::GetClients); + get_response!(res, vec!(), GetClients { clients }); + clients + } + + pub fn client_exists(&self, client: Client) -> bool { + let res = self.send_with_response(&ClientMessage::ClientExists { client }); + get_response!(res, false, ClientExists { exists }); + exists + } + + pub fn client_is_xwayland(&self, client: Client) -> bool { + let res = self.send_with_response(&ClientMessage::ClientIsXwayland { client }); + get_response!(res, false, ClientIsXwayland { is_xwayland }); + is_xwayland + } + + pub fn client_kill(&self, client: Client) { + self.send(&ClientMessage::ClientKill { client }); + } + fn handle_msg(&self, msg: &[u8]) { self.handle_msg2(msg); self.dispatch_futures(); diff --git a/jay-config/src/_private/ipc.rs b/jay-config/src/_private/ipc.rs index 1bf85211..03eb85e3 100644 --- a/jay-config/src/_private/ipc.rs +++ b/jay-config/src/_private/ipc.rs @@ -2,6 +2,7 @@ use { crate::{ _private::{PollableId, WireMode}, Axis, Direction, PciId, Workspace, + client::Client, input::{ FocusFollowsMouseMode, InputDevice, Seat, SwitchEvent, acceleration::AccelProfile, capability::Capability, @@ -565,6 +566,16 @@ pub enum ClientMessage<'a> { GetConnectorWorkspaces { connector: Connector, }, + GetClients, + ClientExists { + client: Client, + }, + ClientKill { + client: Client, + }, + ClientIsXwayland { + client: Client, + }, } #[derive(Serialize, Deserialize, Debug)] @@ -728,6 +739,15 @@ pub enum Response { GetConnectorWorkspaces { workspaces: Vec, }, + GetClients { + clients: Vec, + }, + ClientExists { + exists: bool, + }, + ClientIsXwayland { + is_xwayland: bool, + }, } #[derive(Serialize, Deserialize, Debug)] diff --git a/jay-config/src/client.rs b/jay-config/src/client.rs new file mode 100644 index 00000000..5e908352 --- /dev/null +++ b/jay-config/src/client.rs @@ -0,0 +1,36 @@ +//! Tools for inspecting and manipulating clients. + +use serde::{Deserialize, Serialize}; + +/// A client connected to the compositor. +#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub struct Client(pub u64); + +impl Client { + /// Returns whether the client exists. + pub fn exists(self) -> bool { + self.0 != 0 && get!(false).client_exists(self) + } + + /// Returns whether the client does not exist. + /// + /// This is a shorthand for `!self.exists()`. + pub fn does_not_exist(self) -> bool { + !self.exists() + } + + /// Returns whether this client is XWayland. + pub fn is_xwayland(self) -> bool { + get!(false).client_is_xwayland(self) + } + + /// Disconnects the client. + pub fn kill(self) { + get!().client_kill(self) + } +} + +/// Returns all current clients. +pub fn clients() -> Vec { + get!().clients() +} diff --git a/jay-config/src/lib.rs b/jay-config/src/lib.rs index dbcd2e85..b0570033 100644 --- a/jay-config/src/lib.rs +++ b/jay-config/src/lib.rs @@ -58,6 +58,7 @@ use { mod macros; #[doc(hidden)] pub mod _private; +pub mod client; pub mod embedded; pub mod exec; pub mod input; diff --git a/src/client.rs b/src/client.rs index 3781cd49..f925a2ea 100644 --- a/src/client.rs +++ b/src/client.rs @@ -106,7 +106,6 @@ impl Clients { ClientId(self.next_client_id.fetch_add(1)) } - #[cfg_attr(not(feature = "it"), expect(dead_code))] pub fn get(&self, id: ClientId) -> Result, ClientError> { let clients = self.clients.borrow(); match clients.get(&id) { diff --git a/src/config/handler.rs b/src/config/handler.rs index f9152adc..212c8041 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -5,6 +5,7 @@ use { self, BackendColorSpace, BackendTransferFunction, ConnectorId, DrmDeviceId, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, }, + client::{Client, ClientId}, cmm::cmm_transfer_function::TransferFunction, compositor::MAX_EXTENTS, config::ConfigProxy, @@ -38,6 +39,7 @@ use { ipc::{ClientMessage, Response, ServerMessage, WorkspaceSource}, }, Axis, Direction, Workspace, + client::Client as ConfigClient, input::{ FocusFollowsMouseMode, InputDevice, Seat, acceleration::{ACCEL_PROFILE_ADAPTIVE, ACCEL_PROFILE_FLAT, AccelProfile}, @@ -1717,6 +1719,39 @@ impl ConfigProxyHandler { self.keymaps.remove(&keymap); } + fn get_client(&self, client: ConfigClient) -> Result, CphError> { + self.state + .clients + .get(ClientId::from_raw(client.0)) + .ok() + .ok_or(CphError::ClientDoesNotExist(client)) + } + + fn handle_get_clients(&self) { + let mut clients = vec![]; + for client in self.state.clients.clients.borrow().values() { + clients.push(ConfigClient(client.data.id.raw())); + } + self.respond(Response::GetClients { clients }); + } + + fn handle_client_exists(&self, client: ConfigClient) { + self.respond(Response::ClientExists { + exists: self.get_client(client).is_ok(), + }); + } + + fn handle_client_is_xwayland(&self, client: ConfigClient) -> Result<(), CphError> { + self.respond(Response::ClientIsXwayland { + is_xwayland: self.get_client(client)?.is_xwayland, + }); + Ok(()) + } + + fn handle_client_kill(&self, client: ConfigClient) { + self.state.clients.kill(ClientId::from_raw(client.0)); + } + pub fn handle_request(self: &Rc, msg: &[u8]) { if let Err(e) = self.handle_request_(msg) { log::error!("Could not handle client request: {}", ErrorFmt(e)); @@ -2130,6 +2165,12 @@ impl ConfigProxyHandler { ClientMessage::GetConnectorWorkspaces { connector } => self .handle_get_connector_workspaces(connector) .wrn("get_connector_workspaces")?, + ClientMessage::GetClients => self.handle_get_clients(), + ClientMessage::ClientExists { client } => self.handle_client_exists(client), + ClientMessage::ClientIsXwayland { client } => self + .handle_client_is_xwayland(client) + .wrn("client_is_xwayland")?, + ClientMessage::ClientKill { client } => self.handle_client_kill(client), } Ok(()) } @@ -2205,6 +2246,8 @@ enum CphError { UnknownColorSpace(ColorSpace), #[error("Unknown transfer function {0:?}")] UnknownTransferFunction(ConfigTransferFunction), + #[error("Client {0:?} does not exist")] + ClientDoesNotExist(ConfigClient), } trait WithRequestName {