Skip to main content

jay_config/
lib.rs

1//! This crate allows you to configure the Jay compositor.
2//!
3//! A minimal example configuration looks as follows:
4//!
5//! ```rust
6//! use jay_config::{config, quit, reload};
7//! use jay_config::input::get_default_seat;
8//! use jay_config::keyboard::mods::ALT;
9//! use jay_config::keyboard::syms::{SYM_q, SYM_r};
10//!
11//! fn configure() {
12//!     let seat = get_default_seat();
13//!     // Create a key binding to exit the compositor.
14//!     seat.bind(ALT | SYM_q, || quit());
15//!     // Reload the configuration.
16//!     seat.bind(ALT | SYM_r, || reload());
17//! }
18//!
19//! config!(configure);
20//! ```
21//!
22//! You should configure your crate to be compiled as a shared library:
23//!
24//! ```toml
25//! [lib]
26//! crate-type = ["cdylib"]
27//! ```
28//!
29//! After compiling it, copy the shared library to `$HOME/.config/jay/config.so` and restart
30//! the compositor. It should then use your configuration file.
31//!
32//! Note that you do not have to restart the compositor every time you want to reload your
33//! configuration afterwards. Instead, simply invoke the [`reload`] function via a shortcut.
34
35#![allow(
36    clippy::zero_prefixed_literal,
37    clippy::manual_range_contains,
38    clippy::uninlined_format_args,
39    clippy::len_zero,
40    clippy::single_char_pattern,
41    clippy::single_char_add_str,
42    clippy::single_match
43)]
44#![warn(unsafe_op_in_unsafe_fn)]
45
46#[expect(unused_imports)]
47use crate::input::Seat;
48
49use {
50    crate::{
51        _private::ipc::WorkspaceSource, keyboard::ModifiedKeySym, video::Connector, window::Window,
52    },
53    serde::{Deserialize, Serialize},
54    std::{
55        fmt::{Debug, Display, Formatter},
56        time::Duration,
57    },
58};
59
60#[macro_use]
61mod macros;
62#[doc(hidden)]
63pub mod _private;
64pub mod client;
65pub mod embedded;
66pub mod exec;
67pub mod input;
68pub mod io;
69pub mod keyboard;
70pub mod logging;
71pub mod status;
72pub mod tasks;
73pub mod theme;
74pub mod timer;
75pub mod video;
76pub mod window;
77pub mod workspace;
78pub mod xwayland;
79
80/// A planar direction.
81#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
82pub enum Direction {
83    Left,
84    Down,
85    Up,
86    Right,
87}
88
89/// A planar axis.
90#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
91pub enum Axis {
92    Horizontal,
93    Vertical,
94}
95
96impl Axis {
97    /// Returns the axis orthogonal to `self`.
98    pub fn other(self) -> Self {
99        match self {
100            Self::Horizontal => Self::Vertical,
101            Self::Vertical => Self::Horizontal,
102        }
103    }
104}
105
106/// Exits the compositor.
107pub fn quit() {
108    get!().quit()
109}
110
111/// Switches to a different VT.
112pub fn switch_to_vt(n: u32) {
113    get!().switch_to_vt(n)
114}
115
116/// Reloads the configuration.
117///
118/// If the configuration cannot be reloaded, this function has no effect.
119pub fn reload() {
120    get!().reload()
121}
122
123/// Returns whether this execution of the configuration function is due to a reload.
124///
125/// This can be used to decide whether the configuration should auto-start programs.
126pub fn is_reload() -> bool {
127    get!(false).is_reload()
128}
129
130/// Sets whether new workspaces are captured by default.
131///
132/// The default is `true`.
133pub fn set_default_workspace_capture(capture: bool) {
134    get!().set_default_workspace_capture(capture)
135}
136
137/// Returns whether new workspaces are captured by default.
138pub fn get_default_workspace_capture() -> bool {
139    get!(true).get_default_workspace_capture()
140}
141
142/// Toggles whether new workspaces are captured by default.
143pub fn toggle_default_workspace_capture() {
144    let get = get!();
145    get.set_default_workspace_capture(!get.get_default_workspace_capture());
146}
147
148/// A workspace.
149#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
150pub struct Workspace(pub u64);
151
152impl Workspace {
153    /// Returns whether this workspace existed at the time `Seat::get_workspace` was called.
154    pub fn exists(self) -> bool {
155        self.0 != 0
156    }
157
158    /// Sets whether the workspaces is captured.
159    ///
160    /// The default is determined by `set_default_workspace_capture`.
161    pub fn set_capture(self, capture: bool) {
162        get!().set_workspace_capture(self, capture)
163    }
164
165    /// Returns whether the workspaces is captured.
166    pub fn get_capture(self) -> bool {
167        get!(true).get_workspace_capture(self)
168    }
169
170    /// Toggles whether the workspaces is captured.
171    pub fn toggle_capture(self) {
172        let get = get!();
173        get.set_workspace_capture(self, !get.get_workspace_capture(self));
174    }
175
176    /// Moves this workspace to another output.
177    ///
178    /// This has no effect if the workspace is not currently being shown.
179    pub fn move_to_output(self, output: Connector) {
180        get!().move_to_output(WorkspaceSource::Explicit(self), output);
181    }
182
183    /// Returns the root container of this workspace.
184    ///
185    /// If no such container exists, [`Window::exists`] returns false.
186    pub fn window(self) -> Window {
187        get!(Window(0)).get_workspace_window(self)
188    }
189
190    /// Returns the connector that contains this workspace.
191    ///
192    /// If no such connector exists, [`Connector::exists`] returns false.
193    pub fn connector(self) -> Connector {
194        get!(Connector(0)).get_workspace_connector(self)
195    }
196}
197
198/// Returns the workspace with the given name.
199///
200/// Workspaces are identified by their name. Calling this function alone does not create the
201/// workspace if it doesn't already exist.
202pub fn get_workspace(name: &str) -> Workspace {
203    get!(Workspace(0)).get_workspace(name)
204}
205
206/// A PCI ID.
207///
208/// PCI IDs can be used to identify a hardware component. See the Debian [documentation][pci].
209///
210/// [pci]: https://wiki.debian.org/HowToIdentifyADevice/PCI
211#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, Eq, PartialEq, Default)]
212pub struct PciId {
213    pub vendor: u32,
214    pub model: u32,
215}
216
217impl Display for PciId {
218    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
219        write!(f, "{:04x}:{:04x}", self.vendor, self.model)
220    }
221}
222
223/// Sets the callback to be called when the display goes idle.
224pub fn on_idle<F: FnMut() + 'static>(f: F) {
225    get!().on_idle(f)
226}
227
228/// Sets the callback to be called when all devices have been enumerated.
229///
230/// This callback is only invoked once during the lifetime of the compositor. This is a
231/// good place to select the DRM device used for rendering.
232pub fn on_devices_enumerated<F: FnOnce() + 'static>(f: F) {
233    get!().on_devices_enumerated(f)
234}
235
236/// Returns the Jay config directory.
237pub fn config_dir() -> String {
238    get!().config_dir()
239}
240
241/// Returns all visible workspaces.
242pub fn workspaces() -> Vec<Workspace> {
243    get!().workspaces()
244}
245
246/// Configures the idle timeout.
247///
248/// `None` disables the timeout.
249///
250/// The default is 10 minutes.
251pub fn set_idle(timeout: Option<Duration>) {
252    get!().set_idle(timeout.unwrap_or_default())
253}
254
255/// Configures the idle grace period.
256///
257/// The grace period starts after the idle timeout expires. During the grace period, the
258/// screen goes black but the displays are not yet disabled and the idle callback (set
259/// with [`on_idle`]) is not yet called. This is a purely visual effect to inform the user
260/// that the machine will soon go idle.
261///
262/// The default is 5 seconds.
263pub fn set_idle_grace_period(timeout: Duration) {
264    get!().set_idle_grace_period(timeout)
265}
266
267/// Enables or disables explicit sync.
268///
269/// Calling this after the compositor has started has no effect.
270///
271/// The default is `true`.
272pub fn set_explicit_sync_enabled(enabled: bool) {
273    get!().set_explicit_sync_enabled(enabled);
274}
275
276/// Enables or disables dragging of tiles and workspaces.
277///
278/// The default is `true`.
279pub fn set_ui_drag_enabled(enabled: bool) {
280    get!().set_ui_drag_enabled(enabled);
281}
282
283/// Sets the distance at which ui dragging starts.
284///
285/// The default is `10`.
286pub fn set_ui_drag_threshold(threshold: i32) {
287    get!().set_ui_drag_threshold(threshold);
288}
289
290/// Enables or disables the color-management protocol.
291///
292/// The default is `false`.
293///
294/// Affected applications must be restarted for this to take effect.
295pub fn set_color_management_enabled(enabled: bool) {
296    get!().set_color_management_enabled(enabled);
297}
298
299/// Sets whether floating windows are shown above fullscreen windows.
300///
301/// The default is `false`.
302pub fn set_float_above_fullscreen(above: bool) {
303    get!().set_float_above_fullscreen(above);
304}
305
306/// Gets whether floating windows are shown above fullscreen windows.
307pub fn get_float_above_fullscreen() -> bool {
308    get!().get_float_above_fullscreen()
309}
310
311/// Toggles whether floating windows are shown above fullscreen windows.
312///
313/// The default is `false`.
314pub fn toggle_float_above_fullscreen() {
315    set_float_above_fullscreen(!get_float_above_fullscreen())
316}
317
318/// Sets whether floating windows always show a pin icon.
319///
320/// Clicking on the pin icon toggles the pin mode. See [`Seat::toggle_float_pinned`].
321///
322/// The icon is always shown if the window is pinned. This setting only affects unpinned
323/// windows.
324pub fn set_show_float_pin_icon(show: bool) {
325    get!().set_show_float_pin_icon(show);
326}
327
328/// Sets whether the built-in bar is shown.
329///
330/// The default is `true`.
331pub fn set_show_bar(show: bool) {
332    get!().set_show_bar(show)
333}
334
335/// Returns whether the built-in bar is shown.
336pub fn get_show_bar() -> bool {
337    get!(true).get_show_bar()
338}
339
340/// Toggles whether the built-in bar is shown.
341pub fn toggle_show_bar() {
342    let get = get!();
343    get.set_show_bar(!get.get_show_bar());
344}
345
346/// Sets whether title bars on windows are shown.
347///
348/// The default is `true`.
349pub fn set_show_titles(show: bool) {
350    get!().set_show_titles(show)
351}
352
353/// Returns whether title bars on windows are shown.
354pub fn get_show_titles() -> bool {
355    get!(true).get_show_titles()
356}
357
358/// Toggles whether title bars on windows are shown.
359pub fn toggle_show_titles() {
360    let get = get!();
361    get.set_show_titles(!get.get_show_titles());
362}
363
364/// Sets a callback to run when this config is unloaded.
365///
366/// Only one callback can be set at a time. If another callback is already set, it will be
367/// dropped without being run.
368///
369/// This function can be used to terminate threads and clear reference cycles.
370pub fn on_unload(f: impl FnOnce() + 'static) {
371    get!().on_unload(f);
372}
373
374/// Enables or disables middle-click pasting.
375///
376/// This has no effect on applications that are already running.
377///
378/// The default is `true`.
379#[doc(alias("primary-selection", "primary_selection"))]
380pub fn set_middle_click_paste_enabled(enabled: bool) {
381    get!().set_middle_click_paste_enabled(enabled);
382}
383
384/// Opens the control center.
385pub fn open_control_center() {
386    get!().open_control_center();
387}