Skip to main content

jay_config/
client.rs

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