jay_config/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! This crate allows you to configure the Jay compositor.
//!
//! A minimal example configuration looks as follows:
//!
//! ```rust
//! use jay_config::{config, quit, reload};
//! use jay_config::input::get_default_seat;
//! use jay_config::keyboard::mods::ALT;
//! use jay_config::keyboard::syms::{SYM_q, SYM_r};
//!
//! fn configure() {
//!     let seat = get_default_seat();
//!     // Create a key binding to exit the compositor.
//!     seat.bind(ALT | SYM_q, || quit());
//!     // Reload the configuration.
//!     seat.bind(ALT | SYM_r, || reload());
//! }
//!
//! config!(configure);
//! ```
//!
//! You should configure your crate to be compiled as a shared library:
//!
//! ```toml
//! [lib]
//! crate-type = ["cdylib"]
//! ```
//!
//! After compiling it, copy the shared library to `$HOME/.config/jay/config.so` and restart
//! the compositor. It should then use your configuration file.
//!
//! Note that you do not have to restart the compositor every time you want to reload your
//! configuration afterwards. Instead, simply invoke the [`reload`] function via a shortcut.

#![allow(
    clippy::zero_prefixed_literal,
    clippy::manual_range_contains,
    clippy::uninlined_format_args,
    clippy::len_zero,
    clippy::single_char_pattern,
    clippy::single_char_add_str,
    clippy::single_match
)]
#![warn(unsafe_op_in_unsafe_fn)]

use {
    crate::{_private::ipc::WorkspaceSource, keyboard::ModifiedKeySym, video::Connector},
    serde::{Deserialize, Serialize},
    std::{
        fmt::{Debug, Display, Formatter},
        time::Duration,
    },
};

#[macro_use]
mod macros;
#[doc(hidden)]
pub mod _private;
pub mod embedded;
pub mod exec;
pub mod input;
pub mod io;
pub mod keyboard;
pub mod logging;
pub mod status;
pub mod tasks;
pub mod theme;
pub mod timer;
pub mod video;
pub mod xwayland;

/// A planar direction.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
pub enum Direction {
    Left,
    Down,
    Up,
    Right,
}

/// A planar axis.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Axis {
    Horizontal,
    Vertical,
}

impl Axis {
    /// Returns the axis orthogonal to `self`.
    pub fn other(self) -> Self {
        match self {
            Self::Horizontal => Self::Vertical,
            Self::Vertical => Self::Horizontal,
        }
    }
}

/// Exits the compositor.
pub fn quit() {
    get!().quit()
}

/// Switches to a different VT.
pub fn switch_to_vt(n: u32) {
    get!().switch_to_vt(n)
}

/// Reloads the configuration.
///
/// If the configuration cannot be reloaded, this function has no effect.
pub fn reload() {
    get!().reload()
}

/// Returns whether this execution of the configuration function is due to a reload.
///
/// This can be used to decide whether the configuration should auto-start programs.
pub fn is_reload() -> bool {
    get!(false).is_reload()
}

/// Sets whether new workspaces are captured by default.
///
/// The default is `true`.
pub fn set_default_workspace_capture(capture: bool) {
    get!().set_default_workspace_capture(capture)
}

/// Returns whether new workspaces are captured by default.
pub fn get_default_workspace_capture() -> bool {
    get!(true).get_default_workspace_capture()
}

/// Toggles whether new workspaces are captured by default.
pub fn toggle_default_workspace_capture() {
    let get = get!();
    get.set_default_workspace_capture(!get.get_default_workspace_capture());
}

/// A workspace.
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Workspace(pub u64);

impl Workspace {
    /// Returns whether this workspace existed at the time `Seat::get_workspace` was called.
    pub fn exists(self) -> bool {
        self.0 != 0
    }

    /// Sets whether the workspaces is captured.
    ///
    /// The default is determined by `set_default_workspace_capture`.
    pub fn set_capture(self, capture: bool) {
        get!().set_workspace_capture(self, capture)
    }

    /// Returns whether the workspaces is captured.
    pub fn get_capture(self) -> bool {
        get!(true).get_workspace_capture(self)
    }

    /// Toggles whether the workspaces is captured.
    pub fn toggle_capture(self) {
        let get = get!();
        get.set_workspace_capture(self, !get.get_workspace_capture(self));
    }

    /// Moves this workspace to another output.
    ///
    /// This has no effect if the workspace is not currently being shown.
    pub fn move_to_output(self, output: Connector) {
        get!().move_to_output(WorkspaceSource::Explicit(self), output);
    }
}

/// Returns the workspace with the given name.
///
/// Workspaces are identified by their name. Calling this function alone does not create the
/// workspace if it doesn't already exist.
pub fn get_workspace(name: &str) -> Workspace {
    get!(Workspace(0)).get_workspace(name)
}

/// A PCI ID.
///
/// PCI IDs can be used to identify a hardware component. See the Debian [documentation][pci].
///
/// [pci]: https://wiki.debian.org/HowToIdentifyADevice/PCI
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Hash, Eq, PartialEq, Default)]
pub struct PciId {
    pub vendor: u32,
    pub model: u32,
}

impl Display for PciId {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{:04x}:{:04x}", self.vendor, self.model)
    }
}

/// Sets the callback to be called when the display goes idle.
pub fn on_idle<F: FnMut() + 'static>(f: F) {
    get!().on_idle(f)
}

/// Sets the callback to be called when all devices have been enumerated.
///
/// This callback is only invoked once during the lifetime of the compositor. This is a
/// good place to select the DRM device used for rendering.
pub fn on_devices_enumerated<F: FnOnce() + 'static>(f: F) {
    get!().on_devices_enumerated(f)
}

/// Returns the Jay config directory.
pub fn config_dir() -> String {
    get!().config_dir()
}

/// Returns all visible workspaces.
pub fn workspaces() -> Vec<Workspace> {
    get!().workspaces()
}

/// Configures the idle timeout.
///
/// `None` disables the timeout.
///
/// The default is 10 minutes.
pub fn set_idle(timeout: Option<Duration>) {
    get!().set_idle(timeout.unwrap_or_default())
}

/// Configures the idle grace period.
///
/// The grace period starts after the idle timeout expires. During the grace period, the
/// screen goes black but the displays are not yet disabled and the idle callback (set
/// with [`on_idle`]) is not yet called. This is a purely visual effect to inform the user
/// that the machine will soon go idle.
///
/// The default is 5 seconds.
pub fn set_idle_grace_period(timeout: Duration) {
    get!().set_idle_grace_period(timeout)
}

/// Enables or disables explicit sync.
///
/// Calling this after the compositor has started has no effect.
///
/// The default is `true`.
pub fn set_explicit_sync_enabled(enabled: bool) {
    get!().set_explicit_sync_enabled(enabled);
}

/// Enables or disables dragging of tiles and workspaces.
///
/// The default is `true`.
pub fn set_ui_drag_enabled(enabled: bool) {
    get!().set_ui_drag_enabled(enabled);
}

/// Sets the distance at which ui dragging starts.
///
/// The default is `10`.
pub fn set_ui_drag_threshold(threshold: i32) {
    get!().set_ui_drag_threshold(threshold);
}