Skip to main content

jay_config/
client.rs

1//! Tools for inspecting and manipulating clients.
2
3use serde::Deserialize;
4use serde::Serialize;
5use std::ops::Deref;
6
7/// A client connected to the compositor.
8#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
9pub struct Client(pub u64);
10
11impl Client {
12    /// Returns whether the client exists.
13    pub fn exists(self) -> bool {
14        self.0 != 0 && get!(false).client_exists(self)
15    }
16
17    /// Returns whether the client does not exist.
18    ///
19    /// This is a shorthand for `!self.exists()`.
20    pub fn does_not_exist(self) -> bool {
21        !self.exists()
22    }
23
24    /// Returns whether this client is XWayland.
25    pub fn is_xwayland(self) -> bool {
26        get!(false).client_is_xwayland(self)
27    }
28
29    /// Disconnects the client.
30    pub fn kill(self) {
31        get!().client_kill(self)
32    }
33}
34
35/// Returns all current clients.
36pub fn clients() -> Vec<Client> {
37    get!().clients()
38}
39
40/// A client matcher.
41#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
42pub struct ClientMatcher(pub u64);
43
44/// A matched client.
45#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
46pub struct MatchedClient {
47    pub(crate) matcher: ClientMatcher,
48    pub(crate) client: Client,
49}
50
51/// A criterion for matching a client.
52#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
53#[non_exhaustive]
54pub enum ClientCriterion<'a> {
55    /// Matches if the contained matcher matches.
56    Matcher(ClientMatcher),
57    /// Matches if the contained criterion does not match.
58    Not(&'a ClientCriterion<'a>),
59    /// Matches if all of the contained criteria match.
60    All(&'a [ClientCriterion<'a>]),
61    /// Matches if any of the contained criteria match.
62    Any(&'a [ClientCriterion<'a>]),
63    /// Matches if an exact number of the contained criteria match.
64    Exactly(usize, &'a [ClientCriterion<'a>]),
65    /// Matches the engine name of the client's sandbox verbatim.
66    SandboxEngine(&'a str),
67    /// Matches the engine name of the client's sandbox with a regular expression.
68    SandboxEngineRegex(&'a str),
69    /// Matches the app id of the client's sandbox verbatim.
70    SandboxAppId(&'a str),
71    /// Matches the app id of the client's sandbox with a regular expression.
72    SandboxAppIdRegex(&'a str),
73    /// Matches the instance id of the client's sandbox verbatim.
74    SandboxInstanceId(&'a str),
75    /// Matches the instance id of the client's sandbox with a regular expression.
76    SandboxInstanceIdRegex(&'a str),
77    /// Matches if the client is sandboxed.
78    Sandboxed,
79    /// Matches the user ID of the client.
80    Uid(i32),
81    /// Matches the process ID of the client.
82    Pid(i32),
83    /// Matches if the client is Xwayland.
84    IsXwayland,
85    /// Matches the `/proc/pid/comm` of the client verbatim.
86    Comm(&'a str),
87    /// Matches the `/proc/pid/comm` of the client with a regular expression.
88    CommRegex(&'a str),
89    /// Matches the `/proc/pid/exe` of the client verbatim.
90    Exe(&'a str),
91    /// Matches the `/proc/pid/exe` of the client with a regular expression.
92    ExeRegex(&'a str),
93    /// Matches the tag of the client verbatim.
94    Tag(&'a str),
95    /// Matches the tag of the client with a regular expression.
96    TagRegex(&'a str),
97}
98
99impl ClientCriterion<'_> {
100    /// Converts the criterion to a matcher.
101    pub fn to_matcher(self) -> ClientMatcher {
102        get!(ClientMatcher(0)).create_client_matcher(self)
103    }
104
105    /// Binds a function to execute when the criterion matches a client.
106    ///
107    /// This leaks the matcher.
108    pub fn bind<F: FnMut(MatchedClient) + 'static>(self, cb: F) {
109        self.to_matcher().bind(cb);
110    }
111
112    /// Sets the capabilities granted to clients matching this matcher.
113    ///
114    /// This leaks the matcher.
115    pub fn set_capabilities(self, caps: ClientCapabilities) {
116        self.to_matcher().set_capabilities(caps);
117    }
118
119    /// Sets the upper capability bounds for clients in sandboxes created by this client.
120    ///
121    /// This leaks the matcher.
122    pub fn set_sandbox_bounding_capabilities(self, caps: ClientCapabilities) {
123        self.to_matcher().set_sandbox_bounding_capabilities(caps);
124    }
125}
126
127impl ClientMatcher {
128    /// Destroys the matcher.
129    ///
130    /// Any bound callback will no longer be executed.
131    pub fn destroy(self) {
132        get!().destroy_client_matcher(self);
133    }
134
135    /// Sets a function to execute when the criterion matches a client.
136    ///
137    /// Replaces any already bound callback.
138    pub fn bind<F: FnMut(MatchedClient) + 'static>(self, cb: F) {
139        get!().set_client_matcher_handler(self, cb);
140    }
141
142    /// Sets the capabilities granted to clients matching this matcher.
143    ///
144    /// If multiple matchers match a client, the capabilities are added.
145    ///
146    /// If no matcher matches a client, it is granted the default capabilities depending
147    /// on whether it's sandboxed or not. If it is not sandboxed, it is granted the
148    /// capabilities [`CC_LAYER_SHELL`] and [`CC_DRM_LEASE`]. Otherwise it is granted the
149    /// capability [`CC_DRM_LEASE`].
150    ///
151    /// Regardless of any capabilities set through this function, the capabilities of the
152    /// client can never exceed its bounding capabilities.
153    pub fn set_capabilities(self, caps: ClientCapabilities) {
154        get!().set_client_matcher_capabilities(self, caps);
155    }
156
157    /// Sets the upper capability bounds for clients in sandboxes created by this client.
158    ///
159    /// If multiple matchers match a client, the capabilities are added.
160    ///
161    /// If no matcher matches a client, the bounding capabilities for sandboxes depend on
162    /// whether the client is itself sandboxed. If it is sandboxed, the bounding
163    /// capabilities are the effective capabilities of the client. Otherwise the bounding
164    /// capabilities are all capabilities.
165    ///
166    /// Regardless of any capabilities set through this function, the capabilities set
167    /// through this function can never exceed the client's bounding capabilities.
168    pub fn set_sandbox_bounding_capabilities(self, caps: ClientCapabilities) {
169        get!().set_client_matcher_bounding_capabilities(self, caps);
170    }
171}
172
173impl MatchedClient {
174    /// Returns the client that matched.
175    pub fn client(self) -> Client {
176        self.client
177    }
178
179    /// Returns the matcher.
180    pub fn matcher(self) -> ClientMatcher {
181        self.matcher
182    }
183
184    /// Latches a function to be executed when the client no longer matches the criteria.
185    pub fn latch<F: FnOnce() + 'static>(self, cb: F) {
186        get!().set_client_matcher_latch_handler(self.matcher, self.client, cb);
187    }
188}
189
190impl Deref for MatchedClient {
191    type Target = Client;
192
193    fn deref(&self) -> &Self::Target {
194        &self.client
195    }
196}
197
198bitflags! {
199    /// Capabilities granted to a client.
200    #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
201    pub struct ClientCapabilities(pub u64) {
202        /// Grants access to the `ext_data_control_manager_v1` and
203        /// `zwlr_data_control_manager_v1` globals.
204        pub const CC_DATA_CONTROL             = 1 << 0,
205        /// Grants access to the `zwp_virtual_keyboard_manager_v1` global.
206        pub const CC_VIRTUAL_KEYBOARD         = 1 << 1,
207        /// Grants access to the `ext_foreign_toplevel_list_v1` global.
208        pub const CC_FOREIGN_TOPLEVEL_LIST    = 1 << 2,
209        /// Grants access to the `ext_idle_notifier_v1` global.
210        pub const CC_IDLE_NOTIFIER            = 1 << 3,
211        /// Grants access to the `ext_session_lock_manager_v1` global.
212        pub const CC_SESSION_LOCK             = 1 << 4,
213        /// Grants access to the `zwlr_layer_shell_v1` global.
214        pub const CC_LAYER_SHELL              = 1 << 6,
215        /// Grants access to the `ext_image_copy_capture_manager_v1` and
216        /// `zwlr_screencopy_manager_v1` globals.
217        pub const CC_SCREENCOPY               = 1 << 7,
218        /// Grants access to the `ext_transient_seat_manager_v1` global.
219        pub const CC_SEAT_MANAGER             = 1 << 8,
220        /// Grants access to the `wp_drm_lease_device_v1` global.
221        pub const CC_DRM_LEASE                = 1 << 9,
222        /// Grants access to the `zwp_input_method_manager_v2` global.
223        pub const CC_INPUT_METHOD             = 1 << 10,
224        /// Grants access to the `ext_workspace_manager_v1` global.
225        pub const CC_WORKSPACE_MANAGER        = 1 << 11,
226        /// Grants access to the `zwlr_foreign_toplevel_manager_v1` global.
227        pub const CC_FOREIGN_TOPLEVEL_MANAGER = 1 << 12,
228        /// Grants access to the `zwlr_output_manager_v1` global.
229        pub const CC_HEAD_MANAGER             = 1 << 13,
230        /// Grants access to the `zwlr_gamma_control_manager_v1` global.
231        pub const CC_GAMMA_CONTROL_MANAGER    = 1 << 14,
232        /// Grants access to the `zwlr_virtual_pointer_manager_v1` global.
233        pub const CC_VIRTUAL_POINTER          = 1 << 15,
234    }
235}