Skip to main content

jay_config/
window.rs

1//! Tools for inspecting and manipulating windows.
2
3use crate::_private::WindowThemeKind;
4use crate::Axis;
5use crate::ContainerTarget;
6use crate::Direction;
7use crate::RelativeAxis;
8use crate::Workspace;
9use crate::client::Client;
10use crate::client::ClientCriterion;
11use crate::input::Seat;
12use crate::theme::ContainerTheme;
13use crate::theme::ThemeOverrides;
14use crate::theme::WindowTheme;
15use jay_proc::PrivateEnum;
16use serde::Deserialize;
17use serde::Serialize;
18use std::ops::Deref;
19
20/// A toplevel window.
21///
22/// A toplevel window is anything that can be stored within a container tile or within a
23/// floating window.
24///
25/// There are currently four types of windows:
26///
27/// - Containers
28/// - Placeholders that take the place of a window when it goes fullscreen
29/// - XDG toplevels
30/// - X windows
31///
32/// You can find out the type of a window by using the [`Window::type_`] function.
33#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
34pub struct Window(pub u64);
35
36bitflags! {
37    /// The type of a window.
38    #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
39    pub struct WindowType(pub u64) {
40        /// A container.
41        pub const CONTAINER = 1 << 0,
42        /// A placeholder.
43        pub const PLACEHOLDER = 1 << 1,
44        /// An XDG toplevel.
45        pub const XDG_TOPLEVEL = 1 << 2,
46        /// An X window.
47        pub const X_WINDOW = 1 << 3,
48    }
49}
50
51bitflags! {
52    /// The content type of a window.
53    #[derive(Serialize, Deserialize, Copy, Clone, Hash, Eq, PartialEq)]
54    pub struct ContentType(pub u64) {
55        /// No content type.
56        pub const NO_CONTENT_TYPE = 1 << 0,
57        /// Photo content type.
58        pub const PHOTO_CONTENT = 1 << 1,
59        /// Video content type.
60        pub const VIDEO_CONTENT = 1 << 2,
61        /// Game content type.
62        pub const GAME_CONTENT = 1 << 3,
63    }
64}
65
66/// The tile state of a window.
67#[non_exhaustive]
68#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq, PrivateEnum)]
69pub enum TileState {
70    /// The window is tiled.
71    Tiled,
72    /// The window is floating.
73    Floating,
74}
75
76/// A window created by a client.
77///
78/// This is the same as `XDG_TOPLEVEL | X_WINDOW`.
79pub const CLIENT_WINDOW: WindowType = WindowType(XDG_TOPLEVEL.0 | X_WINDOW.0);
80
81impl Window {
82    /// Returns whether the window exists.
83    pub fn exists(self) -> bool {
84        self.0 != 0 && get!(false).window_exists(self)
85    }
86
87    /// Returns whether the window does not exist.
88    ///
89    /// This is a shorthand for `!self.exists()`.
90    pub fn does_not_exist(self) -> bool {
91        !self.exists()
92    }
93
94    /// Returns the client of the window.
95    ///
96    /// If the window does not have a client, [`Client::exists`] return false.
97    pub fn client(self) -> Client {
98        get!(Client(0)).window_client(self)
99    }
100
101    /// Returns the title of the window.
102    pub fn title(self) -> String {
103        get!().window_title(self)
104    }
105
106    /// Returns the type of the window.
107    pub fn type_(self) -> WindowType {
108        get!(WindowType(0)).window_type(self)
109    }
110
111    /// Returns the content type of the window.
112    pub fn content_type(self) -> ContentType {
113        get!(ContentType(0)).content_type(self)
114    }
115
116    /// Returns the identifier of the window.
117    ///
118    /// This is the identifier used in the `ext-foreign-toplevel-list-v1` protocol.
119    pub fn id(self) -> String {
120        get!().window_id(self)
121    }
122
123    /// Returns whether this window is visible.
124    pub fn is_visible(self) -> bool {
125        get!().window_is_visible(self)
126    }
127
128    /// Returns the parent of this window.
129    ///
130    /// If this window has no parent, [`Window::exists`] returns false.
131    pub fn parent(self) -> Window {
132        get!(Window(0)).window_parent(self)
133    }
134
135    /// Returns the children of this window.
136    ///
137    /// Only containers have children.
138    pub fn children(self) -> Vec<Window> {
139        get!().window_children(self)
140    }
141
142    /// Moves the window in the specified direction.
143    pub fn move_(self, direction: Direction) {
144        get!().window_move(self, direction)
145    }
146
147    /// Returns whether the parent-container of the window is in mono-mode.
148    pub fn mono(self) -> bool {
149        get!(false).window_mono(self)
150    }
151
152    /// Sets whether the parent-container of the window is in mono-mode.
153    pub fn set_mono(self, mono: bool) {
154        get!().set_window_mono(self, mono)
155    }
156
157    /// Toggles whether the parent-container of the window is in mono-mode.
158    pub fn toggle_mono(self) {
159        self.set_mono(!self.mono());
160    }
161
162    /// Returns the split axis of the parent-container of the window.
163    pub fn split(self) -> Axis {
164        get!(Axis::Horizontal).window_split(self)
165    }
166
167    /// Sets the split axis of the parent-container of the window.
168    pub fn set_split(self, axis: Axis) {
169        get!().set_window_split(self, axis)
170    }
171
172    /// Toggles the split axis of the parent-container of the window.
173    pub fn toggle_split(self) {
174        self.set_split(self.split().other());
175    }
176
177    /// Returns whether the target container of the window is in mono-mode.
178    pub fn container_mono(self, target: ContainerTarget) -> bool {
179        get!(false).window_container_mono(self, target.to_private())
180    }
181
182    /// Sets whether the target container of the window is in mono-mode.
183    pub fn set_container_mono(self, target: ContainerTarget, mono: bool) {
184        get!().set_window_container_mono(self, target.to_private(), mono)
185    }
186
187    /// Toggles whether the target container of the window is in mono-mode.
188    pub fn toggle_container_mono(self, target: ContainerTarget) {
189        self.set_container_mono(target, !self.container_mono(target));
190    }
191
192    /// Returns the split axis of the target container of the window.
193    pub fn container_split(self, target: ContainerTarget) -> Axis {
194        get!(Axis::Horizontal).window_container_split(self, target.to_private())
195    }
196
197    /// Sets the split axis of the target container of the window.
198    pub fn set_container_split(self, target: ContainerTarget, axis: Axis) {
199        get!().set_window_container_split(self, target.to_private(), axis)
200    }
201
202    /// Toggles the split axis of the target container of the window.
203    pub fn toggle_container_split(self, target: ContainerTarget) {
204        self.set_container_split(target, self.container_split(target).other());
205    }
206
207    /// Sets the split axis of the target container of the window relative to the
208    /// dimensions of that container.
209    pub fn set_container_split_relative(self, target: ContainerTarget, axis: RelativeAxis) {
210        get!().set_window_container_split_relative(self, target.to_private(), axis.to_private())
211    }
212
213    /// Creates a new container with the specified split in place of the window.
214    ///
215    /// If the window is the only child of its container and
216    /// [`set_split_reuses_container`](crate::set_split_reuses_container) is enabled, the
217    /// split axis of that container is changed instead.
218    pub fn create_split(self, axis: Axis) {
219        get!().create_window_split(self, axis);
220    }
221
222    /// Creates a new container in place of the window with a split relative to the
223    /// dimensions of that window.
224    ///
225    /// If the window is the only child of its container and
226    /// [`set_split_reuses_container`](crate::set_split_reuses_container) is enabled, the
227    /// split axis of that container is changed instead.
228    pub fn create_split_relative(self, axis: RelativeAxis) {
229        get!().create_window_split_relative(self, axis.to_private());
230    }
231
232    /// Requests the window to be closed.
233    pub fn close(self) {
234        get!().close_window(self);
235    }
236
237    /// Returns whether the window is floating.
238    pub fn floating(self) -> bool {
239        get!().get_window_floating(self)
240    }
241    /// Sets whether the window is floating.
242    pub fn set_floating(self, floating: bool) {
243        get!().set_window_floating(self, floating);
244    }
245
246    /// Toggles whether the window is floating.
247    ///
248    /// You can do the same by double-clicking on the header.
249    pub fn toggle_floating(self) {
250        self.set_floating(!self.floating());
251    }
252
253    /// Returns the workspace that this window belongs to.
254    ///
255    /// If no such workspace exists, `exists` returns `false` for the returned workspace.
256    pub fn workspace(self) -> Workspace {
257        get!(Workspace(0)).get_window_workspace(self)
258    }
259
260    /// Moves the window to the workspace.
261    pub fn set_workspace(self, workspace: Workspace) {
262        get!().set_window_workspace(self, workspace)
263    }
264
265    /// Toggles whether the currently focused window is fullscreen.
266    pub fn toggle_fullscreen(self) {
267        self.set_fullscreen(!self.fullscreen())
268    }
269    /// Returns whether the window is fullscreen.
270    pub fn fullscreen(self) -> bool {
271        get!(false).get_window_fullscreen(self)
272    }
273
274    /// Sets whether the window is fullscreen.
275    pub fn set_fullscreen(self, fullscreen: bool) {
276        get!().set_window_fullscreen(self, fullscreen)
277    }
278
279    /// Gets whether the window is pinned.
280    ///
281    /// If a floating window is pinned, it will stay visible even when switching to a
282    /// different workspace.
283    pub fn float_pinned(self) -> bool {
284        get!().get_window_pinned(self)
285    }
286
287    /// Sets whether the window is pinned.
288    pub fn set_float_pinned(self, pinned: bool) {
289        get!().set_window_pinned(self, pinned);
290    }
291
292    /// Toggles whether the window is pinned.
293    pub fn toggle_float_pinned(self) {
294        self.set_float_pinned(!self.float_pinned());
295    }
296
297    /// Resizes the window.
298    pub fn resize(self, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
299        get!().resize_window(self, dx1, dy1, dx2, dy2);
300    }
301
302    /// Returns the position of the window in the global compositor space.
303    ///
304    /// This value is only accurate for visible windows.
305    pub fn position(self) -> (i32, i32) {
306        let (x, y, _, _) = get!((0, 0)).get_window_position(self);
307        (x, y)
308    }
309
310    /// Returns the size of the window.
311    ///
312    /// This value is only accurate for visible windows.
313    pub fn size(self) -> (i32, i32) {
314        let (_, _, width, height) = get!((0, 0)).get_window_position(self);
315        (width, height)
316    }
317
318    /// Returns the theme overrides of this window.
319    ///
320    /// See [`WindowTheme`] for details.
321    pub fn theme(self) -> WindowTheme {
322        WindowTheme(ThemeOverrides {
323            window: self,
324            kind: WindowThemeKind::ParentTheme,
325        })
326    }
327
328    /// Returns the theme overrides of how this container decorates its children.
329    ///
330    /// This is only supported for containers.
331    ///
332    /// See [`ContainerTheme`] for details.
333    pub fn container_theme(self) -> ContainerTheme {
334        ContainerTheme(ThemeOverrides {
335            window: self,
336            kind: WindowThemeKind::SelfTheme,
337        })
338    }
339}
340
341/// A window matcher.
342#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
343pub struct WindowMatcher(pub u64);
344
345/// A matched window.
346#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
347pub struct MatchedWindow {
348    pub(crate) matcher: WindowMatcher,
349    pub(crate) window: Window,
350}
351
352/// A criterion for matching a window.
353#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
354#[non_exhaustive]
355pub enum WindowCriterion<'a> {
356    /// Matches if the contained matcher matches.
357    Matcher(WindowMatcher),
358    /// Matches if the contained criterion does not match.
359    Not(&'a WindowCriterion<'a>),
360    /// Matches if the window has one of the types.
361    Types(WindowType),
362    /// Matches if all of the contained criteria match.
363    All(&'a [WindowCriterion<'a>]),
364    /// Matches if any of the contained criteria match.
365    Any(&'a [WindowCriterion<'a>]),
366    /// Matches if an exact number of the contained criteria match.
367    Exactly(usize, &'a [WindowCriterion<'a>]),
368    /// Matches if the window's client matches the client criterion.
369    Client(&'a ClientCriterion<'a>),
370    /// Matches the title of the window verbatim.
371    Title(&'a str),
372    /// Matches the title of the window with a regular expression.
373    TitleRegex(&'a str),
374    /// Matches the app-id of the window verbatim.
375    AppId(&'a str),
376    /// Matches the app-id of the window with a regular expression.
377    AppIdRegex(&'a str),
378    /// Matches if the window is floating.
379    Floating,
380    /// Matches if the window is visible.
381    Visible,
382    /// Matches if the window has the urgency flag set.
383    Urgent,
384    /// Matches if the window has the keyboard focus of the seat.
385    Focus(Seat),
386    /// Matches if the window is fullscreen.
387    Fullscreen,
388    /// Matches if the window has/hasn't just been mapped.
389    ///
390    /// This is true for one iteration of the compositor's main loop immediately after the
391    /// window has been mapped.
392    JustMapped,
393    /// Matches the toplevel-tag of the window verbatim.
394    Tag(&'a str),
395    /// Matches the toplevel-tag of the window with a regular expression.
396    TagRegex(&'a str),
397    /// Matches the X class of the window verbatim.
398    XClass(&'a str),
399    /// Matches the X class of the window with a regular expression.
400    XClassRegex(&'a str),
401    /// Matches the X instance of the window verbatim.
402    XInstance(&'a str),
403    /// Matches the X instance of the window with a regular expression.
404    XInstanceRegex(&'a str),
405    /// Matches the X role of the window verbatim.
406    XRole(&'a str),
407    /// Matches the X role of the window with a regular expression.
408    XRoleRegex(&'a str),
409    /// Matches the workspace the window.
410    Workspace(Workspace),
411    /// Matches the workspace name of the window verbatim.
412    WorkspaceName(&'a str),
413    /// Matches the workspace name of the window with a regular expression.
414    WorkspaceNameRegex(&'a str),
415    /// Matches if the window has one of the content types.
416    ContentTypes(ContentType),
417    /// Matches if the window is the root container of a workspace.
418    IsWorkspaceContainer,
419}
420
421impl WindowCriterion<'_> {
422    /// Converts the criterion to a matcher.
423    pub fn to_matcher(self) -> WindowMatcher {
424        get!(WindowMatcher(0)).create_window_matcher(self)
425    }
426
427    /// Binds a function to execute when the criterion matches a window.
428    ///
429    /// This leaks the matcher.
430    pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
431        self.to_matcher().bind(cb);
432    }
433
434    /// Sets whether newly mapped windows that match this criterion get the keyboard focus.
435    ///
436    /// If a window matches any criterion for which this is false, the window will not be
437    /// automatically focused.
438    ///
439    /// This leaks the matcher.
440    pub fn set_auto_focus(self, auto_focus: bool) {
441        self.to_matcher().set_auto_focus(auto_focus);
442    }
443
444    /// Sets whether newly mapped windows that match this matcher are mapped tiling or
445    /// floating.
446    ///
447    /// If multiple such window matchers match a window, the used tile state is
448    /// unspecified.
449    ///
450    /// This leaks the matcher.
451    pub fn set_initial_tile_state(self, tile_state: TileState) {
452        self.to_matcher().set_initial_tile_state(tile_state);
453    }
454
455    /// Sets the size that matched windows have when they are initially mapped floating.
456    ///
457    /// This has no effect on windows that are initially mapped tiled, even if they later
458    /// become floating.
459    ///
460    /// If multiple such window matchers match a window, the used size is unspecified.
461    ///
462    /// This leaks the matcher.
463    pub fn set_initial_floating_size(self, width: i32, height: i32) {
464        self.to_matcher().set_initial_floating_size(width, height);
465    }
466
467    /// Sets the position that matched windows have when they are initially mapped
468    /// floating.
469    ///
470    /// The position is relative to the top-left corner of the output that the window is
471    /// mapped on and refers to the top-left corner of the window itself, excluding any
472    /// decorations. It is clamped so that the window remains at least partially visible
473    /// on that output.
474    ///
475    /// This has no effect on windows that are initially mapped tiled, even if they later
476    /// become floating.
477    ///
478    /// If multiple such window matchers match a window, the used position is unspecified.
479    ///
480    /// This leaks the matcher.
481    pub fn set_initial_floating_position(self, x: i32, y: i32) {
482        self.to_matcher().set_initial_floating_position(x, y);
483    }
484}
485
486impl WindowMatcher {
487    /// Destroys the matcher.
488    ///
489    /// Any bound callback will no longer be executed.
490    pub fn destroy(self) {
491        get!().destroy_window_matcher(self);
492    }
493
494    /// Sets a function to execute when the criterion matches a window.
495    ///
496    /// Replaces any already bound callback.
497    pub fn bind<F: FnMut(MatchedWindow) + 'static>(self, cb: F) {
498        get!().set_window_matcher_handler(self, cb);
499    }
500
501    /// Sets whether newly mapped windows that match this matcher get the keyboard focus.
502    ///
503    /// If a window matches any matcher for which this is false, the window will not be
504    /// automatically focused.
505    pub fn set_auto_focus(self, auto_focus: bool) {
506        get!().set_window_matcher_auto_focus(self, auto_focus);
507    }
508
509    /// Sets whether newly mapped windows that match this matcher are mapped tiling or
510    /// floating.
511    ///
512    /// If multiple such window matchers match a window, the used tile state is
513    /// unspecified.
514    pub fn set_initial_tile_state(self, tile_state: TileState) {
515        get!().set_window_matcher_initial_tile_state(self, tile_state.to_private());
516    }
517
518    /// Sets the size that matched windows have when they are initially mapped floating.
519    ///
520    /// This has no effect on windows that are initially mapped tiled, even if they later
521    /// become floating.
522    ///
523    /// If multiple such window matchers match a window, the used size is unspecified.
524    pub fn set_initial_floating_size(self, width: i32, height: i32) {
525        get!().set_window_matcher_initial_floating_size(self, width, height);
526    }
527
528    /// Sets the position that matched windows have when they are initially mapped
529    /// floating.
530    ///
531    /// The position is relative to the top-left corner of the output that the window is
532    /// mapped on and refers to the top-left corner of the window itself, excluding any
533    /// decorations. It is clamped so that the window remains at least partially visible
534    /// on that output.
535    ///
536    /// This has no effect on windows that are initially mapped tiled, even if they later
537    /// become floating.
538    ///
539    /// If multiple such window matchers match a window, the used position is unspecified.
540    pub fn set_initial_floating_position(self, x: i32, y: i32) {
541        get!().set_window_matcher_initial_floating_position(self, x, y);
542    }
543}
544
545impl MatchedWindow {
546    /// Returns the window that matched.
547    pub fn window(self) -> Window {
548        self.window
549    }
550
551    /// Returns the matcher.
552    pub fn matcher(self) -> WindowMatcher {
553        self.matcher
554    }
555
556    /// Latches a function to be executed when the window no longer matches the criteria.
557    pub fn latch<F: FnOnce() + 'static>(self, cb: F) {
558        get!().set_window_matcher_latch_handler(self.matcher, self.window, cb);
559    }
560}
561
562impl Deref for MatchedWindow {
563    type Target = Window;
564
565    fn deref(&self) -> &Self::Target {
566        &self.window
567    }
568}