Skip to main content

miracle_plugin/
config.rs

1//! Configuration types for the plugin `configure()` hook.
2//!
3//! Return a [`Configuration`] from your [`crate::plugin::Plugin::configure`] implementation
4//! to override compositor configuration values on every config reload. Any field
5//! left as `None` is ignored; the compositor keeps its own value for that field.
6//!
7//! The `plugins` and `includes` keys cannot be set by plugins.
8//!
9//! # Example
10//! ```rust,ignore
11//! use miracle_plugin::config::{BindingAction, Configuration, CustomKeyAction, Gaps, Key, Modifier};
12//!
13//! fn configure(&mut self) -> Option<Configuration> {
14//!     Some(Configuration {
15//!         primary_modifier: Some(Modifier::Meta),
16//!         custom_key_actions: Some(vec![CustomKeyAction {
17//!             action: BindingAction::Down,
18//!             modifiers: vec![Modifier::Primary],
19//!             key: Key::new("Return"),
20//!             command: "kitty".to_string(),
21//!         }]),
22//!         inner_gaps: Some(Gaps { x: 5, y: 5 }),
23//!         ..Default::default()
24//!     })
25//! }
26//! ```
27
28use serde::Serialize;
29
30// ─── Modifier ────────────────────────────────────────────────────────────────
31
32/// A keyboard modifier key for use in configuration bindings.
33///
34/// These names correspond exactly to the lowercase strings accepted by
35/// miracle-wm's configuration parser. [`Modifier::Primary`] is a special
36/// sentinel meaning "use whatever the user has configured as their primary
37/// modifier key" — it is the recommended value for plugins that want to
38/// integrate naturally with the user's keybinding preferences.
39///
40/// # Relationship to `input::InputEventModifiers`
41/// At runtime, `Modifier::Meta` corresponds to `InputEventModifiers::META`,
42/// `Modifier::Shift` to `InputEventModifiers::SHIFT`, etc. Config uses a
43/// simple enum because the set of recognised modifiers is fixed and small.
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub enum Modifier {
46    /// Either Alt key. Serializes as `"alt"`.
47    Alt,
48    /// Left Alt key. Serializes as `"alt_left"`.
49    AltLeft,
50    /// Right Alt key. Serializes as `"alt_right"`.
51    AltRight,
52    /// Either Shift key. Serializes as `"shift"`.
53    Shift,
54    /// Left Shift key. Serializes as `"shift_left"`.
55    ShiftLeft,
56    /// Right Shift key. Serializes as `"shift_right"`.
57    ShiftRight,
58    /// Sym key. Serializes as `"sym"`.
59    Sym,
60    /// Function key. Serializes as `"function"`.
61    Function,
62    /// Either Ctrl key. Serializes as `"ctrl"`.
63    Ctrl,
64    /// Left Ctrl key. Serializes as `"ctrl_left"`.
65    CtrlLeft,
66    /// Right Ctrl key. Serializes as `"ctrl_right"`.
67    CtrlRight,
68    /// Either Meta/Super/Windows key. Serializes as `"meta"`.
69    /// This is the most common choice for compositor bindings.
70    Meta,
71    /// Left Meta key. Serializes as `"meta_left"`.
72    MetaLeft,
73    /// Right Meta key. Serializes as `"meta_right"`.
74    MetaRight,
75    /// Caps Lock. Serializes as `"caps_lock"`.
76    CapsLock,
77    /// Num Lock. Serializes as `"num_lock"`.
78    NumLock,
79    /// Scroll Lock. Serializes as `"scroll_lock"`.
80    ScrollLock,
81    /// Sentinel: "use the user's configured primary modifier key".
82    /// Serializes as `"primary"`. Recommended for plugins that should
83    /// respect the user's own modifier preference.
84    Primary,
85}
86
87impl Modifier {
88    /// Returns the string representation expected by the compositor.
89    pub fn as_str(self) -> &'static str {
90        match self {
91            Self::Alt => "alt",
92            Self::AltLeft => "alt_left",
93            Self::AltRight => "alt_right",
94            Self::Shift => "shift",
95            Self::ShiftLeft => "shift_left",
96            Self::ShiftRight => "shift_right",
97            Self::Sym => "sym",
98            Self::Function => "function",
99            Self::Ctrl => "ctrl",
100            Self::CtrlLeft => "ctrl_left",
101            Self::CtrlRight => "ctrl_right",
102            Self::Meta => "meta",
103            Self::MetaLeft => "meta_left",
104            Self::MetaRight => "meta_right",
105            Self::CapsLock => "caps_lock",
106            Self::NumLock => "num_lock",
107            Self::ScrollLock => "scroll_lock",
108            Self::Primary => "primary",
109        }
110    }
111}
112
113impl Serialize for Modifier {
114    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
115        s.serialize_str(self.as_str())
116    }
117}
118
119// ─── BindingAction ───────────────────────────────────────────────────────────
120
121/// The keyboard event phase that triggers a key binding.
122///
123/// Named `BindingAction` (rather than `KeyboardAction`) to avoid confusion
124/// with [`crate::input::KeyboardAction`], which carries additional runtime
125/// variants that have no meaning in a configuration context.
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
127pub enum BindingAction {
128    /// Key was pressed. This is the most common trigger for bindings.
129    #[default]
130    Down,
131    /// Key was released.
132    Up,
133    /// Key is being held and auto-repeating.
134    Repeat,
135}
136
137impl BindingAction {
138    /// Returns the string representation expected by the compositor.
139    pub fn as_str(self) -> &'static str {
140        match self {
141            Self::Down => "down",
142            Self::Up => "up",
143            Self::Repeat => "repeat",
144        }
145    }
146}
147
148impl Serialize for BindingAction {
149    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
150        s.serialize_str(self.as_str())
151    }
152}
153
154// ─── Key ─────────────────────────────────────────────────────────────────────
155
156/// An XKB keysym name for use in configuration bindings.
157///
158/// Examples: `Key::new("Return")`, `Key::new("a")`, `Key::new("Up")`,
159/// `Key::new("F5")`.
160///
161/// The compositor validates the name using `xkb_keysym_from_name`. A full
162/// list of valid names is available at:
163/// <https://xkbcommon.org/doc/current/xkbcommon-keysyms_8h.html>
164#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
165pub struct Key(pub String);
166
167impl Key {
168    /// Create a key from an XKB keysym name.
169    pub fn new(name: impl Into<String>) -> Self {
170        Self(name.into())
171    }
172}
173
174impl<S: Into<String>> From<S> for Key {
175    fn from(s: S) -> Self {
176        Self(s.into())
177    }
178}
179
180impl Serialize for Key {
181    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
182        s.serialize_str(&self.0)
183    }
184}
185
186// ─── Handedness ──────────────────────────────────────────────────────────────
187
188/// Mouse button handedness.
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
190#[serde(rename_all = "snake_case")]
191pub enum Handedness {
192    #[default]
193    Right,
194    Left,
195}
196
197// ─── PointerAcceleration ─────────────────────────────────────────────────────
198
199/// Pointer acceleration profile.
200#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
201#[serde(rename_all = "snake_case")]
202pub enum PointerAcceleration {
203    Adaptive,
204    #[default]
205    None,
206}
207
208// ─── CursorFocusMode ─────────────────────────────────────────────────────────
209
210/// Whether focus follows the pointer or requires a click.
211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
212#[serde(rename_all = "snake_case")]
213pub enum CursorFocusMode {
214    #[default]
215    Hover,
216    Click,
217}
218
219// ─── TouchpadClickMode ───────────────────────────────────────────────────────
220
221/// Touchpad click emulation mode.
222#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
223#[serde(rename_all = "snake_case")]
224pub enum TouchpadClickMode {
225    #[default]
226    None,
227    AreaToClick,
228    FingerCount,
229}
230
231// ─── TouchpadScrollMode ──────────────────────────────────────────────────────
232
233/// Touchpad scroll method.
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
235#[serde(rename_all = "snake_case")]
236pub enum TouchpadScrollMode {
237    #[default]
238    None,
239    TwoFingerScroll,
240    EdgeScroll,
241    ButtonDownScroll,
242}
243
244// ─── AnimationPartType ───────────────────────────────────────────────────────
245
246/// Built-in animation visual effect for one phase of an animation sequence.
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
248#[serde(rename_all = "snake_case")]
249pub enum AnimationPartType {
250    #[default]
251    Disabled,
252    Slide,
253    Grow,
254    Shrink,
255    Fade,
256}
257
258// ─── EasingFunction ──────────────────────────────────────────────────────────
259
260/// Easing function for animation timing.
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
262#[serde(rename_all = "snake_case")]
263pub enum EasingFunction {
264    #[default]
265    Linear,
266    EaseInSine,
267    EaseOutSine,
268    EaseInOutSine,
269    EaseInQuad,
270    EaseOutQuad,
271    EaseInOutQuad,
272    EaseInCubic,
273    EaseOutCubic,
274    EaseInOutCubic,
275    EaseInQuart,
276    EaseOutQuart,
277    EaseInOutQuart,
278    EaseInQuint,
279    EaseOutQuint,
280    EaseInOutQuint,
281    EaseInExpo,
282    EaseOutExpo,
283    EaseInOutExpo,
284    EaseInCirc,
285    EaseOutCirc,
286    EaseInOutCirc,
287    EaseInBack,
288    EaseOutBack,
289    EaseInOutBack,
290    EaseInElastic,
291    EaseOutElastic,
292    EaseInOutElastic,
293    EaseInBounce,
294    EaseOutBounce,
295    EaseInOutBounce,
296}
297
298// ─── AnimationEvent ──────────────────────────────────────────────────────────
299
300/// The compositor event that an animation definition applies to.
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
302#[serde(rename_all = "snake_case")]
303pub enum AnimationEvent {
304    #[default]
305    WindowOpen,
306    WindowMove,
307    WindowClose,
308    WorkspaceSwitch,
309}
310
311// ─── AnimationKind ───────────────────────────────────────────────────────────
312
313/// Whether an animation is driven by a built-in effect or a plugin callback.
314#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
315#[serde(rename_all = "snake_case")]
316pub enum AnimationKind {
317    #[default]
318    BuiltIn,
319    Plugin,
320}
321
322// ─── Config structs ──────────────────────────────────────────────────────────
323
324/// Gaps configuration. Both `x` (left/right) and `y` (top/bottom) are in pixels.
325#[derive(Debug, Clone, Default, Serialize)]
326pub struct Gaps {
327    pub x: i32,
328    pub y: i32,
329}
330
331/// A custom key binding that runs a shell command.
332#[derive(Debug, Clone, Serialize)]
333pub struct CustomKeyAction {
334    /// The keyboard event phase that triggers this binding.
335    pub action: BindingAction,
336    /// The modifier keys required for this binding.
337    pub modifiers: Vec<Modifier>,
338    /// The XKB keysym name (e.g. `Key::new("Return")`, `Key::new("a")`).
339    pub key: Key,
340    /// The shell command to execute.
341    pub command: String,
342}
343
344/// Override the key binding for a built-in compositor action.
345#[derive(Debug, Clone, Serialize)]
346pub struct BuiltInKeyCommandOverride {
347    /// Name of the built-in action (e.g. `"terminal"`, `"close_window"`).
348    pub name: String,
349    /// The keyboard event phase that triggers this binding.
350    pub action: BindingAction,
351    /// The modifier keys required for this binding.
352    pub modifiers: Vec<Modifier>,
353    /// The XKB keysym name.
354    pub key: Key,
355}
356
357/// An application to start on compositor launch.
358#[derive(Debug, Clone, Default, Serialize)]
359pub struct StartupApp {
360    pub command: String,
361    #[serde(skip_serializing_if = "is_false")]
362    pub restart_on_death: bool,
363    #[serde(skip_serializing_if = "is_false")]
364    pub no_startup_id: bool,
365    #[serde(skip_serializing_if = "is_false")]
366    pub should_halt_compositor_on_death: bool,
367    #[serde(skip_serializing_if = "is_false")]
368    pub in_systemd_scope: bool,
369}
370
371/// An environment variable to set in the compositor's environment.
372#[derive(Debug, Clone, Default, Serialize)]
373pub struct EnvironmentVariable {
374    pub key: String,
375    pub value: String,
376}
377
378/// Window border appearance.
379///
380/// Borders are enabled by default (`size` 2, `radius` 8, focused `"FBDA25FF"`,
381/// unfocused `"55595CFF"`). Every field is serialized when `border` is set, so
382/// a plugin that overrides one value must supply the others as well.
383#[derive(Debug, Clone, Default, Serialize)]
384pub struct BorderConfig {
385    pub size: i32,
386    pub radius: f32,
387    /// Color as a hex string (`"RRGGBBAA"`) or an RGBA array `[r, g, b, a]` (0–255).
388    pub color: String,
389    /// Focused-window color as a hex string or RGBA array.
390    pub focus_color: String,
391}
392
393/// Workspace configuration entry.
394#[derive(Debug, Clone, Default, Serialize)]
395pub struct WorkspaceConfig {
396    #[serde(skip_serializing_if = "Option::is_none")]
397    pub number: Option<i32>,
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub name: Option<String>,
400}
401
402/// Drag-and-drop behaviour.
403#[derive(Debug, Clone, Serialize)]
404pub struct DragAndDropConfiguration {
405    pub enabled: bool,
406    /// The modifier keys required to initiate a drag-and-drop operation.
407    #[serde(skip_serializing_if = "Vec::is_empty")]
408    pub modifiers: Vec<Modifier>,
409}
410
411impl Default for DragAndDropConfiguration {
412    fn default() -> Self {
413        Self {
414            enabled: true,
415            modifiers: Vec::new(),
416        }
417    }
418}
419
420/// A single built-in animation (one phase of an easing sequence).
421#[derive(Debug, Clone, Default, Serialize)]
422pub struct BuiltInAnimationPart {
423    /// The visual effect for this animation phase.
424    #[serde(rename = "type")]
425    pub type_: AnimationPartType,
426    /// The easing function that controls timing.
427    pub function: EasingFunction,
428    #[serde(skip_serializing_if = "Option::is_none")]
429    pub c1: Option<f32>,
430    #[serde(skip_serializing_if = "Option::is_none")]
431    pub c2: Option<f32>,
432    #[serde(skip_serializing_if = "Option::is_none")]
433    pub c3: Option<f32>,
434    #[serde(skip_serializing_if = "Option::is_none")]
435    pub c4: Option<f32>,
436    #[serde(skip_serializing_if = "Option::is_none")]
437    pub c5: Option<f32>,
438    #[serde(skip_serializing_if = "Option::is_none")]
439    pub n1: Option<f32>,
440    #[serde(skip_serializing_if = "Option::is_none")]
441    pub d1: Option<f32>,
442    /// The scale at the collapsed end of a `grow` or `shrink` animation.
443    ///
444    /// `grow` interpolates from this value up to 1, while `shrink`
445    /// interpolates from 1 down to this value. Omitting it means the window
446    /// scales all the way to nothing. A value close to 1 (e.g. 0.9) produces
447    /// a subtle "pop" instead of a full zoom.
448    #[serde(skip_serializing_if = "Option::is_none")]
449    pub scale: Option<f32>,
450}
451
452/// An animation definition for one animatable event.
453#[derive(Debug, Clone, Default, Serialize)]
454pub struct AnimationDefinition {
455    /// The compositor event to animate.
456    pub event: AnimationEvent,
457    /// Whether to use a built-in animation effect or a plugin callback.
458    #[serde(rename = "type")]
459    pub type_: AnimationKind,
460    /// Duration in seconds.
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub duration: Option<f32>,
463    /// The list of animation phases (required when `type_` is `BuiltIn`).
464    #[serde(skip_serializing_if = "Vec::is_empty")]
465    pub parts: Vec<BuiltInAnimationPart>,
466}
467
468/// Mouse pointer configuration.
469#[derive(Debug, Clone, Default, Serialize)]
470pub struct MouseConfiguration {
471    /// Swap left and right buttons.
472    #[serde(skip_serializing_if = "Option::is_none")]
473    pub handedness: Option<Handedness>,
474    /// Pointer acceleration profile.
475    #[serde(skip_serializing_if = "Option::is_none")]
476    pub acceleration: Option<PointerAcceleration>,
477    #[serde(skip_serializing_if = "Option::is_none")]
478    pub acceleration_bias: Option<f64>,
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub vscroll_speed: Option<f64>,
481    #[serde(skip_serializing_if = "Option::is_none")]
482    pub hscroll_speed: Option<f64>,
483}
484
485/// Keymap (keyboard layout) configuration.
486#[derive(Debug, Clone, Default, Serialize)]
487pub struct KeymapConfiguration {
488    pub language: String,
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub variant: Option<String>,
491    #[serde(skip_serializing_if = "Vec::is_empty")]
492    pub options: Vec<String>,
493}
494
495/// Keyboard repeat and layout configuration.
496#[derive(Debug, Clone, Default, Serialize)]
497pub struct KeyboardConfiguration {
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub repeat_delay: Option<i32>,
500    #[serde(skip_serializing_if = "Option::is_none")]
501    pub repeat_rate: Option<i32>,
502    #[serde(skip_serializing_if = "Option::is_none")]
503    pub keymap: Option<KeymapConfiguration>,
504}
505
506/// Hover-click (dwell click) configuration.
507#[derive(Debug, Clone, Default, Serialize)]
508pub struct HoverClickConfiguration {
509    pub enabled: bool,
510    /// How long (ms) the pointer must hover before a click is generated.
511    #[serde(skip_serializing_if = "Option::is_none", rename = "hover_duration")]
512    pub hover_duration_ms: Option<u32>,
513    #[serde(skip_serializing_if = "Option::is_none")]
514    pub cancel_displacement_threshold: Option<i32>,
515    #[serde(skip_serializing_if = "Option::is_none")]
516    pub reclick_displacement_threshold: Option<i32>,
517}
518
519/// Simulated secondary (right) click via long-press.
520#[derive(Debug, Clone, Default, Serialize)]
521pub struct SimulatedSecondaryClickConfiguration {
522    pub enabled: bool,
523    /// How long (ms) to hold before the secondary click is generated.
524    #[serde(skip_serializing_if = "Option::is_none", rename = "hold_duration")]
525    pub hold_duration_ms: Option<u32>,
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub displacement_threshold: Option<i32>,
528}
529
530/// Output (display) filter shader.
531#[derive(Debug, Clone, Default, Serialize)]
532pub struct OutputFilterConfiguration {
533    #[serde(skip_serializing_if = "Option::is_none")]
534    pub shader_path: Option<String>,
535}
536
537/// Cursor appearance and focus behaviour.
538#[derive(Debug, Clone, Default, Serialize)]
539pub struct CursorConfiguration {
540    #[serde(skip_serializing_if = "Option::is_none")]
541    pub scale: Option<f32>,
542    /// Whether focus follows hover or requires a click.
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub focus_mode: Option<CursorFocusMode>,
545}
546
547/// Slow keys (accessibility) configuration.
548#[derive(Debug, Clone, Default, Serialize)]
549pub struct SlowKeysConfiguration {
550    pub enabled: bool,
551    /// How long (ms) a key must be held before it registers.
552    #[serde(skip_serializing_if = "Option::is_none", rename = "hold_delay")]
553    pub hold_delay_ms: Option<u32>,
554}
555
556/// Sticky keys (accessibility) configuration.
557#[derive(Debug, Clone, Default, Serialize)]
558pub struct StickyKeysConfiguration {
559    pub enabled: bool,
560    #[serde(skip_serializing_if = "Option::is_none")]
561    pub should_disable_if_two_keys_are_pressed_together: Option<bool>,
562}
563
564/// Touchpad configuration.
565#[derive(Debug, Clone, Default, Serialize)]
566pub struct TouchpadConfiguration {
567    #[serde(skip_serializing_if = "Option::is_none")]
568    pub disable_while_typing: Option<bool>,
569    #[serde(skip_serializing_if = "Option::is_none")]
570    pub disable_with_external_mouse: Option<bool>,
571    #[serde(skip_serializing_if = "Option::is_none")]
572    pub acceleration_bias: Option<f64>,
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub vscroll_speed: Option<f64>,
575    #[serde(skip_serializing_if = "Option::is_none")]
576    pub hscroll_speed: Option<f64>,
577    #[serde(skip_serializing_if = "Option::is_none")]
578    pub tap_to_click: Option<bool>,
579    #[serde(skip_serializing_if = "Option::is_none")]
580    pub middle_mouse_button_emulation: Option<bool>,
581    /// Touchpad click emulation mode.
582    #[serde(skip_serializing_if = "Option::is_none")]
583    pub click_mode: Option<TouchpadClickMode>,
584    /// Touchpad scroll method.
585    #[serde(skip_serializing_if = "Option::is_none")]
586    pub scroll_mode: Option<TouchpadScrollMode>,
587}
588
589/// Screen magnifier configuration.
590#[derive(Debug, Clone, Default, Serialize)]
591pub struct MagnifierConfiguration {
592    pub enabled: bool,
593    #[serde(skip_serializing_if = "Option::is_none")]
594    pub scale: Option<f32>,
595    #[serde(skip_serializing_if = "Option::is_none")]
596    pub scale_increment: Option<f32>,
597    #[serde(skip_serializing_if = "Option::is_none")]
598    pub width: Option<i32>,
599    #[serde(skip_serializing_if = "Option::is_none")]
600    pub height: Option<i32>,
601    #[serde(skip_serializing_if = "Option::is_none")]
602    pub size_increment: Option<i32>,
603}
604
605/// Configuration overrides that a plugin may return from [`Plugin::configure`].
606///
607/// Every field is optional. `None` means "do not override this value". The
608/// compositor merges all loaded plugins' results and then merges the combined
609/// result with the file-based configuration (plugin values win on conflict).
610///
611/// The `plugins` and `includes` keys of the compositor config cannot be set
612/// by plugins and are intentionally absent from this struct.
613#[derive(Debug, Clone, Default, Serialize)]
614pub struct Configuration {
615    /// The primary modifier key (e.g. `Modifier::Meta` for the Super/Windows key).
616    #[serde(skip_serializing_if = "Option::is_none", rename = "action_key")]
617    pub primary_modifier: Option<Modifier>,
618
619    /// Custom key bindings that run shell commands.
620    #[serde(skip_serializing_if = "Option::is_none", rename = "custom_actions")]
621    pub custom_key_actions: Option<Vec<CustomKeyAction>>,
622
623    /// Overrides for built-in compositor key bindings.
624    #[serde(skip_serializing_if = "Option::is_none")]
625    pub default_action_overrides: Option<Vec<BuiltInKeyCommandOverride>>,
626
627    /// Inner (between windows) gap size.
628    #[serde(skip_serializing_if = "Option::is_none")]
629    pub inner_gaps: Option<Gaps>,
630
631    /// Outer (screen edge) gap size.
632    #[serde(skip_serializing_if = "Option::is_none")]
633    pub outer_gaps: Option<Gaps>,
634
635    /// Applications to launch on startup.
636    #[serde(skip_serializing_if = "Option::is_none")]
637    pub startup_apps: Option<Vec<StartupApp>>,
638
639    /// Override the default terminal emulator command.
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub terminal: Option<String>,
642
643    /// Pixel amount to jump when resizing with keyboard shortcuts.
644    #[serde(skip_serializing_if = "Option::is_none")]
645    pub resize_jump: Option<i32>,
646
647    /// Extra environment variables to set in the compositor process.
648    #[serde(skip_serializing_if = "Option::is_none")]
649    pub environment_variables: Option<Vec<EnvironmentVariable>>,
650
651    /// Window border appearance.
652    #[serde(skip_serializing_if = "Option::is_none")]
653    pub border: Option<BorderConfig>,
654
655    /// Workspace layout definitions.
656    #[serde(skip_serializing_if = "Option::is_none")]
657    pub workspaces: Option<Vec<WorkspaceConfig>>,
658
659    /// Animation definitions per event. Each entry names an `event` plus the definition.
660    #[serde(skip_serializing_if = "Option::is_none")]
661    pub animations: Option<Vec<AnimationDefinition>>,
662
663    /// Whether animations are globally enabled.
664    #[serde(skip_serializing_if = "Option::is_none")]
665    pub enable_animations: Option<bool>,
666
667    /// The modifier keys used for window move operations.
668    /// Use `vec![Modifier::Primary]` to follow the user's primary modifier.
669    #[serde(skip_serializing_if = "Option::is_none")]
670    pub move_modifier: Option<Vec<Modifier>>,
671
672    /// Drag-and-drop behaviour.
673    #[serde(skip_serializing_if = "Option::is_none")]
674    pub drag_and_drop: Option<DragAndDropConfiguration>,
675
676    /// Mouse pointer settings.
677    #[serde(skip_serializing_if = "Option::is_none")]
678    pub mouse: Option<MouseConfiguration>,
679
680    /// Touchpad settings.
681    #[serde(skip_serializing_if = "Option::is_none")]
682    pub touchpad: Option<TouchpadConfiguration>,
683
684    /// Keyboard repeat rate, delay, and keymap.
685    #[serde(skip_serializing_if = "Option::is_none")]
686    pub keyboard: Option<KeyboardConfiguration>,
687
688    /// Hover-click (dwell click) accessibility feature.
689    #[serde(skip_serializing_if = "Option::is_none")]
690    pub hover_click: Option<HoverClickConfiguration>,
691
692    /// Simulated secondary click accessibility feature.
693    #[serde(skip_serializing_if = "Option::is_none")]
694    pub simulated_secondary_click: Option<SimulatedSecondaryClickConfiguration>,
695
696    /// Output (display) post-processing filter.
697    #[serde(skip_serializing_if = "Option::is_none")]
698    pub output_filter: Option<OutputFilterConfiguration>,
699
700    /// Cursor appearance and focus behaviour.
701    #[serde(skip_serializing_if = "Option::is_none")]
702    pub cursor: Option<CursorConfiguration>,
703
704    /// Slow keys accessibility feature.
705    #[serde(skip_serializing_if = "Option::is_none")]
706    pub slow_keys: Option<SlowKeysConfiguration>,
707
708    /// Sticky keys accessibility feature.
709    #[serde(skip_serializing_if = "Option::is_none")]
710    pub sticky_keys: Option<StickyKeysConfiguration>,
711
712    /// Screen magnifier.
713    #[serde(skip_serializing_if = "Option::is_none")]
714    pub magnifier: Option<MagnifierConfiguration>,
715
716    /// Whether switching to the current workspace goes back to the previous one.
717    #[serde(skip_serializing_if = "Option::is_none")]
718    pub workspace_back_and_forth: Option<bool>,
719
720    /// The color that the compositor clears the screen to, as a hex string
721    /// (`"RRGGBB"`) or an RGB array `[r, g, b]` (0-255). Always fully opaque.
722    #[serde(skip_serializing_if = "Option::is_none")]
723    pub background_color: Option<String>,
724}
725
726fn is_false(v: &bool) -> bool {
727    !v
728}