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}