jay_config/window.rs
1//! Tools for inspecting and manipulating windows.
2
3use {
4 crate::{
5 Axis, Direction, Workspace,
6 client::{Client, ClientCriterion},
7 input::Seat,
8 },
9 serde::{Deserialize, Serialize},
10 std::ops::Deref,
11};
12
13/// A toplevel window.
14///
15/// A toplevel window is anything that can be stored within a container tile or within a
16/// floating window.
17///
18/// There are currently four types of windows:
19///
20/// - Containers
21/// - Placeholders that take the place of a window when it goes fullscreen
22/// - XDG toplevels
23/// - X windows
24///
25/// You can find out the type of a window by using the [`Window::type_`] function.
26#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
27pub struct Window(pub u64);
28
29bitflags! {
30 /// The type of a window.
31 #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
32 pub struct WindowType(pub u64) {
33 /// A container.
34 pub const CONTAINER = 1 << 0,
35 /// A placeholder.
36 pub const PLACEHOLDER = 1 << 1,
37 /// An XDG toplevel.
38 pub const XDG_TOPLEVEL = 1 << 2,
39 /// An X window.
40 pub const X_WINDOW = 1 << 3,
41 }
42}
43
44bitflags! {
45 /// The content type of a window.
46 #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
47 pub struct ContentType(pub u64) {
48 /// No content type.
49 pub const NO_CONTENT_TYPE = 1 << 0,
50 /// Photo content type.
51 pub const PHOTO_CONTENT = 1 << 1,
52 /// Video content type.
53 pub const VIDEO_CONTENT = 1 << 2,
54 /// Game content type.
55 pub const GAME_CONTENT = 1 << 3,
56 }
57}
58
59/// The tile state of a window.
60#[non_exhaustive]
61#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
62pub enum TileState {
63 /// The window is tiled.
64 Tiled,
65 /// The window is floating.
66 Floating,
67}
68
69/// A window created by a client.
70///
71/// This is the same as `XDG_TOPLEVEL | X_WINDOW`.
72pub const CLIENT_WINDOW: WindowType = WindowType(XDG_TOPLEVEL.0 | X_WINDOW.0);
73
74impl Window {
75 /// Returns whether the window exists.
76 pub fn exists(self) -> bool {
77 self.0 != 0 && get!(false).window_exists(self)
78 }
79
80 /// Returns whether the window does not exist.
81 ///
82 /// This is a shorthand for `!self.exists()`.
83 pub fn does_not_exist(self) -> bool {
84 !self.exists()
85 }
86
87 /// Returns the client of the window.
88 ///
89 /// If the window does not have a client, [`Client::exists`] return false.
90 pub fn client(self) -> Client {
91 get!(Client(0)).window_client(self)
92 }
93
94 /// Returns the title of the window.
95 pub fn title(self) -> String {
96 get!().window_title(self)
97 }
98
99 /// Returns the type of the window.
100 pub fn type_(self) -> WindowType {
101 get!(WindowType(0)).window_type(self)
102 }
103
104 /// Returns the content type of the window.
105 pub fn content_type(self) -> ContentType {
106 get!(ContentType(0)).content_type(self)
107 }
108
109 /// Returns the identifier of the window.
110 ///
111 /// This is the identifier used in the `ext-foreign-toplevel-list-v1` protocol.
112 pub fn id(self) -> String {
113 get!().window_id(self)
114 }
115
116 /// Returns whether this window is visible.
117 pub fn is_visible(self) -> bool {
118 get!().window_is_visible(self)
119 }
120
121 /// Returns the parent of this window.
122 ///
123 /// If this window has no parent, [`Window::exists`] returns false.
124 pub fn parent(self) -> Window {
125 get!(Window(0)).window_parent(self)
126 }
127
128 /// Returns the children of this window.
129 ///
130 /// Only containers have children.
131 pub fn children(self) -> Vec<Window> {
132 get!().window_children(self)
133 }
134
135 /// Moves the window in the specified direction.
136 pub fn move_(self, direction: Direction) {
137 get!().window_move(self, direction)
138 }
139
140 /// Returns whether the parent-container of the window is in mono-mode.
141 pub fn mono(self) -> bool {
142 get!(false).window_mono(self)
143 }
144
145 /// Sets whether the parent-container of the window is in mono-mode.
146 pub fn set_mono(self, mono: bool) {
147 get!().set_window_mono(self, mono)
148 }
149
150 /// Toggles whether the parent-container of the window is in mono-mode.
151 pub fn toggle_mono(self) {
152 self.set_mono(!self.mono());
153 }
154
155 /// Returns the split axis of the parent-container of the window.
156 pub fn split(self) -> Axis {
157 get!(Axis::Horizontal).window_split(self)
158 }
159
160 /// Sets the split axis of the parent-container of the window.
161 pub fn set_split(self, axis: Axis) {
162 get!().set_window_split(self, axis)
163 }
164
165 /// Toggles the split axis of the parent-container of the window.
166 pub fn toggle_split(self) {
167 self.set_split(self.split().other());
168 }
169
170 /// Creates a new container with the specified split in place of the window.
171 pub fn create_split(self, axis: Axis) {
172 get!().create_window_split(self, axis);
173 }
174
175 /// Requests the window to be closed.
176 pub fn close(self) {
177 get!().close_window(self);
178 }
179
180 /// Returns whether the window is floating.
181 pub fn floating(self) -> bool {
182 get!().get_window_floating(self)
183 }
184 /// Sets whether the window is floating.
185 pub fn set_floating(self, floating: bool) {
186 get!().set_window_floating(self, floating);
187 }
188
189 /// Toggles whether the window is floating.
190 ///
191 /// You can do the same by double-clicking on the header.
192 pub fn toggle_floating(self) {
193 self.set_floating(!self.floating());
194 }
195
196 /// Returns the workspace that this window belongs to.
197 ///
198 /// If no such workspace exists, `exists` returns `false` for the returned workspace.
199 pub fn workspace(self) -> Workspace {
200 get!(Workspace(0)).get_window_workspace(self)
201 }
202
203 /// Moves the window to the workspace.
204 pub fn set_workspace(self, workspace: Workspace) {
205 get!().set_window_workspace(self, workspace)
206 }
207
208 /// Toggles whether the currently focused window is fullscreen.
209 pub fn toggle_fullscreen(self) {
210 self.set_fullscreen(!self.fullscreen())
211 }
212 /// Returns whether the window is fullscreen.
213 pub fn fullscreen(self) -> bool {
214 get!(false).get_window_fullscreen(self)
215 }
216
217 /// Sets whether the window is fullscreen.
218 pub fn set_fullscreen(self, fullscreen: bool) {
219 get!().set_window_fullscreen(self, fullscreen)
220 }
221
222 /// Gets whether the window is pinned.
223 ///
224 /// If a floating window is pinned, it will stay visible even when switching to a
225 /// different workspace.
226 pub fn float_pinned(self) -> bool {
227 get!().get_window_pinned(self)
228 }
229
230 /// Sets whether the window is pinned.
231 pub fn set_float_pinned(self, pinned: bool) {
232 get!().set_window_pinned(self, pinned);
233 }
234
235 /// Toggles whether the window is pinned.
236 pub fn toggle_float_pinned(self) {
237 self.set_float_pinned(!self.float_pinned());
238 }
239}
240
241/// A window matcher.
242#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
243pub struct WindowMatcher(pub u64);
244
245/// A matched window.
246#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
247pub struct MatchedWindow {
248 pub(crate) matcher: WindowMatcher,
249 pub(crate) window: Window,
250}
251
252/// A criterion for matching a window.
253#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
254#[non_exhaustive]
255pub enum WindowCriterion<'a> {
256 /// Matches if the contained matcher matches.
257 Matcher(WindowMatcher),
258 /// Matches if the contained criterion does not match.
259 Not(&'a WindowCriterion<'a>),
260 /// Matches if the window has one of the types.
261 Types(WindowType),
262 /// Matches if all of the contained criteria match.
263 All(&'a [WindowCriterion<'a>]),
264 /// Matches if any of the contained criteria match.
265 Any(&'a [WindowCriterion<'a>]),
266 /// Matches if an exact number of the contained criteria match.
267 Exactly(usize, &'a [WindowCriterion<'a>]),
268 /// Matches if the window's client matches the client criterion.
269 Client(&'a ClientCriterion<'a>),
270 /// Matches the title of the window verbatim.
271 Title(&'a str),
272 /// Matches the title of the window with a regular expression.
273 TitleRegex(&'a str),
274 /// Matches the app-id of the window verbatim.
275 AppId(&'a str),
276 /// Matches the app-id of the window with a regular expression.
277 AppIdRegex(&'a str),
278 /// Matches if the window is floating.
279 Floating,
280 /// Matches if the window is visible.
281 Visible,
282 /// Matches if the window has the urgency flag set.
283 Urgent,
284 /// Matches if the window has the keyboard focus of the seat.
285 Focus(Seat),
286 /// Matches if the window is fullscreen.
287 Fullscreen,
288 /// Matches if the window has/hasn't just been mapped.
289 ///
290 /// This is true for one iteration of the compositor's main loop immediately after the
291 /// window has been mapped.
292 JustMapped,
293 /// Matches the toplevel-tag of the window verbatim.
294 Tag(&'a str),
295 /// Matches the toplevel-tag of the window with a regular expression.
296 TagRegex(&'a str),
297 /// Matches the X class of the window verbatim.
298 XClass(&'a str),
299 /// Matches the X class of the window with a regular expression.
300 XClassRegex(&'a str),
301 /// Matches the X instance of the window verbatim.
302 XInstance(&'a str),
303 /// Matches the X instance of the window with a regular expression.
304 XInstanceRegex(&'a str),
305 /// Matches the X role of the window verbatim.
306 XRole(&'a str),
307 /// Matches the X role of the window with a regular expression.
308 XRoleRegex(&'a str),
309 /// Matches the workspace the window.
310 Workspace(Workspace),
311 /// Matches the workspace name of the window verbatim.
312 WorkspaceName(&'a str),
313 /// Matches the workspace name of the window with a regular expression.
314 WorkspaceNameRegex(&'a str),
315 /// Matches if the window has one of the content types.
316 ContentTypes(ContentType),
317}
318
319impl WindowCriterion<'_> {
320 /// Converts the criterion to a matcher.
321 pub fn to_matcher(self) -> WindowMatcher {
322 get!(WindowMatcher(0)).create_window_matcher(self)
323 }
324
325 /// Binds a function to execute when the criterion matches a window.
326 ///
327 /// This leaks the matcher.
328 pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
329 self.to_matcher().bind(cb);
330 }
331
332 /// Sets whether newly mapped windows that match this criterion get the keyboard focus.
333 ///
334 /// If a window matches any criterion for which this is false, the window will not be
335 /// automatically focused.
336 ///
337 /// This leaks the matcher.
338 pub fn set_auto_focus(self, auto_focus: bool) {
339 self.to_matcher().set_auto_focus(auto_focus);
340 }
341
342 /// Sets whether newly mapped windows that match this matcher are mapped tiling or
343 /// floating.
344 ///
345 /// If multiple such window matchers match a window, the used tile state is
346 /// unspecified.
347 ///
348 /// This leaks the matcher.
349 pub fn set_initial_tile_state(self, tile_state: TileState) {
350 self.to_matcher().set_initial_tile_state(tile_state);
351 }
352}
353
354impl WindowMatcher {
355 /// Destroys the matcher.
356 ///
357 /// Any bound callback will no longer be executed.
358 pub fn destroy(self) {
359 get!().destroy_window_matcher(self);
360 }
361
362 /// Sets a function to execute when the criterion matches a window.
363 ///
364 /// Replaces any already bound callback.
365 pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
366 get!().set_window_matcher_handler(self, cb);
367 }
368
369 /// Sets whether newly mapped windows that match this matcher get the keyboard focus.
370 ///
371 /// If a window matches any matcher for which this is false, the window will not be
372 /// automatically focused.
373 pub fn set_auto_focus(self, auto_focus: bool) {
374 get!().set_window_matcher_auto_focus(self, auto_focus);
375 }
376
377 /// Sets whether newly mapped windows that match this matcher are mapped tiling or
378 /// floating.
379 ///
380 /// If multiple such window matchers match a window, the used tile state is
381 /// unspecified.
382 pub fn set_initial_tile_state(self, tile_state: TileState) {
383 get!().set_window_matcher_initial_tile_state(self, tile_state);
384 }
385}
386
387impl MatchedWindow {
388 /// Returns the window that matched.
389 pub fn window(self) -> Window {
390 self.window
391 }
392
393 /// Returns the matcher.
394 pub fn matcher(self) -> WindowMatcher {
395 self.matcher
396 }
397
398 /// Latches a function to be executed when the window no longer matches the criteria.
399 pub fn latch<F: FnOnce() + 'static>(self, cb: F) {
400 get!().set_window_matcher_latch_handler(self.matcher, self.window, cb);
401 }
402}
403
404impl Deref for MatchedWindow {
405 type Target = Window;
406
407 fn deref(&self) -> &Self::Target {
408 &self.window
409 }
410}