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