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 /// Resizes the window.
241 pub fn resize(self, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
242 get!().resize_window(self, dx1, dy1, dx2, dy2);
243 }
244}
245
246/// A window matcher.
247#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
248pub struct WindowMatcher(pub u64);
249
250/// A matched window.
251#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
252pub struct MatchedWindow {
253 pub(crate) matcher: WindowMatcher,
254 pub(crate) window: Window,
255}
256
257/// A criterion for matching a window.
258#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
259#[non_exhaustive]
260pub enum WindowCriterion<'a> {
261 /// Matches if the contained matcher matches.
262 Matcher(WindowMatcher),
263 /// Matches if the contained criterion does not match.
264 Not(&'a WindowCriterion<'a>),
265 /// Matches if the window has one of the types.
266 Types(WindowType),
267 /// Matches if all of the contained criteria match.
268 All(&'a [WindowCriterion<'a>]),
269 /// Matches if any of the contained criteria match.
270 Any(&'a [WindowCriterion<'a>]),
271 /// Matches if an exact number of the contained criteria match.
272 Exactly(usize, &'a [WindowCriterion<'a>]),
273 /// Matches if the window's client matches the client criterion.
274 Client(&'a ClientCriterion<'a>),
275 /// Matches the title of the window verbatim.
276 Title(&'a str),
277 /// Matches the title of the window with a regular expression.
278 TitleRegex(&'a str),
279 /// Matches the app-id of the window verbatim.
280 AppId(&'a str),
281 /// Matches the app-id of the window with a regular expression.
282 AppIdRegex(&'a str),
283 /// Matches if the window is floating.
284 Floating,
285 /// Matches if the window is visible.
286 Visible,
287 /// Matches if the window has the urgency flag set.
288 Urgent,
289 /// Matches if the window has the keyboard focus of the seat.
290 Focus(Seat),
291 /// Matches if the window is fullscreen.
292 Fullscreen,
293 /// Matches if the window has/hasn't just been mapped.
294 ///
295 /// This is true for one iteration of the compositor's main loop immediately after the
296 /// window has been mapped.
297 JustMapped,
298 /// Matches the toplevel-tag of the window verbatim.
299 Tag(&'a str),
300 /// Matches the toplevel-tag of the window with a regular expression.
301 TagRegex(&'a str),
302 /// Matches the X class of the window verbatim.
303 XClass(&'a str),
304 /// Matches the X class of the window with a regular expression.
305 XClassRegex(&'a str),
306 /// Matches the X instance of the window verbatim.
307 XInstance(&'a str),
308 /// Matches the X instance of the window with a regular expression.
309 XInstanceRegex(&'a str),
310 /// Matches the X role of the window verbatim.
311 XRole(&'a str),
312 /// Matches the X role of the window with a regular expression.
313 XRoleRegex(&'a str),
314 /// Matches the workspace the window.
315 Workspace(Workspace),
316 /// Matches the workspace name of the window verbatim.
317 WorkspaceName(&'a str),
318 /// Matches the workspace name of the window with a regular expression.
319 WorkspaceNameRegex(&'a str),
320 /// Matches if the window has one of the content types.
321 ContentTypes(ContentType),
322}
323
324impl WindowCriterion<'_> {
325 /// Converts the criterion to a matcher.
326 pub fn to_matcher(self) -> WindowMatcher {
327 get!(WindowMatcher(0)).create_window_matcher(self)
328 }
329
330 /// Binds a function to execute when the criterion matches a window.
331 ///
332 /// This leaks the matcher.
333 pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
334 self.to_matcher().bind(cb);
335 }
336
337 /// Sets whether newly mapped windows that match this criterion get the keyboard focus.
338 ///
339 /// If a window matches any criterion for which this is false, the window will not be
340 /// automatically focused.
341 ///
342 /// This leaks the matcher.
343 pub fn set_auto_focus(self, auto_focus: bool) {
344 self.to_matcher().set_auto_focus(auto_focus);
345 }
346
347 /// Sets whether newly mapped windows that match this matcher are mapped tiling or
348 /// floating.
349 ///
350 /// If multiple such window matchers match a window, the used tile state is
351 /// unspecified.
352 ///
353 /// This leaks the matcher.
354 pub fn set_initial_tile_state(self, tile_state: TileState) {
355 self.to_matcher().set_initial_tile_state(tile_state);
356 }
357}
358
359impl WindowMatcher {
360 /// Destroys the matcher.
361 ///
362 /// Any bound callback will no longer be executed.
363 pub fn destroy(self) {
364 get!().destroy_window_matcher(self);
365 }
366
367 /// Sets a function to execute when the criterion matches a window.
368 ///
369 /// Replaces any already bound callback.
370 pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
371 get!().set_window_matcher_handler(self, cb);
372 }
373
374 /// Sets whether newly mapped windows that match this matcher get the keyboard focus.
375 ///
376 /// If a window matches any matcher for which this is false, the window will not be
377 /// automatically focused.
378 pub fn set_auto_focus(self, auto_focus: bool) {
379 get!().set_window_matcher_auto_focus(self, auto_focus);
380 }
381
382 /// Sets whether newly mapped windows that match this matcher are mapped tiling or
383 /// floating.
384 ///
385 /// If multiple such window matchers match a window, the used tile state is
386 /// unspecified.
387 pub fn set_initial_tile_state(self, tile_state: TileState) {
388 get!().set_window_matcher_initial_tile_state(self, tile_state);
389 }
390}
391
392impl MatchedWindow {
393 /// Returns the window that matched.
394 pub fn window(self) -> Window {
395 self.window
396 }
397
398 /// Returns the matcher.
399 pub fn matcher(self) -> WindowMatcher {
400 self.matcher
401 }
402
403 /// Latches a function to be executed when the window no longer matches the criteria.
404 pub fn latch<F: FnOnce() + 'static>(self, cb: F) {
405 get!().set_window_matcher_latch_handler(self.matcher, self.window, cb);
406 }
407}
408
409impl Deref for MatchedWindow {
410 type Target = Window;
411
412 fn deref(&self) -> &Self::Target {
413 &self.window
414 }
415}