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}