2024-01-07 09:07:22 +04:00
|
|
|
#[macro_use]
|
|
|
|
|
extern crate tracing;
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
use std::collections::HashSet;
|
|
|
|
|
use std::ffi::OsStr;
|
2025-08-05 15:27:28 +02:00
|
|
|
use std::fs::{self, File};
|
|
|
|
|
use std::io::Write;
|
2025-02-10 13:29:00 +03:00
|
|
|
use std::ops::{Mul, MulAssign};
|
2024-01-18 11:02:15 +04:00
|
|
|
use std::path::{Path, PathBuf};
|
2023-09-05 12:58:51 +04:00
|
|
|
use std::str::FromStr;
|
2024-03-22 20:47:40 +04:00
|
|
|
use std::time::Duration;
|
2023-09-05 12:58:51 +04:00
|
|
|
|
|
|
|
|
use bitflags::bitflags;
|
2024-03-01 12:50:49 +01:00
|
|
|
use knuffel::errors::DecodeError;
|
2024-04-17 14:06:32 +04:00
|
|
|
use knuffel::Decode as _;
|
2024-11-14 11:33:08 +03:00
|
|
|
use layer_rule::LayerRule;
|
2025-02-16 19:30:33 +03:00
|
|
|
use miette::{miette, Context, IntoDiagnostic};
|
2024-12-28 11:40:16 +03:00
|
|
|
use niri_ipc::{
|
2025-02-06 08:42:09 +03:00
|
|
|
ColumnDisplay, ConfiguredMode, LayoutSwitchTarget, PositionChange, SizeChange, Transform,
|
2024-12-28 11:40:16 +03:00
|
|
|
WorkspaceReferenceArg,
|
|
|
|
|
};
|
2024-08-20 11:43:32 +03:00
|
|
|
use smithay::backend::renderer::Color32F;
|
2023-09-24 11:04:30 +04:00
|
|
|
use smithay::input::keyboard::keysyms::KEY_NoSymbol;
|
|
|
|
|
use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
|
2023-12-05 08:04:46 +04:00
|
|
|
use smithay::input::keyboard::{Keysym, XkbConfig};
|
2024-01-08 10:15:29 +04:00
|
|
|
use smithay::reexports::input;
|
2023-09-05 12:58:51 +04:00
|
|
|
|
2025-04-25 08:53:16 +03:00
|
|
|
pub const DEFAULT_BACKGROUND_COLOR: Color = Color::from_array_unpremul([0.25, 0.25, 0.25, 1.]);
|
|
|
|
|
pub const DEFAULT_BACKDROP_COLOR: Color = Color::from_array_unpremul([0.15, 0.15, 0.15, 1.]);
|
2024-07-25 23:41:33 +05:30
|
|
|
|
2024-11-14 11:33:08 +03:00
|
|
|
pub mod layer_rule;
|
|
|
|
|
|
2024-11-14 09:44:07 +03:00
|
|
|
mod utils;
|
|
|
|
|
pub use utils::RegexEq;
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, PartialEq)]
|
|
|
|
|
pub struct Config {
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub input: Input,
|
2023-09-21 13:48:32 +04:00
|
|
|
#[knuffel(children(name = "output"))]
|
2024-07-04 17:51:11 +04:00
|
|
|
pub outputs: Outputs,
|
2023-09-21 19:58:03 +04:00
|
|
|
#[knuffel(children(name = "spawn-at-startup"))]
|
|
|
|
|
pub spawn_at_startup: Vec<SpawnAtStartup>,
|
2023-09-05 12:58:51 +04:00
|
|
|
#[knuffel(child, default)]
|
2024-01-06 13:04:21 +04:00
|
|
|
pub layout: Layout,
|
2023-09-26 13:09:33 +04:00
|
|
|
#[knuffel(child, default)]
|
2023-09-26 13:44:37 +04:00
|
|
|
pub prefer_no_csd: bool,
|
|
|
|
|
#[knuffel(child, default)]
|
2023-10-01 17:42:56 +04:00
|
|
|
pub cursor: Cursor,
|
2023-10-31 14:23:54 +04:00
|
|
|
#[knuffel(
|
|
|
|
|
child,
|
|
|
|
|
unwrap(argument),
|
|
|
|
|
default = Some(String::from(
|
|
|
|
|
"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
|
|
|
|
|
)))
|
|
|
|
|
]
|
|
|
|
|
pub screenshot_path: Option<String>,
|
2023-10-01 17:42:56 +04:00
|
|
|
#[knuffel(child, default)]
|
2025-01-22 00:00:35 -05:00
|
|
|
pub clipboard: Clipboard,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-01-20 08:31:05 +04:00
|
|
|
pub hotkey_overlay: HotkeyOverlay,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-02-07 17:05:15 +04:00
|
|
|
pub animations: Animations,
|
2024-02-24 10:08:56 +04:00
|
|
|
#[knuffel(child, default)]
|
2025-02-16 08:46:38 +03:00
|
|
|
pub gestures: Gestures,
|
|
|
|
|
#[knuffel(child, default)]
|
2025-04-25 09:36:50 +03:00
|
|
|
pub overview: Overview,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-02-24 10:08:56 +04:00
|
|
|
pub environment: Environment,
|
2025-06-04 08:26:51 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub xwayland_satellite: XwaylandSatellite,
|
2024-02-13 17:46:37 +04:00
|
|
|
#[knuffel(children(name = "window-rule"))]
|
|
|
|
|
pub window_rules: Vec<WindowRule>,
|
2024-11-14 11:33:08 +03:00
|
|
|
#[knuffel(children(name = "layer-rule"))]
|
|
|
|
|
pub layer_rules: Vec<LayerRule>,
|
2024-02-07 17:05:15 +04:00
|
|
|
#[knuffel(child, default)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub binds: Binds,
|
2023-09-06 15:49:46 +04:00
|
|
|
#[knuffel(child, default)]
|
2024-10-18 16:00:40 +02:00
|
|
|
pub switch_events: SwitchBinds,
|
|
|
|
|
#[knuffel(child, default)]
|
2023-09-06 15:49:46 +04:00
|
|
|
pub debug: DebugConfig,
|
2024-05-11 22:40:30 +02:00
|
|
|
#[knuffel(children(name = "workspace"))]
|
|
|
|
|
pub workspaces: Vec<Workspace>,
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Input {
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub keyboard: Keyboard,
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub touchpad: Touchpad,
|
2023-10-03 17:02:07 +04:00
|
|
|
#[knuffel(child, default)]
|
2024-01-08 11:53:34 +04:00
|
|
|
pub mouse: Mouse,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-02-14 16:24:46 +01:00
|
|
|
pub trackpoint: Trackpoint,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-10-16 16:51:56 +03:00
|
|
|
pub trackball: Trackball,
|
|
|
|
|
#[knuffel(child, default)]
|
2023-10-03 17:02:07 +04:00
|
|
|
pub tablet: Tablet,
|
2024-02-24 18:32:13 +01:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub touch: Touch,
|
2023-12-28 09:36:10 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_power_key_handling: bool,
|
2024-02-26 18:47:46 +01:00
|
|
|
#[knuffel(child)]
|
2025-03-13 14:55:16 +01:00
|
|
|
pub warp_mouse_to_focus: Option<WarpMouseToFocus>,
|
2024-03-18 18:17:04 +04:00
|
|
|
#[knuffel(child)]
|
2024-07-05 20:12:56 +04:00
|
|
|
pub focus_follows_mouse: Option<FocusFollowsMouse>,
|
2024-03-19 14:27:52 +00:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub workspace_auto_back_and_forth: bool,
|
2025-02-05 09:34:25 -05:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub mod_key: Option<ModKey>,
|
|
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub mod_key_nested: Option<ModKey>,
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-19 17:55:54 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, PartialEq, Eq)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub struct Keyboard {
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub xkb: Xkb,
|
2023-09-16 20:01:52 +04:00
|
|
|
// The defaults were chosen to match wlroots and sway.
|
2024-05-19 17:55:54 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().repeat_delay)]
|
2023-09-16 12:37:23 +04:00
|
|
|
pub repeat_delay: u16,
|
2024-05-19 17:55:54 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().repeat_rate)]
|
2023-09-16 12:37:23 +04:00
|
|
|
pub repeat_rate: u8,
|
2023-11-02 00:10:22 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub track_layout: TrackLayout,
|
2025-04-20 11:30:40 +02:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub numlock: bool,
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-19 17:55:54 +04:00
|
|
|
impl Default for Keyboard {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
xkb: Default::default(),
|
|
|
|
|
repeat_delay: 600,
|
|
|
|
|
repeat_rate: 25,
|
|
|
|
|
track_layout: Default::default(),
|
2025-04-20 11:30:40 +02:00
|
|
|
numlock: Default::default(),
|
2024-05-19 17:55:54 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-08 07:58:03 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq, Eq, Clone)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub struct Xkb {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub rules: String,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub model: String,
|
2024-03-23 10:10:01 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub layout: String,
|
2023-09-05 12:58:51 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub variant: String,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub options: Option<String>,
|
2025-01-30 14:50:05 +01:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub file: Option<String>,
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2023-12-05 08:04:46 +04:00
|
|
|
impl Xkb {
|
|
|
|
|
pub fn to_xkb_config(&self) -> XkbConfig {
|
|
|
|
|
XkbConfig {
|
|
|
|
|
rules: &self.rules,
|
|
|
|
|
model: &self.model,
|
2024-03-23 10:10:01 +04:00
|
|
|
layout: &self.layout,
|
2023-12-05 08:04:46 +04:00
|
|
|
variant: &self.variant,
|
|
|
|
|
options: self.options.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 17:17:19 +04:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq, Clone, Copy)]
|
|
|
|
|
pub enum CenterFocusedColumn {
|
|
|
|
|
/// Focusing a column will not center the column.
|
|
|
|
|
#[default]
|
|
|
|
|
Never,
|
|
|
|
|
/// The focused column will always be centered.
|
|
|
|
|
Always,
|
|
|
|
|
/// Focusing a column will center it if it doesn't fit on the screen together with the
|
|
|
|
|
/// previously focused column.
|
|
|
|
|
OnOverflow,
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-02 00:10:22 +04:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Default, PartialEq, Eq)]
|
|
|
|
|
pub enum TrackLayout {
|
|
|
|
|
/// The layout change is global.
|
|
|
|
|
#[default]
|
|
|
|
|
Global,
|
|
|
|
|
/// The layout change is window local.
|
|
|
|
|
Window,
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Touchpad {
|
2024-05-23 16:01:54 +09:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2023-09-05 12:58:51 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub tap: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-01-08 10:24:00 +04:00
|
|
|
pub dwt: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-02-03 08:20:15 +04:00
|
|
|
pub dwtp: bool,
|
2025-03-01 23:01:34 -08:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub drag: Option<bool>,
|
2024-02-03 08:20:15 +04:00
|
|
|
#[knuffel(child)]
|
2025-02-04 20:21:15 +08:00
|
|
|
pub drag_lock: bool,
|
|
|
|
|
#[knuffel(child)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub natural_scroll: bool,
|
2024-03-13 21:26:03 -07:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub click_method: Option<ClickMethod>,
|
2023-09-05 12:58:51 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2025-06-09 14:02:17 +03:00
|
|
|
pub accel_speed: FloatOrInt<-1, 1>,
|
2024-01-08 10:15:29 +04:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub accel_profile: Option<AccelProfile>,
|
2024-01-08 10:32:04 +04:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
2024-05-26 14:41:00 +09:00
|
|
|
pub scroll_method: Option<ScrollMethod>,
|
2024-10-17 01:43:47 -04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_button: Option<u32>,
|
2025-06-19 02:22:01 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub scroll_button_lock: bool,
|
2024-05-26 14:41:00 +09:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
2024-01-08 10:32:04 +04:00
|
|
|
pub tap_button_map: Option<TapButtonMap>,
|
2024-05-21 10:10:11 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub left_handed: bool,
|
2024-05-23 16:01:54 +09:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disabled_on_external_mouse: bool,
|
2024-07-13 07:34:22 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub middle_emulation: bool,
|
2024-11-10 09:14:22 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_factor: Option<FloatOrInt<0, 100>>,
|
2024-01-08 10:15:29 +04:00
|
|
|
}
|
|
|
|
|
|
2024-01-08 11:53:34 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Mouse {
|
2024-05-23 16:01:54 +09:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2024-01-08 11:53:34 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub natural_scroll: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2025-06-09 14:02:17 +03:00
|
|
|
pub accel_speed: FloatOrInt<-1, 1>,
|
2024-01-08 11:53:34 +04:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub accel_profile: Option<AccelProfile>,
|
2024-05-26 14:41:00 +09:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub scroll_method: Option<ScrollMethod>,
|
2024-10-17 01:43:47 -04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_button: Option<u32>,
|
2024-05-21 10:10:11 +04:00
|
|
|
#[knuffel(child)]
|
2025-06-19 02:22:01 +03:00
|
|
|
pub scroll_button_lock: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-05-21 10:10:11 +04:00
|
|
|
pub left_handed: bool,
|
2024-07-13 07:34:22 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub middle_emulation: bool,
|
2024-11-10 09:14:22 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_factor: Option<FloatOrInt<0, 100>>,
|
2024-01-08 11:53:34 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-14 16:24:46 +01:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Trackpoint {
|
2024-05-23 16:01:54 +09:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2024-02-14 16:24:46 +01:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub natural_scroll: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2025-06-09 14:02:17 +03:00
|
|
|
pub accel_speed: FloatOrInt<-1, 1>,
|
2024-02-14 16:24:46 +01:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub accel_profile: Option<AccelProfile>,
|
2024-05-26 14:41:00 +09:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub scroll_method: Option<ScrollMethod>,
|
2024-10-17 01:43:47 -04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_button: Option<u32>,
|
2024-07-13 07:34:22 +03:00
|
|
|
#[knuffel(child)]
|
2025-06-19 02:22:01 +03:00
|
|
|
pub scroll_button_lock: bool,
|
|
|
|
|
#[knuffel(child)]
|
2025-03-13 09:20:42 +01:00
|
|
|
pub left_handed: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-07-13 07:34:22 +03:00
|
|
|
pub middle_emulation: bool,
|
2024-02-14 16:24:46 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-16 16:51:56 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Trackball {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub natural_scroll: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2025-06-09 14:02:17 +03:00
|
|
|
pub accel_speed: FloatOrInt<-1, 1>,
|
2024-10-16 16:51:56 +03:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub accel_profile: Option<AccelProfile>,
|
2024-10-17 01:43:47 -04:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub scroll_method: Option<ScrollMethod>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_button: Option<u32>,
|
2024-10-16 16:51:56 +03:00
|
|
|
#[knuffel(child)]
|
2025-06-19 02:22:01 +03:00
|
|
|
pub scroll_button_lock: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-10-16 16:51:56 +03:00
|
|
|
pub left_handed: bool,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub middle_emulation: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 21:26:03 -07:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum ClickMethod {
|
|
|
|
|
Clickfinger,
|
|
|
|
|
ButtonAreas,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ClickMethod> for input::ClickMethod {
|
|
|
|
|
fn from(value: ClickMethod) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
ClickMethod::Clickfinger => Self::Clickfinger,
|
|
|
|
|
ClickMethod::ButtonAreas => Self::ButtonAreas,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 10:15:29 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum AccelProfile {
|
|
|
|
|
Adaptive,
|
|
|
|
|
Flat,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<AccelProfile> for input::AccelProfile {
|
|
|
|
|
fn from(value: AccelProfile) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
AccelProfile::Adaptive => Self::Adaptive,
|
|
|
|
|
AccelProfile::Flat => Self::Flat,
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-26 14:41:00 +09:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum ScrollMethod {
|
|
|
|
|
NoScroll,
|
|
|
|
|
TwoFinger,
|
|
|
|
|
Edge,
|
|
|
|
|
OnButtonDown,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ScrollMethod> for input::ScrollMethod {
|
|
|
|
|
fn from(value: ScrollMethod) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
ScrollMethod::NoScroll => Self::NoScroll,
|
|
|
|
|
ScrollMethod::TwoFinger => Self::TwoFinger,
|
|
|
|
|
ScrollMethod::Edge => Self::Edge,
|
|
|
|
|
ScrollMethod::OnButtonDown => Self::OnButtonDown,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 10:32:04 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum TapButtonMap {
|
|
|
|
|
LeftRightMiddle,
|
|
|
|
|
LeftMiddleRight,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<TapButtonMap> for input::TapButtonMap {
|
|
|
|
|
fn from(value: TapButtonMap) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
TapButtonMap::LeftRightMiddle => Self::LeftRightMiddle,
|
|
|
|
|
TapButtonMap::LeftMiddleRight => Self::LeftMiddleRight,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 17:02:07 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Tablet {
|
2024-05-23 16:01:54 +09:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2025-02-14 08:15:45 +03:00
|
|
|
#[knuffel(child, unwrap(arguments))]
|
|
|
|
|
pub calibration_matrix: Option<Vec<f32>>,
|
2023-10-03 17:02:07 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub map_to_output: Option<String>,
|
2024-05-21 10:10:11 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub left_handed: bool,
|
2023-10-03 17:02:07 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-24 18:32:13 +01:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Touch {
|
2025-02-21 13:35:10 -05:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2024-02-24 18:32:13 +01:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub map_to_output: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 20:12:56 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct FocusFollowsMouse {
|
|
|
|
|
#[knuffel(property, str)]
|
|
|
|
|
pub max_scroll_amount: Option<Percent>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-13 14:55:16 +01:00
|
|
|
#[derive(knuffel::Decode, Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
|
|
pub struct WarpMouseToFocus {
|
|
|
|
|
#[knuffel(property, str)]
|
|
|
|
|
pub mode: Option<WarpMouseToFocusMode>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
|
|
pub enum WarpMouseToFocusMode {
|
|
|
|
|
CenterXy,
|
|
|
|
|
CenterXyAlways,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FromStr for WarpMouseToFocusMode {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"center-xy" => Ok(Self::CenterXy),
|
|
|
|
|
"center-xy-always" => Ok(Self::CenterXyAlways),
|
|
|
|
|
_ => Err(miette!(
|
|
|
|
|
r#"invalid mode for warp-mouse-to-focus, can be "center-xy" or "center-xy-always" (or leave unset for separate centering)"#
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 20:12:56 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Percent(pub f64);
|
|
|
|
|
|
2025-02-05 09:34:25 -05:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
|
|
|
pub enum ModKey {
|
|
|
|
|
Ctrl,
|
|
|
|
|
Shift,
|
|
|
|
|
Alt,
|
|
|
|
|
Super,
|
|
|
|
|
IsoLevel3Shift,
|
|
|
|
|
IsoLevel5Shift,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ModKey {
|
|
|
|
|
pub fn to_modifiers(&self) -> Modifiers {
|
|
|
|
|
match self {
|
|
|
|
|
ModKey::Ctrl => Modifiers::CTRL,
|
|
|
|
|
ModKey::Shift => Modifiers::SHIFT,
|
|
|
|
|
ModKey::Alt => Modifiers::ALT,
|
|
|
|
|
ModKey::Super => Modifiers::SUPER,
|
|
|
|
|
ModKey::IsoLevel3Shift => Modifiers::ISO_LEVEL3_SHIFT,
|
|
|
|
|
ModKey::IsoLevel5Shift => Modifiers::ISO_LEVEL5_SHIFT,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 17:51:11 +04:00
|
|
|
#[derive(Debug, Default, Clone, PartialEq)]
|
|
|
|
|
pub struct Outputs(pub Vec<Output>);
|
|
|
|
|
|
2023-09-21 13:48:32 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
|
|
|
|
pub struct Output {
|
2023-12-18 10:27:41 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2023-09-21 13:48:32 +04:00
|
|
|
#[knuffel(argument)]
|
|
|
|
|
pub name: String,
|
2024-05-05 12:39:20 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
2024-06-18 11:01:18 +03:00
|
|
|
pub scale: Option<FloatOrInt<0, 10>>,
|
2024-01-28 14:25:40 +01:00
|
|
|
#[knuffel(child, unwrap(argument, str), default = Transform::Normal)]
|
|
|
|
|
pub transform: Transform,
|
2023-09-30 11:33:02 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub position: Option<Position>,
|
2023-10-03 08:35:24 +04:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
2024-05-05 10:19:47 +04:00
|
|
|
pub mode: Option<ConfiguredMode>,
|
2024-04-14 09:23:15 +04:00
|
|
|
#[knuffel(child)]
|
2024-08-22 18:58:07 +10:00
|
|
|
pub variable_refresh_rate: Option<Vrr>,
|
2025-03-29 10:13:59 +00:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub focus_at_startup: bool,
|
2025-05-06 17:12:07 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub background_color: Option<Color>,
|
2025-04-28 07:53:03 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub backdrop_color: Option<Color>,
|
2023-09-21 13:48:32 +04:00
|
|
|
}
|
|
|
|
|
|
2024-08-22 18:58:07 +10:00
|
|
|
impl Output {
|
|
|
|
|
pub fn is_vrr_always_on(&self) -> bool {
|
|
|
|
|
self.variable_refresh_rate == Some(Vrr { on_demand: false })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_vrr_on_demand(&self) -> bool {
|
|
|
|
|
self.variable_refresh_rate == Some(Vrr { on_demand: true })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn is_vrr_always_off(&self) -> bool {
|
|
|
|
|
self.variable_refresh_rate.is_none()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 13:48:32 +04:00
|
|
|
impl Default for Output {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
2023-12-18 10:27:41 +04:00
|
|
|
off: false,
|
2025-03-29 10:13:59 +00:00
|
|
|
focus_at_startup: false,
|
2023-09-21 13:48:32 +04:00
|
|
|
name: String::new(),
|
2024-05-05 12:39:20 +04:00
|
|
|
scale: None,
|
2024-01-28 14:25:40 +01:00
|
|
|
transform: Transform::Normal,
|
2023-09-30 11:33:02 +04:00
|
|
|
position: None,
|
2023-10-03 08:35:24 +04:00
|
|
|
mode: None,
|
2024-08-22 18:58:07 +10:00
|
|
|
variable_refresh_rate: None,
|
2025-05-06 17:12:07 +03:00
|
|
|
background_color: None,
|
2025-04-28 07:53:03 +03:00
|
|
|
backdrop_color: None,
|
2023-09-21 13:48:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-03 12:13:04 +03:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct OutputName {
|
|
|
|
|
pub connector: String,
|
|
|
|
|
pub make: Option<String>,
|
|
|
|
|
pub model: Option<String>,
|
|
|
|
|
pub serial: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-16 08:43:28 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq, Eq)]
|
2023-09-30 11:33:02 +04:00
|
|
|
pub struct Position {
|
|
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub x: i32,
|
|
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub y: i32,
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-22 18:58:07 +10:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Default)]
|
|
|
|
|
pub struct Vrr {
|
|
|
|
|
#[knuffel(property, default = false)]
|
|
|
|
|
pub on_demand: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
// MIN and MAX generics are only used during parsing to check the value.
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct FloatOrInt<const MIN: i32, const MAX: i32>(pub f64);
|
|
|
|
|
|
2024-02-13 13:14:49 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
2024-01-06 13:04:21 +04:00
|
|
|
pub struct Layout {
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub focus_ring: FocusRing,
|
2024-02-12 09:34:54 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub border: Border,
|
2024-07-15 15:51:48 +02:00
|
|
|
#[knuffel(child, default)]
|
2025-01-15 14:16:05 +03:00
|
|
|
pub shadow: Shadow,
|
|
|
|
|
#[knuffel(child, default)]
|
2025-02-02 08:41:42 +03:00
|
|
|
pub tab_indicator: TabIndicator,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-07-15 15:51:48 +02:00
|
|
|
pub insert_hint: InsertHint,
|
2024-01-06 13:04:21 +04:00
|
|
|
#[knuffel(child, unwrap(children), default)]
|
2024-09-05 23:37:10 +02:00
|
|
|
pub preset_column_widths: Vec<PresetSize>,
|
2024-01-06 13:04:21 +04:00
|
|
|
#[knuffel(child)]
|
2024-09-05 23:37:10 +02:00
|
|
|
pub default_column_width: Option<DefaultPresetSize>,
|
|
|
|
|
#[knuffel(child, unwrap(children), default)]
|
|
|
|
|
pub preset_window_heights: Vec<PresetSize>,
|
2024-01-08 17:17:19 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
|
|
|
|
pub center_focused_column: CenterFocusedColumn,
|
2024-08-29 16:41:59 +02:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub always_center_single_column: bool,
|
2024-11-29 06:46:13 +01:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub empty_workspace_above_first: bool,
|
2025-02-06 08:42:09 +03:00
|
|
|
#[knuffel(child, unwrap(argument, str), default = Self::default().default_column_display)]
|
2025-02-01 10:46:52 +03:00
|
|
|
pub default_column_display: ColumnDisplay,
|
2024-02-13 13:14:49 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().gaps)]
|
2024-06-17 09:16:28 +03:00
|
|
|
pub gaps: FloatOrInt<0, 65535>,
|
2024-01-06 13:04:21 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub struts: Struts,
|
2025-05-06 17:12:07 +03:00
|
|
|
#[knuffel(child, default = DEFAULT_BACKGROUND_COLOR)]
|
|
|
|
|
pub background_color: Color,
|
2024-01-06 13:04:21 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-13 13:14:49 +04:00
|
|
|
impl Default for Layout {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
focus_ring: Default::default(),
|
|
|
|
|
border: Default::default(),
|
2025-01-15 14:16:05 +03:00
|
|
|
shadow: Default::default(),
|
2025-02-02 08:41:42 +03:00
|
|
|
tab_indicator: Default::default(),
|
2024-07-15 15:51:48 +02:00
|
|
|
insert_hint: Default::default(),
|
2024-02-13 13:14:49 +04:00
|
|
|
preset_column_widths: Default::default(),
|
|
|
|
|
default_column_width: Default::default(),
|
|
|
|
|
center_focused_column: Default::default(),
|
2024-08-29 16:41:59 +02:00
|
|
|
always_center_single_column: false,
|
2024-11-29 06:46:13 +01:00
|
|
|
empty_workspace_above_first: false,
|
2025-02-06 08:42:09 +03:00
|
|
|
default_column_display: ColumnDisplay::Normal,
|
2024-06-17 09:16:28 +03:00
|
|
|
gaps: FloatOrInt(16.),
|
2024-02-13 13:14:49 +04:00
|
|
|
struts: Default::default(),
|
2024-09-05 23:37:10 +02:00
|
|
|
preset_window_heights: Default::default(),
|
2025-05-06 17:12:07 +03:00
|
|
|
background_color: DEFAULT_BACKGROUND_COLOR,
|
2024-02-13 13:14:49 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-21 19:58:03 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct SpawnAtStartup {
|
|
|
|
|
#[knuffel(arguments)]
|
|
|
|
|
pub command: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 09:25:07 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
2023-09-26 13:09:33 +04:00
|
|
|
pub struct FocusRing {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2024-02-12 09:34:54 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().width)]
|
2024-06-17 09:16:28 +03:00
|
|
|
pub width: FloatOrInt<0, 65535>,
|
2024-02-12 09:34:54 +04:00
|
|
|
#[knuffel(child, default = Self::default().active_color)]
|
2023-09-26 13:09:33 +04:00
|
|
|
pub active_color: Color,
|
2024-02-12 09:34:54 +04:00
|
|
|
#[knuffel(child, default = Self::default().inactive_color)]
|
2023-09-26 13:09:33 +04:00
|
|
|
pub inactive_color: Color,
|
2025-03-22 19:04:24 +01:00
|
|
|
#[knuffel(child, default = Self::default().urgent_color)]
|
|
|
|
|
pub urgent_color: Color,
|
2024-02-21 21:27:44 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub active_gradient: Option<Gradient>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_gradient: Option<Gradient>,
|
2025-03-22 19:04:24 +01:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub urgent_gradient: Option<Gradient>,
|
2023-09-26 13:09:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for FocusRing {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
2024-06-17 09:16:28 +03:00
|
|
|
width: FloatOrInt(4.),
|
2024-07-16 10:22:03 +03:00
|
|
|
active_color: Color::from_rgba8_unpremul(127, 200, 255, 255),
|
|
|
|
|
inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255),
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: Color::from_rgba8_unpremul(155, 0, 0, 255),
|
2024-02-21 21:27:44 +04:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2023-09-26 13:09:33 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 21:27:44 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Gradient {
|
|
|
|
|
#[knuffel(property, str)]
|
|
|
|
|
pub from: Color,
|
|
|
|
|
#[knuffel(property, str)]
|
|
|
|
|
pub to: Color,
|
|
|
|
|
#[knuffel(property, default = 180)]
|
|
|
|
|
pub angle: i16,
|
|
|
|
|
#[knuffel(property, default)]
|
|
|
|
|
pub relative_to: GradientRelativeTo,
|
2024-07-16 10:22:03 +03:00
|
|
|
#[knuffel(property(name = "in"), str, default)]
|
|
|
|
|
pub in_: GradientInterpolation,
|
2024-02-21 21:27:44 +04:00
|
|
|
}
|
|
|
|
|
|
2025-02-02 08:41:42 +03:00
|
|
|
impl From<Color> for Gradient {
|
|
|
|
|
fn from(value: Color) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
from: value,
|
|
|
|
|
to: value,
|
|
|
|
|
angle: 0,
|
|
|
|
|
relative_to: GradientRelativeTo::Window,
|
|
|
|
|
in_: GradientInterpolation::default(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 21:27:44 +04:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum GradientRelativeTo {
|
|
|
|
|
#[default]
|
|
|
|
|
Window,
|
|
|
|
|
WorkspaceView,
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
#[derive(Default, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct GradientInterpolation {
|
|
|
|
|
pub color_space: GradientColorSpace,
|
|
|
|
|
pub hue_interpolation: HueInterpolation,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum GradientColorSpace {
|
|
|
|
|
#[default]
|
|
|
|
|
Srgb,
|
|
|
|
|
SrgbLinear,
|
|
|
|
|
Oklab,
|
|
|
|
|
Oklch,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum HueInterpolation {
|
|
|
|
|
#[default]
|
|
|
|
|
Shorter,
|
|
|
|
|
Longer,
|
|
|
|
|
Increasing,
|
|
|
|
|
Decreasing,
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 09:34:54 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Border {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().width)]
|
2024-06-17 09:16:28 +03:00
|
|
|
pub width: FloatOrInt<0, 65535>,
|
2024-02-12 09:34:54 +04:00
|
|
|
#[knuffel(child, default = Self::default().active_color)]
|
|
|
|
|
pub active_color: Color,
|
|
|
|
|
#[knuffel(child, default = Self::default().inactive_color)]
|
|
|
|
|
pub inactive_color: Color,
|
2025-03-22 19:04:24 +01:00
|
|
|
#[knuffel(child, default = Self::default().urgent_color)]
|
|
|
|
|
pub urgent_color: Color,
|
2024-02-21 21:27:44 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub active_gradient: Option<Gradient>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_gradient: Option<Gradient>,
|
2025-03-22 19:04:24 +01:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub urgent_gradient: Option<Gradient>,
|
2024-02-12 09:34:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Border {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
2024-02-12 09:22:22 +04:00
|
|
|
off: true,
|
2024-06-17 09:16:28 +03:00
|
|
|
width: FloatOrInt(4.),
|
2024-07-16 10:22:03 +03:00
|
|
|
active_color: Color::from_rgba8_unpremul(255, 200, 127, 255),
|
|
|
|
|
inactive_color: Color::from_rgba8_unpremul(80, 80, 80, 255),
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: Color::from_rgba8_unpremul(155, 0, 0, 255),
|
2024-02-21 21:27:44 +04:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2024-02-12 09:22:22 +04:00
|
|
|
}
|
2023-12-27 21:51:42 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-12 09:34:54 +04:00
|
|
|
impl From<Border> for FocusRing {
|
|
|
|
|
fn from(value: Border) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: value.off,
|
|
|
|
|
width: value.width,
|
|
|
|
|
active_color: value.active_color,
|
|
|
|
|
inactive_color: value.inactive_color,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: value.urgent_color,
|
2024-02-21 21:27:44 +04:00
|
|
|
active_gradient: value.active_gradient,
|
|
|
|
|
inactive_gradient: value.inactive_gradient,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: value.urgent_gradient,
|
2024-02-12 09:34:54 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-24 22:17:53 +04:00
|
|
|
impl From<FocusRing> for Border {
|
|
|
|
|
fn from(value: FocusRing) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: value.off,
|
|
|
|
|
width: value.width,
|
|
|
|
|
active_color: value.active_color,
|
|
|
|
|
inactive_color: value.inactive_color,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: value.urgent_color,
|
2024-04-24 22:17:53 +04:00
|
|
|
active_gradient: value.active_gradient,
|
|
|
|
|
inactive_gradient: value.inactive_gradient,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: value.urgent_gradient,
|
2024-04-24 22:17:53 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-15 14:16:05 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Shadow {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub on: bool,
|
|
|
|
|
#[knuffel(child, default = Self::default().offset)]
|
|
|
|
|
pub offset: ShadowOffset,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().softness)]
|
|
|
|
|
pub softness: FloatOrInt<0, 1024>,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().spread)]
|
2025-03-09 18:55:17 +01:00
|
|
|
pub spread: FloatOrInt<-1024, 1024>,
|
2025-01-15 14:16:05 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().draw_behind_window)]
|
|
|
|
|
pub draw_behind_window: bool,
|
|
|
|
|
#[knuffel(child, default = Self::default().color)]
|
|
|
|
|
pub color: Color,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_color: Option<Color>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Shadow {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
on: false,
|
|
|
|
|
offset: ShadowOffset {
|
|
|
|
|
x: FloatOrInt(0.),
|
|
|
|
|
y: FloatOrInt(5.),
|
|
|
|
|
},
|
|
|
|
|
softness: FloatOrInt(30.),
|
|
|
|
|
spread: FloatOrInt(5.),
|
|
|
|
|
draw_behind_window: false,
|
|
|
|
|
color: Color::from_rgba8_unpremul(0, 0, 0, 0x70),
|
|
|
|
|
inactive_color: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct ShadowOffset {
|
|
|
|
|
#[knuffel(property, default)]
|
|
|
|
|
pub x: FloatOrInt<-65535, 65535>,
|
|
|
|
|
#[knuffel(property, default)]
|
|
|
|
|
pub y: FloatOrInt<-65535, 65535>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-01 09:36:10 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct WorkspaceShadow {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child, default = Self::default().offset)]
|
|
|
|
|
pub offset: ShadowOffset,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().softness)]
|
|
|
|
|
pub softness: FloatOrInt<0, 1024>,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().spread)]
|
|
|
|
|
pub spread: FloatOrInt<-1024, 1024>,
|
|
|
|
|
#[knuffel(child, default = Self::default().color)]
|
|
|
|
|
pub color: Color,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for WorkspaceShadow {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
|
|
|
|
offset: ShadowOffset {
|
|
|
|
|
x: FloatOrInt(0.),
|
2025-05-01 10:30:50 +03:00
|
|
|
y: FloatOrInt(10.),
|
2025-05-01 09:36:10 +03:00
|
|
|
},
|
2025-05-01 10:53:35 +03:00
|
|
|
softness: FloatOrInt(40.),
|
2025-05-01 10:30:50 +03:00
|
|
|
spread: FloatOrInt(10.),
|
2025-05-01 10:53:35 +03:00
|
|
|
color: Color::from_rgba8_unpremul(0, 0, 0, 0x50),
|
2025-05-01 09:36:10 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<WorkspaceShadow> for Shadow {
|
|
|
|
|
fn from(value: WorkspaceShadow) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
on: !value.off,
|
|
|
|
|
offset: value.offset,
|
|
|
|
|
softness: value.softness,
|
|
|
|
|
spread: value.spread,
|
|
|
|
|
draw_behind_window: false,
|
|
|
|
|
color: value.color,
|
|
|
|
|
inactive_color: None,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 08:41:42 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct TabIndicator {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2025-02-05 08:09:48 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub hide_when_single_tab: bool,
|
2025-02-07 09:36:08 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub place_within_column: bool,
|
2025-02-02 08:41:42 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().gap)]
|
|
|
|
|
pub gap: FloatOrInt<-65535, 65535>,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().width)]
|
|
|
|
|
pub width: FloatOrInt<0, 65535>,
|
|
|
|
|
#[knuffel(child, default = Self::default().length)]
|
|
|
|
|
pub length: TabIndicatorLength,
|
2025-02-05 10:36:46 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().position)]
|
|
|
|
|
pub position: TabIndicatorPosition,
|
2025-02-07 10:23:56 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().gaps_between_tabs)]
|
|
|
|
|
pub gaps_between_tabs: FloatOrInt<0, 65535>,
|
2025-02-12 07:59:21 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().corner_radius)]
|
|
|
|
|
pub corner_radius: FloatOrInt<0, 65535>,
|
2025-02-02 08:41:42 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub active_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2025-05-10 22:34:53 +03:00
|
|
|
pub urgent_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2025-02-02 08:41:42 +03:00
|
|
|
pub active_gradient: Option<Gradient>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_gradient: Option<Gradient>,
|
2025-05-10 22:34:53 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub urgent_gradient: Option<Gradient>,
|
2025-02-02 08:41:42 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for TabIndicator {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
2025-02-05 08:09:48 +03:00
|
|
|
hide_when_single_tab: false,
|
2025-02-07 09:36:08 +03:00
|
|
|
place_within_column: false,
|
2025-02-02 08:41:42 +03:00
|
|
|
gap: FloatOrInt(5.),
|
|
|
|
|
width: FloatOrInt(4.),
|
|
|
|
|
length: TabIndicatorLength {
|
|
|
|
|
total_proportion: Some(0.5),
|
|
|
|
|
},
|
2025-02-05 10:36:46 +03:00
|
|
|
position: TabIndicatorPosition::Left,
|
2025-02-07 10:23:56 +03:00
|
|
|
gaps_between_tabs: FloatOrInt(0.),
|
2025-02-12 07:59:21 +03:00
|
|
|
corner_radius: FloatOrInt(0.),
|
2025-02-02 08:41:42 +03:00
|
|
|
active_color: None,
|
|
|
|
|
inactive_color: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_color: None,
|
2025-02-02 08:41:42 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_gradient: None,
|
2025-02-02 08:41:42 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct TabIndicatorLength {
|
|
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub total_proportion: Option<f64>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 10:36:46 +03:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub enum TabIndicatorPosition {
|
|
|
|
|
Left,
|
|
|
|
|
Right,
|
|
|
|
|
Top,
|
|
|
|
|
Bottom,
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct InsertHint {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child, default = Self::default().color)]
|
|
|
|
|
pub color: Color,
|
2024-11-02 09:33:44 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub gradient: Option<Gradient>,
|
2024-07-15 15:51:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for InsertHint {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
|
|
|
|
color: Color::from_rgba8_unpremul(127, 200, 255, 128),
|
2024-11-02 09:33:44 +03:00
|
|
|
gradient: None,
|
2024-07-15 15:51:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
/// RGB color in [0, 1] with unpremultiplied alpha.
|
|
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
2023-09-26 13:09:33 +04:00
|
|
|
pub struct Color {
|
2024-07-16 10:22:03 +03:00
|
|
|
pub r: f32,
|
|
|
|
|
pub g: f32,
|
|
|
|
|
pub b: f32,
|
|
|
|
|
pub a: f32,
|
2023-09-26 13:09:33 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Color {
|
2024-07-16 10:22:03 +03:00
|
|
|
pub const fn new_unpremul(r: f32, g: f32, b: f32, a: f32) -> Self {
|
2023-09-26 13:09:33 +04:00
|
|
|
Self { r, g, b, a }
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
pub fn from_rgba8_unpremul(r: u8, g: u8, b: u8, a: u8) -> Self {
|
|
|
|
|
Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_array_premul([r, g, b, a]: [f32; 4]) -> Self {
|
|
|
|
|
let a = a.clamp(0., 1.);
|
|
|
|
|
|
|
|
|
|
if a == 0. {
|
|
|
|
|
Self::new_unpremul(0., 0., 0., 0.)
|
|
|
|
|
} else {
|
|
|
|
|
Self {
|
|
|
|
|
r: (r / a).clamp(0., 1.),
|
|
|
|
|
g: (g / a).clamp(0., 1.),
|
|
|
|
|
b: (b / a).clamp(0., 1.),
|
|
|
|
|
a,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-25 23:41:33 +05:30
|
|
|
pub const fn from_array_unpremul([r, g, b, a]: [f32; 4]) -> Self {
|
2024-07-16 10:22:03 +03:00
|
|
|
Self { r, g, b, a }
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-20 11:43:32 +03:00
|
|
|
pub fn from_color32f(color: Color32F) -> Self {
|
|
|
|
|
Self::from_array_premul(color.components())
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
pub fn to_array_unpremul(self) -> [f32; 4] {
|
|
|
|
|
[self.r, self.g, self.b, self.a]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn to_array_premul(self) -> [f32; 4] {
|
|
|
|
|
let [r, g, b, a] = [self.r, self.g, self.b, self.a];
|
2024-02-01 18:53:45 +04:00
|
|
|
[r * a, g * a, b * a, a]
|
2023-09-26 13:09:33 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-15 14:16:05 +03:00
|
|
|
impl Mul<f32> for Color {
|
|
|
|
|
type Output = Self;
|
|
|
|
|
|
|
|
|
|
fn mul(mut self, rhs: f32) -> Self::Output {
|
|
|
|
|
self.a *= rhs;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-10 13:29:00 +03:00
|
|
|
impl MulAssign<f32> for Color {
|
|
|
|
|
fn mul_assign(&mut self, rhs: f32) {
|
|
|
|
|
self.a *= rhs;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-01 17:42:56 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, PartialEq)]
|
|
|
|
|
pub struct Cursor {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = String::from("default"))]
|
|
|
|
|
pub xcursor_theme: String,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = 24)]
|
|
|
|
|
pub xcursor_size: u8,
|
2024-10-02 08:22:50 +08:00
|
|
|
#[knuffel(child)]
|
2024-10-29 12:46:58 +03:00
|
|
|
pub hide_when_typing: bool,
|
2024-10-02 08:22:50 +08:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub hide_after_inactive_ms: Option<u32>,
|
2023-10-01 17:42:56 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Cursor {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
xcursor_theme: String::from("default"),
|
|
|
|
|
xcursor_size: 24,
|
2024-10-29 12:46:58 +03:00
|
|
|
hide_when_typing: false,
|
2024-10-02 08:22:50 +08:00
|
|
|
hide_after_inactive_ms: None,
|
2023-10-01 17:42:56 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-05 09:25:07 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
2024-09-05 23:37:10 +02:00
|
|
|
pub enum PresetSize {
|
2023-10-05 09:25:07 +04:00
|
|
|
Proportion(#[knuffel(argument)] f64),
|
|
|
|
|
Fixed(#[knuffel(argument)] i32),
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-31 19:24:26 +03:00
|
|
|
impl From<PresetSize> for SizeChange {
|
|
|
|
|
fn from(value: PresetSize) -> Self {
|
|
|
|
|
match value {
|
|
|
|
|
PresetSize::Proportion(prop) => SizeChange::SetProportion(prop * 100.),
|
|
|
|
|
PresetSize::Fixed(fixed) => SizeChange::SetFixed(fixed),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-27 09:58:22 +03:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
2024-09-05 23:37:10 +02:00
|
|
|
pub struct DefaultPresetSize(pub Option<PresetSize>);
|
2023-11-02 21:07:29 +04:00
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
2023-12-21 08:37:30 +04:00
|
|
|
pub struct Struts {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2024-07-19 04:39:27 +05:00
|
|
|
pub left: FloatOrInt<-65535, 65535>,
|
2023-12-21 08:37:30 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2024-07-19 04:39:27 +05:00
|
|
|
pub right: FloatOrInt<-65535, 65535>,
|
2023-12-21 08:37:30 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2024-07-19 04:39:27 +05:00
|
|
|
pub top: FloatOrInt<-65535, 65535>,
|
2023-12-21 08:37:30 +04:00
|
|
|
#[knuffel(child, unwrap(argument), default)]
|
2024-07-19 04:39:27 +05:00
|
|
|
pub bottom: FloatOrInt<-65535, 65535>,
|
2023-12-21 08:37:30 +04:00
|
|
|
}
|
|
|
|
|
|
2024-01-20 08:31:05 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub struct HotkeyOverlay {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub skip_at_startup: bool,
|
2025-06-03 19:31:18 +02:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub hide_not_bound: bool,
|
2024-01-20 08:31:05 +04:00
|
|
|
}
|
|
|
|
|
|
2025-01-22 00:00:35 -05:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub struct Clipboard {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_primary: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-21 20:10:35 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
2024-02-07 17:05:15 +04:00
|
|
|
pub struct Animations {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
2025-06-09 14:02:17 +03:00
|
|
|
#[knuffel(child, unwrap(argument), default = FloatOrInt(1.))]
|
|
|
|
|
pub slowdown: FloatOrInt<0, { i32::MAX }>,
|
2024-04-17 14:06:32 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub workspace_switch: WorkspaceSwitchAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub window_open: WindowOpenAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub window_close: WindowCloseAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-04-18 17:36:12 +04:00
|
|
|
pub horizontal_view_movement: HorizontalViewMovementAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub window_movement: WindowMovementAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
2024-04-17 14:06:32 +04:00
|
|
|
pub window_resize: WindowResizeAnim,
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub config_notification_open_close: ConfigNotificationOpenCloseAnim,
|
2024-07-08 11:24:08 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub screenshot_ui_open: ScreenshotUiOpenAnim,
|
2025-04-25 09:36:50 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub overview_open_close: OverviewOpenCloseAnim,
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Animations {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
2025-06-09 14:02:17 +03:00
|
|
|
slowdown: FloatOrInt(1.),
|
2024-04-17 14:06:32 +04:00
|
|
|
workspace_switch: Default::default(),
|
|
|
|
|
horizontal_view_movement: Default::default(),
|
|
|
|
|
window_movement: Default::default(),
|
|
|
|
|
window_open: Default::default(),
|
|
|
|
|
window_close: Default::default(),
|
|
|
|
|
window_resize: Default::default(),
|
|
|
|
|
config_notification_open_close: Default::default(),
|
2024-07-08 11:24:08 +04:00
|
|
|
screenshot_ui_open: Default::default(),
|
2025-04-25 09:36:50 +03:00
|
|
|
overview_open_close: Default::default(),
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
2024-04-17 14:06:32 +04:00
|
|
|
pub struct WorkspaceSwitchAnim(pub Animation);
|
2024-02-07 17:05:15 +04:00
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
impl Default for WorkspaceSwitchAnim {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
2024-03-05 13:32:30 +04:00
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
|
|
|
|
damping_ratio: 1.,
|
|
|
|
|
stiffness: 1000,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
}),
|
2024-04-17 14:06:32 +04:00
|
|
|
})
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-02-07 17:05:15 +04:00
|
|
|
|
2024-05-15 19:38:29 +04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
|
pub struct WindowOpenAnim {
|
|
|
|
|
pub anim: Animation,
|
|
|
|
|
pub custom_shader: Option<String>,
|
|
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for WindowOpenAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
2024-05-15 19:38:29 +04:00
|
|
|
Self {
|
|
|
|
|
anim: Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Easing(EasingParams {
|
|
|
|
|
duration_ms: 150,
|
|
|
|
|
curve: AnimationCurve::EaseOutExpo,
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
custom_shader: None,
|
|
|
|
|
}
|
2024-04-08 18:23:18 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-04-08 18:23:18 +04:00
|
|
|
|
2024-05-12 09:52:21 +04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
|
pub struct WindowCloseAnim {
|
|
|
|
|
pub anim: Animation,
|
|
|
|
|
pub custom_shader: Option<String>,
|
|
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for WindowCloseAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
2024-05-12 09:52:21 +04:00
|
|
|
Self {
|
|
|
|
|
anim: Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Easing(EasingParams {
|
|
|
|
|
duration_ms: 150,
|
|
|
|
|
curve: AnimationCurve::EaseOutQuad,
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
custom_shader: None,
|
|
|
|
|
}
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-02-07 17:05:15 +04:00
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
2024-04-18 17:36:12 +04:00
|
|
|
pub struct HorizontalViewMovementAnim(pub Animation);
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for HorizontalViewMovementAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
2024-03-05 13:32:30 +04:00
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
2024-04-18 17:36:12 +04:00
|
|
|
damping_ratio: 1.,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
2024-03-05 13:32:30 +04:00
|
|
|
}),
|
2024-04-17 14:06:32 +04:00
|
|
|
})
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-02-07 17:05:15 +04:00
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
2024-04-18 17:36:12 +04:00
|
|
|
pub struct WindowMovementAnim(pub Animation);
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for WindowMovementAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
2024-03-05 13:32:30 +04:00
|
|
|
off: false,
|
2024-04-18 17:36:12 +04:00
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
|
|
|
|
damping_ratio: 1.,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
2024-03-05 13:32:30 +04:00
|
|
|
}),
|
2024-04-17 14:06:32 +04:00
|
|
|
})
|
2024-03-05 13:32:30 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-04-09 22:37:10 +04:00
|
|
|
|
2024-04-21 20:10:35 +04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
|
|
pub struct WindowResizeAnim {
|
|
|
|
|
pub anim: Animation,
|
|
|
|
|
pub custom_shader: Option<String>,
|
|
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for WindowResizeAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
2024-04-21 20:10:35 +04:00
|
|
|
Self {
|
|
|
|
|
anim: Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
|
|
|
|
damping_ratio: 1.,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
}),
|
|
|
|
|
},
|
|
|
|
|
custom_shader: None,
|
|
|
|
|
}
|
2024-04-09 22:37:10 +04:00
|
|
|
}
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
2024-04-13 11:07:23 +04:00
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
2024-04-18 17:36:12 +04:00
|
|
|
pub struct ConfigNotificationOpenCloseAnim(pub Animation);
|
2024-04-17 14:06:32 +04:00
|
|
|
|
2024-04-18 17:36:12 +04:00
|
|
|
impl Default for ConfigNotificationOpenCloseAnim {
|
2024-04-17 14:06:32 +04:00
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
2024-04-13 11:07:23 +04:00
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
2024-04-18 17:36:12 +04:00
|
|
|
damping_ratio: 0.6,
|
|
|
|
|
stiffness: 1000,
|
|
|
|
|
epsilon: 0.001,
|
2024-04-13 11:07:23 +04:00
|
|
|
}),
|
2024-04-17 14:06:32 +04:00
|
|
|
})
|
2024-04-13 11:07:23 +04:00
|
|
|
}
|
2024-03-05 13:32:30 +04:00
|
|
|
}
|
|
|
|
|
|
2024-07-08 11:24:08 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct ScreenshotUiOpenAnim(pub Animation);
|
|
|
|
|
|
|
|
|
|
impl Default for ScreenshotUiOpenAnim {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Easing(EasingParams {
|
|
|
|
|
duration_ms: 200,
|
|
|
|
|
curve: AnimationCurve::EaseOutQuad,
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 09:36:50 +03:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct OverviewOpenCloseAnim(pub Animation);
|
|
|
|
|
|
|
|
|
|
impl Default for OverviewOpenCloseAnim {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self(Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: AnimationKind::Spring(SpringParams {
|
|
|
|
|
damping_ratio: 1.,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
}),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Animation {
|
|
|
|
|
pub off: bool,
|
|
|
|
|
pub kind: AnimationKind,
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub enum AnimationKind {
|
|
|
|
|
Easing(EasingParams),
|
|
|
|
|
Spring(SpringParams),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct EasingParams {
|
2024-04-17 14:06:32 +04:00
|
|
|
pub duration_ms: u32,
|
|
|
|
|
pub curve: AnimationCurve,
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub enum AnimationCurve {
|
2024-05-12 09:50:16 +04:00
|
|
|
Linear,
|
2024-04-09 21:36:19 +04:00
|
|
|
EaseOutQuad,
|
2024-02-07 17:05:15 +04:00
|
|
|
EaseOutCubic,
|
|
|
|
|
EaseOutExpo,
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct SpringParams {
|
|
|
|
|
pub damping_ratio: f64,
|
|
|
|
|
pub stiffness: u32,
|
|
|
|
|
pub epsilon: f64,
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-16 08:46:38 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Gestures {
|
|
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub dnd_edge_view_scroll: DndEdgeViewScroll,
|
2025-04-25 10:02:31 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch,
|
2025-04-25 10:08:26 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub hot_corners: HotCorners,
|
2025-02-16 08:46:38 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct DndEdgeViewScroll {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().trigger_width)]
|
|
|
|
|
pub trigger_width: FloatOrInt<0, 65535>,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().delay_ms)]
|
|
|
|
|
pub delay_ms: u16,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().max_speed)]
|
|
|
|
|
pub max_speed: FloatOrInt<0, 1_000_000>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for DndEdgeViewScroll {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
trigger_width: FloatOrInt(30.), // Taken from GTK 4.
|
2025-02-19 07:49:09 +03:00
|
|
|
delay_ms: 100,
|
2025-02-16 08:46:38 +03:00
|
|
|
max_speed: FloatOrInt(1500.),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 10:02:31 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct DndEdgeWorkspaceSwitch {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().trigger_height)]
|
|
|
|
|
pub trigger_height: FloatOrInt<0, 65535>,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().delay_ms)]
|
|
|
|
|
pub delay_ms: u16,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().max_speed)]
|
|
|
|
|
pub max_speed: FloatOrInt<0, 1_000_000>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for DndEdgeWorkspaceSwitch {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
trigger_height: FloatOrInt(50.),
|
|
|
|
|
delay_ms: 100,
|
|
|
|
|
max_speed: FloatOrInt(1500.),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 10:08:26 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct HotCorners {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 09:36:50 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct Overview {
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().zoom)]
|
|
|
|
|
pub zoom: FloatOrInt<0, 1>,
|
2025-04-28 07:53:03 +03:00
|
|
|
#[knuffel(child, default = Self::default().backdrop_color)]
|
|
|
|
|
pub backdrop_color: Color,
|
2025-05-01 09:36:10 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub workspace_shadow: WorkspaceShadow,
|
2025-04-25 09:36:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Overview {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
zoom: FloatOrInt(0.5),
|
2025-04-28 07:53:03 +03:00
|
|
|
backdrop_color: DEFAULT_BACKDROP_COLOR,
|
2025-05-01 09:36:10 +03:00
|
|
|
workspace_shadow: WorkspaceShadow::default(),
|
2025-04-25 09:36:50 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 10:08:56 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct Environment(#[knuffel(children)] pub Vec<EnvironmentVariable>);
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct EnvironmentVariable {
|
|
|
|
|
#[knuffel(node_name)]
|
|
|
|
|
pub name: String,
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
pub value: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-04 08:26:51 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct XwaylandSatellite {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument), default = Self::default().path)]
|
|
|
|
|
pub path: String,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for XwaylandSatellite {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: false,
|
|
|
|
|
path: String::from("xwayland-satellite"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-11 22:40:30 +02:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct Workspace {
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
pub name: WorkspaceName,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub open_on_output: Option<String>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
|
|
|
pub struct WorkspaceName(pub String);
|
|
|
|
|
|
2024-02-13 17:46:37 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
|
|
|
|
pub struct WindowRule {
|
|
|
|
|
#[knuffel(children(name = "match"))]
|
|
|
|
|
pub matches: Vec<Match>,
|
|
|
|
|
#[knuffel(children(name = "exclude"))]
|
|
|
|
|
pub excludes: Vec<Match>,
|
|
|
|
|
|
2024-03-19 15:20:03 +04:00
|
|
|
// Rules applied at initial configure.
|
2024-02-13 17:46:37 +04:00
|
|
|
#[knuffel(child)]
|
2024-09-05 23:37:10 +02:00
|
|
|
pub default_column_width: Option<DefaultPresetSize>,
|
2024-12-27 09:58:22 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub default_window_height: Option<DefaultPresetSize>,
|
2024-02-13 17:46:37 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub open_on_output: Option<String>,
|
2024-02-23 14:24:39 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
2024-05-11 22:40:30 +02:00
|
|
|
pub open_on_workspace: Option<String>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
2024-02-23 14:24:39 +04:00
|
|
|
pub open_maximized: Option<bool>,
|
2024-02-24 08:44:21 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub open_fullscreen: Option<bool>,
|
2024-11-29 21:11:02 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub open_floating: Option<bool>,
|
2024-12-27 09:31:32 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub open_focused: Option<bool>,
|
2024-03-19 15:20:03 +04:00
|
|
|
|
|
|
|
|
// Rules applied dynamically.
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub min_width: Option<u16>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub min_height: Option<u16>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub max_width: Option<u16>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub max_height: Option<u16>,
|
2024-03-19 18:22:25 +04:00
|
|
|
|
2024-04-24 22:17:53 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub focus_ring: BorderRule,
|
2024-04-24 21:49:07 +04:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub border: BorderRule,
|
2025-01-15 14:16:05 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub shadow: ShadowRule,
|
2025-02-02 08:41:42 +03:00
|
|
|
#[knuffel(child, default)]
|
|
|
|
|
pub tab_indicator: TabIndicatorRule,
|
2024-03-19 18:22:25 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub draw_border_with_background: Option<bool>,
|
2024-03-24 08:30:26 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub opacity: Option<f32>,
|
2024-05-01 19:06:08 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub geometry_corner_radius: Option<CornerRadius>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub clip_to_geometry: Option<bool>,
|
2024-03-24 09:03:59 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
2025-02-11 15:35:06 +03:00
|
|
|
pub baba_is_float: Option<bool>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
2024-03-24 09:03:59 +04:00
|
|
|
pub block_out_from: Option<BlockOutFrom>,
|
2024-08-22 18:58:07 +10:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub variable_refresh_rate: Option<bool>,
|
2025-02-06 09:09:07 +03:00
|
|
|
#[knuffel(child, unwrap(argument, str))]
|
|
|
|
|
pub default_column_display: Option<ColumnDisplay>,
|
2024-12-29 10:39:21 +03:00
|
|
|
#[knuffel(child)]
|
2025-01-15 14:16:05 +03:00
|
|
|
pub default_floating_position: Option<FloatingPosition>,
|
2025-01-20 22:52:43 +02:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub scroll_factor: Option<FloatOrInt<0, 100>>,
|
2025-03-13 11:32:54 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub tiled_state: Option<bool>,
|
2024-02-13 17:46:37 +04:00
|
|
|
}
|
|
|
|
|
|
2024-11-14 09:44:07 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
2024-02-13 17:46:37 +04:00
|
|
|
pub struct Match {
|
|
|
|
|
#[knuffel(property, str)]
|
2024-11-14 09:44:07 +03:00
|
|
|
pub app_id: Option<RegexEq>,
|
2024-02-13 17:46:37 +04:00
|
|
|
#[knuffel(property, str)]
|
2024-11-14 09:44:07 +03:00
|
|
|
pub title: Option<RegexEq>,
|
2024-03-23 14:38:07 +04:00
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub is_active: Option<bool>,
|
2024-03-23 16:16:52 +04:00
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub is_focused: Option<bool>,
|
2024-04-22 22:51:52 +02:00
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub is_active_in_column: Option<bool>,
|
2024-05-16 11:43:13 +04:00
|
|
|
#[knuffel(property)]
|
2024-12-27 11:20:03 +03:00
|
|
|
pub is_floating: Option<bool>,
|
|
|
|
|
#[knuffel(property)]
|
2025-02-11 09:52:05 +03:00
|
|
|
pub is_window_cast_target: Option<bool>,
|
|
|
|
|
#[knuffel(property)]
|
2025-05-10 22:48:11 +03:00
|
|
|
pub is_urgent: Option<bool>,
|
|
|
|
|
#[knuffel(property)]
|
2024-05-16 11:43:13 +04:00
|
|
|
pub at_startup: Option<bool>,
|
2024-02-13 17:46:37 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:06:08 +04:00
|
|
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct CornerRadius {
|
|
|
|
|
pub top_left: f32,
|
|
|
|
|
pub top_right: f32,
|
|
|
|
|
pub bottom_right: f32,
|
|
|
|
|
pub bottom_left: f32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<CornerRadius> for [f32; 4] {
|
|
|
|
|
fn from(value: CornerRadius) -> Self {
|
|
|
|
|
[
|
|
|
|
|
value.top_left,
|
|
|
|
|
value.top_right,
|
|
|
|
|
value.bottom_right,
|
|
|
|
|
value.bottom_left,
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 07:59:21 +03:00
|
|
|
impl From<f32> for CornerRadius {
|
|
|
|
|
fn from(value: f32) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
top_left: value,
|
|
|
|
|
top_right: value,
|
|
|
|
|
bottom_right: value,
|
|
|
|
|
bottom_left: value,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-24 09:03:59 +04:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum BlockOutFrom {
|
|
|
|
|
Screencast,
|
|
|
|
|
ScreenCapture,
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-24 21:49:07 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct BorderRule {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub on: bool,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
2024-06-17 09:16:28 +03:00
|
|
|
pub width: Option<FloatOrInt<0, 65535>>,
|
2024-04-24 21:49:07 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub active_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2025-03-22 19:04:24 +01:00
|
|
|
pub urgent_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2024-04-24 21:49:07 +04:00
|
|
|
pub active_gradient: Option<Gradient>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_gradient: Option<Gradient>,
|
2025-03-22 19:04:24 +01:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub urgent_gradient: Option<Gradient>,
|
2024-04-24 21:49:07 +04:00
|
|
|
}
|
|
|
|
|
|
2025-01-15 14:16:05 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct ShadowRule {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub off: bool,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub on: bool,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub offset: Option<ShadowOffset>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub softness: Option<FloatOrInt<0, 1024>>,
|
|
|
|
|
#[knuffel(child, unwrap(argument))]
|
2025-03-09 18:55:17 +01:00
|
|
|
pub spread: Option<FloatOrInt<-1024, 1024>>,
|
2025-01-15 14:16:05 +03:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub draw_behind_window: Option<bool>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_color: Option<Color>,
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 08:41:42 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq)]
|
|
|
|
|
pub struct TabIndicatorRule {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub active_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2025-05-10 22:34:53 +03:00
|
|
|
pub urgent_color: Option<Color>,
|
|
|
|
|
#[knuffel(child)]
|
2025-02-02 08:41:42 +03:00
|
|
|
pub active_gradient: Option<Gradient>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub inactive_gradient: Option<Gradient>,
|
2025-05-10 22:34:53 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub urgent_gradient: Option<Gradient>,
|
2025-02-02 08:41:42 +03:00
|
|
|
}
|
|
|
|
|
|
2024-12-29 10:39:21 +03:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
|
2025-01-15 14:16:05 +03:00
|
|
|
pub struct FloatingPosition {
|
2024-12-29 10:39:21 +03:00
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub x: FloatOrInt<-65535, 65535>,
|
|
|
|
|
#[knuffel(property)]
|
|
|
|
|
pub y: FloatOrInt<-65535, 65535>,
|
2024-12-30 13:22:02 +03:00
|
|
|
#[knuffel(property, default)]
|
|
|
|
|
pub relative_to: RelativeTo,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum RelativeTo {
|
|
|
|
|
#[default]
|
|
|
|
|
TopLeft,
|
|
|
|
|
TopRight,
|
|
|
|
|
BottomLeft,
|
|
|
|
|
BottomRight,
|
2025-03-01 18:46:27 +01:00
|
|
|
Top,
|
|
|
|
|
Bottom,
|
|
|
|
|
Left,
|
|
|
|
|
Right,
|
2024-12-29 10:39:21 +03:00
|
|
|
}
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
#[derive(Debug, Default, PartialEq)]
|
|
|
|
|
pub struct Binds(pub Vec<Bind>);
|
2023-09-05 12:58:51 +04:00
|
|
|
|
2024-03-22 20:27:58 +04:00
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub struct Bind {
|
|
|
|
|
pub key: Key,
|
2024-03-01 12:50:49 +01:00
|
|
|
pub action: Action,
|
2024-06-30 22:37:44 +05:00
|
|
|
pub repeat: bool,
|
2024-03-22 20:47:40 +04:00
|
|
|
pub cooldown: Option<Duration>,
|
2024-04-19 10:49:46 +04:00
|
|
|
pub allow_when_locked: bool,
|
2025-01-18 15:26:42 +01:00
|
|
|
pub allow_inhibiting: bool,
|
2025-02-13 08:45:23 +03:00
|
|
|
pub hotkey_overlay_title: Option<Option<String>>,
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub struct Key {
|
2024-03-22 10:36:19 +04:00
|
|
|
pub trigger: Trigger,
|
2023-09-05 12:58:51 +04:00
|
|
|
pub modifiers: Modifiers,
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-22 10:36:19 +04:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
|
|
|
|
|
pub enum Trigger {
|
|
|
|
|
Keysym(Keysym),
|
2024-12-13 02:40:36 +02:00
|
|
|
MouseLeft,
|
|
|
|
|
MouseRight,
|
|
|
|
|
MouseMiddle,
|
|
|
|
|
MouseBack,
|
|
|
|
|
MouseForward,
|
2024-03-23 08:49:58 +04:00
|
|
|
WheelScrollDown,
|
|
|
|
|
WheelScrollUp,
|
|
|
|
|
WheelScrollLeft,
|
|
|
|
|
WheelScrollRight,
|
2024-03-23 20:23:21 +04:00
|
|
|
TouchpadScrollDown,
|
|
|
|
|
TouchpadScrollUp,
|
|
|
|
|
TouchpadScrollLeft,
|
|
|
|
|
TouchpadScrollRight,
|
2024-03-22 10:36:19 +04:00
|
|
|
}
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
bitflags! {
|
2024-03-01 12:50:49 +01:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub struct Modifiers : u8 {
|
|
|
|
|
const CTRL = 1;
|
2024-07-31 11:00:35 -04:00
|
|
|
const SHIFT = 1 << 1;
|
|
|
|
|
const ALT = 1 << 2;
|
|
|
|
|
const SUPER = 1 << 3;
|
|
|
|
|
const ISO_LEVEL3_SHIFT = 1 << 4;
|
|
|
|
|
const ISO_LEVEL5_SHIFT = 1 << 5;
|
|
|
|
|
const COMPOSITOR = 1 << 6;
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-18 16:00:40 +02:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)]
|
|
|
|
|
pub struct SwitchBinds {
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub lid_open: Option<SwitchAction>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub lid_close: Option<SwitchAction>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub tablet_mode_on: Option<SwitchAction>,
|
|
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub tablet_mode_off: Option<SwitchAction>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
|
|
|
|
pub struct SwitchAction {
|
|
|
|
|
#[knuffel(child, unwrap(arguments))]
|
|
|
|
|
pub spawn: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-10 09:33:32 +04:00
|
|
|
// Remember to add new actions to the CLI enum too.
|
2023-10-03 11:38:42 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
2023-09-05 12:58:51 +04:00
|
|
|
pub enum Action {
|
2024-02-12 07:53:06 +04:00
|
|
|
Quit(#[knuffel(property(name = "skip-confirmation"), default)] bool),
|
2023-09-05 12:58:51 +04:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ChangeVt(i32),
|
|
|
|
|
Suspend,
|
2023-10-09 18:37:43 +04:00
|
|
|
PowerOffMonitors,
|
2024-10-09 01:50:06 -07:00
|
|
|
PowerOnMonitors,
|
2023-09-05 12:58:51 +04:00
|
|
|
ToggleDebugTint,
|
2024-05-02 17:52:06 +04:00
|
|
|
DebugToggleOpaqueRegions,
|
2024-05-03 10:25:51 +04:00
|
|
|
DebugToggleDamage,
|
2023-09-05 12:58:51 +04:00
|
|
|
Spawn(#[knuffel(arguments)] Vec<String>),
|
2024-05-07 22:06:43 +04:00
|
|
|
DoScreenTransition(#[knuffel(property(name = "delay-ms"))] Option<u16>),
|
2023-10-30 20:29:03 +04:00
|
|
|
#[knuffel(skip)]
|
2025-01-14 09:10:45 +01:00
|
|
|
ConfirmScreenshot {
|
|
|
|
|
write_to_disk: bool,
|
|
|
|
|
},
|
2023-10-30 20:29:03 +04:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
CancelScreenshot,
|
2024-07-07 09:23:40 +04:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ScreenshotTogglePointer,
|
2025-03-09 20:55:30 +00:00
|
|
|
Screenshot(#[knuffel(property(name = "show-pointer"), default = true)] bool),
|
|
|
|
|
ScreenshotScreen(
|
|
|
|
|
#[knuffel(property(name = "write-to-disk"), default = true)] bool,
|
|
|
|
|
#[knuffel(property(name = "show-pointer"), default = true)] bool,
|
|
|
|
|
),
|
2025-01-14 09:10:45 +01:00
|
|
|
ScreenshotWindow(#[knuffel(property(name = "write-to-disk"), default = true)] bool),
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
2025-01-14 09:10:45 +01:00
|
|
|
ScreenshotWindowById {
|
|
|
|
|
id: u64,
|
|
|
|
|
write_to_disk: bool,
|
|
|
|
|
},
|
2025-01-18 15:26:42 +01:00
|
|
|
ToggleKeyboardShortcutsInhibit,
|
2023-09-05 12:58:51 +04:00
|
|
|
CloseWindow,
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
CloseWindowById(u64),
|
2023-09-05 12:58:51 +04:00
|
|
|
FullscreenWindow,
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FullscreenWindowById(u64),
|
2025-03-17 14:56:29 +03:00
|
|
|
ToggleWindowedFullscreen,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ToggleWindowedFullscreenById(u64),
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FocusWindow(u64),
|
2025-02-05 17:18:21 +03:00
|
|
|
FocusWindowInColumn(#[knuffel(argument)] u8),
|
2024-11-21 12:48:51 +01:00
|
|
|
FocusWindowPrevious,
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusColumnLeft,
|
2025-04-25 09:47:45 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FocusColumnLeftUnderMouse,
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusColumnRight,
|
2025-04-25 09:47:45 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FocusColumnRightUnderMouse,
|
2023-12-29 07:51:14 +04:00
|
|
|
FocusColumnFirst,
|
|
|
|
|
FocusColumnLast,
|
2024-06-09 21:14:51 +10:00
|
|
|
FocusColumnRightOrFirst,
|
|
|
|
|
FocusColumnLeftOrLast,
|
2025-03-13 03:05:55 +01:00
|
|
|
FocusColumn(#[knuffel(argument)] usize),
|
2024-07-05 06:55:04 +02:00
|
|
|
FocusWindowOrMonitorUp,
|
|
|
|
|
FocusWindowOrMonitorDown,
|
2024-06-28 11:44:24 -03:00
|
|
|
FocusColumnOrMonitorLeft,
|
|
|
|
|
FocusColumnOrMonitorRight,
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusWindowDown,
|
|
|
|
|
FocusWindowUp,
|
2024-05-22 13:53:44 -06:00
|
|
|
FocusWindowDownOrColumnLeft,
|
|
|
|
|
FocusWindowDownOrColumnRight,
|
|
|
|
|
FocusWindowUpOrColumnLeft,
|
|
|
|
|
FocusWindowUpOrColumnRight,
|
2023-12-19 19:25:05 +11:00
|
|
|
FocusWindowOrWorkspaceDown,
|
|
|
|
|
FocusWindowOrWorkspaceUp,
|
2025-02-05 17:18:21 +03:00
|
|
|
FocusWindowTop,
|
|
|
|
|
FocusWindowBottom,
|
|
|
|
|
FocusWindowDownOrTop,
|
|
|
|
|
FocusWindowUpOrBottom,
|
2023-09-05 12:58:51 +04:00
|
|
|
MoveColumnLeft,
|
|
|
|
|
MoveColumnRight,
|
2023-12-29 08:01:02 +04:00
|
|
|
MoveColumnToFirst,
|
|
|
|
|
MoveColumnToLast,
|
2024-07-09 21:52:48 -07:00
|
|
|
MoveColumnLeftOrToMonitorLeft,
|
|
|
|
|
MoveColumnRightOrToMonitorRight,
|
2025-03-13 03:09:06 +01:00
|
|
|
MoveColumnToIndex(#[knuffel(argument)] usize),
|
2023-09-05 12:58:51 +04:00
|
|
|
MoveWindowDown,
|
|
|
|
|
MoveWindowUp,
|
2023-12-19 19:25:05 +11:00
|
|
|
MoveWindowDownOrToWorkspaceDown,
|
|
|
|
|
MoveWindowUpOrToWorkspaceUp,
|
2024-02-03 20:25:08 +01:00
|
|
|
ConsumeOrExpelWindowLeft,
|
2024-10-11 19:52:03 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ConsumeOrExpelWindowLeftById(u64),
|
2024-02-03 20:25:08 +01:00
|
|
|
ConsumeOrExpelWindowRight,
|
2024-10-11 19:52:03 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ConsumeOrExpelWindowRightById(u64),
|
2023-09-05 12:58:51 +04:00
|
|
|
ConsumeWindowIntoColumn,
|
|
|
|
|
ExpelWindowFromColumn,
|
2025-01-09 08:29:36 +00:00
|
|
|
SwapWindowLeft,
|
|
|
|
|
SwapWindowRight,
|
2025-02-01 10:46:52 +03:00
|
|
|
ToggleColumnTabbedDisplay,
|
2025-02-06 08:42:09 +03:00
|
|
|
SetColumnDisplay(#[knuffel(argument, str)] ColumnDisplay),
|
2023-11-13 19:08:29 +04:00
|
|
|
CenterColumn,
|
2024-12-29 22:44:19 +03:00
|
|
|
CenterWindow,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
CenterWindowById(u64),
|
2025-05-12 14:11:38 +03:00
|
|
|
CenterVisibleColumns,
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusWorkspaceDown,
|
2025-04-25 09:47:45 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FocusWorkspaceDownUnderMouse,
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusWorkspaceUp,
|
2025-04-25 09:47:45 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
FocusWorkspaceUpUnderMouse,
|
2024-05-11 22:40:30 +02:00
|
|
|
FocusWorkspace(#[knuffel(argument)] WorkspaceReference),
|
2024-03-19 14:27:52 +00:00
|
|
|
FocusWorkspacePrevious,
|
2023-09-05 12:58:51 +04:00
|
|
|
MoveWindowToWorkspaceDown,
|
|
|
|
|
MoveWindowToWorkspaceUp,
|
2025-03-29 02:40:08 -04:00
|
|
|
MoveWindowToWorkspace(
|
|
|
|
|
#[knuffel(argument)] WorkspaceReference,
|
|
|
|
|
#[knuffel(property(name = "focus"), default = true)] bool,
|
|
|
|
|
),
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWindowToWorkspaceById {
|
|
|
|
|
window_id: u64,
|
|
|
|
|
reference: WorkspaceReference,
|
2025-03-29 02:40:08 -04:00
|
|
|
focus: bool,
|
2024-09-06 15:10:01 +03:00
|
|
|
},
|
2025-05-01 20:36:33 +03:00
|
|
|
MoveColumnToWorkspaceDown(#[knuffel(property(name = "focus"), default = true)] bool),
|
|
|
|
|
MoveColumnToWorkspaceUp(#[knuffel(property(name = "focus"), default = true)] bool),
|
|
|
|
|
MoveColumnToWorkspace(
|
|
|
|
|
#[knuffel(argument)] WorkspaceReference,
|
|
|
|
|
#[knuffel(property(name = "focus"), default = true)] bool,
|
|
|
|
|
),
|
2023-10-14 20:42:10 +04:00
|
|
|
MoveWorkspaceDown,
|
|
|
|
|
MoveWorkspaceUp,
|
2025-01-25 10:49:51 +02:00
|
|
|
MoveWorkspaceToIndex(#[knuffel(argument)] usize),
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWorkspaceToIndexByRef {
|
|
|
|
|
new_idx: usize,
|
|
|
|
|
reference: WorkspaceReference,
|
|
|
|
|
},
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWorkspaceToMonitorByRef {
|
|
|
|
|
output_name: String,
|
|
|
|
|
reference: WorkspaceReference,
|
|
|
|
|
},
|
|
|
|
|
MoveWorkspaceToMonitor(#[knuffel(argument)] String),
|
2025-01-10 06:03:19 +00:00
|
|
|
SetWorkspaceName(#[knuffel(argument)] String),
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SetWorkspaceNameByRef {
|
|
|
|
|
name: String,
|
|
|
|
|
reference: WorkspaceReference,
|
|
|
|
|
},
|
|
|
|
|
UnsetWorkspaceName,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
UnsetWorkSpaceNameByRef(#[knuffel(argument)] WorkspaceReference),
|
2023-09-05 12:58:51 +04:00
|
|
|
FocusMonitorLeft,
|
|
|
|
|
FocusMonitorRight,
|
|
|
|
|
FocusMonitorDown,
|
|
|
|
|
FocusMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
FocusMonitorPrevious,
|
|
|
|
|
FocusMonitorNext,
|
2025-02-20 20:25:43 +01:00
|
|
|
FocusMonitor(#[knuffel(argument)] String),
|
2023-09-05 12:58:51 +04:00
|
|
|
MoveWindowToMonitorLeft,
|
|
|
|
|
MoveWindowToMonitorRight,
|
|
|
|
|
MoveWindowToMonitorDown,
|
|
|
|
|
MoveWindowToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
MoveWindowToMonitorPrevious,
|
|
|
|
|
MoveWindowToMonitorNext,
|
2025-02-20 23:09:59 +01:00
|
|
|
MoveWindowToMonitor(#[knuffel(argument)] String),
|
2025-03-02 12:09:44 +01:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWindowToMonitorById {
|
|
|
|
|
id: u64,
|
|
|
|
|
output: String,
|
|
|
|
|
},
|
2024-01-15 10:36:59 +04:00
|
|
|
MoveColumnToMonitorLeft,
|
|
|
|
|
MoveColumnToMonitorRight,
|
|
|
|
|
MoveColumnToMonitorDown,
|
|
|
|
|
MoveColumnToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
MoveColumnToMonitorPrevious,
|
|
|
|
|
MoveColumnToMonitorNext,
|
2025-02-20 23:09:59 +01:00
|
|
|
MoveColumnToMonitor(#[knuffel(argument)] String),
|
2024-12-28 10:12:50 +03:00
|
|
|
SetWindowWidth(#[knuffel(argument, str)] SizeChange),
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SetWindowWidthById {
|
|
|
|
|
id: u64,
|
|
|
|
|
change: SizeChange,
|
|
|
|
|
},
|
2023-11-08 11:17:06 +04:00
|
|
|
SetWindowHeight(#[knuffel(argument, str)] SizeChange),
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SetWindowHeightById {
|
|
|
|
|
id: u64,
|
|
|
|
|
change: SizeChange,
|
|
|
|
|
},
|
2024-05-11 09:33:23 +04:00
|
|
|
ResetWindowHeight,
|
2024-09-06 15:10:01 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ResetWindowHeightById(u64),
|
2023-09-05 12:58:51 +04:00
|
|
|
SwitchPresetColumnWidth,
|
2024-12-30 09:05:35 +03:00
|
|
|
SwitchPresetWindowWidth,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SwitchPresetWindowWidthById(u64),
|
2024-09-05 23:37:10 +02:00
|
|
|
SwitchPresetWindowHeight,
|
2024-09-12 11:53:10 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SwitchPresetWindowHeightById(u64),
|
2023-09-05 12:58:51 +04:00
|
|
|
MaximizeColumn,
|
2023-10-03 11:38:42 +04:00
|
|
|
SetColumnWidth(#[knuffel(argument, str)] SizeChange),
|
2025-02-17 21:22:10 +03:00
|
|
|
ExpandColumnToAvailableWidth,
|
2024-02-10 09:33:32 +04:00
|
|
|
SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget),
|
2024-01-18 19:20:46 +04:00
|
|
|
ShowHotkeyOverlay,
|
2023-12-02 23:57:01 +01:00
|
|
|
MoveWorkspaceToMonitorLeft,
|
|
|
|
|
MoveWorkspaceToMonitorRight,
|
|
|
|
|
MoveWorkspaceToMonitorDown,
|
|
|
|
|
MoveWorkspaceToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
MoveWorkspaceToMonitorPrevious,
|
|
|
|
|
MoveWorkspaceToMonitorNext,
|
2024-11-29 21:11:02 +03:00
|
|
|
ToggleWindowFloating,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ToggleWindowFloatingById(u64),
|
2024-12-28 10:13:30 +03:00
|
|
|
MoveWindowToFloating,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWindowToFloatingById(u64),
|
|
|
|
|
MoveWindowToTiling,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveWindowToTilingById(u64),
|
2024-12-28 10:14:02 +03:00
|
|
|
FocusFloating,
|
|
|
|
|
FocusTiling,
|
2024-11-29 21:11:02 +03:00
|
|
|
SwitchFocusBetweenFloatingAndTiling,
|
2024-12-28 11:40:16 +03:00
|
|
|
#[knuffel(skip)]
|
|
|
|
|
MoveFloatingWindowById {
|
|
|
|
|
id: Option<u64>,
|
|
|
|
|
x: PositionChange,
|
|
|
|
|
y: PositionChange,
|
|
|
|
|
},
|
2025-01-23 10:40:52 +03:00
|
|
|
ToggleWindowRuleOpacity,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
ToggleWindowRuleOpacityById(u64),
|
2025-03-15 11:23:01 +03:00
|
|
|
SetDynamicCastWindow,
|
|
|
|
|
#[knuffel(skip)]
|
|
|
|
|
SetDynamicCastWindowById(u64),
|
|
|
|
|
SetDynamicCastMonitor(#[knuffel(argument)] Option<String>),
|
|
|
|
|
ClearDynamicCastTarget,
|
2025-04-25 09:36:50 +03:00
|
|
|
ToggleOverview,
|
|
|
|
|
OpenOverview,
|
|
|
|
|
CloseOverview,
|
2025-03-27 19:38:14 +01:00
|
|
|
#[knuffel(skip)]
|
2025-05-25 08:41:33 +03:00
|
|
|
ToggleWindowUrgent(u64),
|
2025-03-27 19:38:14 +01:00
|
|
|
#[knuffel(skip)]
|
2025-05-25 08:41:33 +03:00
|
|
|
SetWindowUrgent(u64),
|
2025-03-27 19:38:14 +01:00
|
|
|
#[knuffel(skip)]
|
2025-05-25 08:41:33 +03:00
|
|
|
UnsetWindowUrgent(u64),
|
2023-10-03 11:38:42 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-10 09:33:32 +04:00
|
|
|
impl From<niri_ipc::Action> for Action {
|
|
|
|
|
fn from(value: niri_ipc::Action) -> Self {
|
|
|
|
|
match value {
|
2024-02-12 07:53:06 +04:00
|
|
|
niri_ipc::Action::Quit { skip_confirmation } => Self::Quit(skip_confirmation),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::PowerOffMonitors {} => Self::PowerOffMonitors,
|
2024-10-09 01:50:06 -07:00
|
|
|
niri_ipc::Action::PowerOnMonitors {} => Self::PowerOnMonitors,
|
2024-02-10 09:33:32 +04:00
|
|
|
niri_ipc::Action::Spawn { command } => Self::Spawn(command),
|
2024-05-07 22:06:43 +04:00
|
|
|
niri_ipc::Action::DoScreenTransition { delay_ms } => Self::DoScreenTransition(delay_ms),
|
2025-03-09 20:55:30 +00:00
|
|
|
niri_ipc::Action::Screenshot { show_pointer } => Self::Screenshot(show_pointer),
|
|
|
|
|
niri_ipc::Action::ScreenshotScreen {
|
|
|
|
|
write_to_disk,
|
|
|
|
|
show_pointer,
|
|
|
|
|
} => Self::ScreenshotScreen(write_to_disk, show_pointer),
|
2025-01-14 09:10:45 +01:00
|
|
|
niri_ipc::Action::ScreenshotWindow {
|
|
|
|
|
id: None,
|
|
|
|
|
write_to_disk,
|
|
|
|
|
} => Self::ScreenshotWindow(write_to_disk),
|
|
|
|
|
niri_ipc::Action::ScreenshotWindow {
|
|
|
|
|
id: Some(id),
|
|
|
|
|
write_to_disk,
|
|
|
|
|
} => Self::ScreenshotWindowById { id, write_to_disk },
|
2025-03-31 07:00:10 +02:00
|
|
|
niri_ipc::Action::ToggleKeyboardShortcutsInhibit {} => {
|
|
|
|
|
Self::ToggleKeyboardShortcutsInhibit
|
|
|
|
|
}
|
2024-09-06 15:10:01 +03:00
|
|
|
niri_ipc::Action::CloseWindow { id: None } => Self::CloseWindow,
|
|
|
|
|
niri_ipc::Action::CloseWindow { id: Some(id) } => Self::CloseWindowById(id),
|
|
|
|
|
niri_ipc::Action::FullscreenWindow { id: None } => Self::FullscreenWindow,
|
|
|
|
|
niri_ipc::Action::FullscreenWindow { id: Some(id) } => Self::FullscreenWindowById(id),
|
2025-03-17 14:56:29 +03:00
|
|
|
niri_ipc::Action::ToggleWindowedFullscreen { id: None } => {
|
|
|
|
|
Self::ToggleWindowedFullscreen
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::ToggleWindowedFullscreen { id: Some(id) } => {
|
|
|
|
|
Self::ToggleWindowedFullscreenById(id)
|
|
|
|
|
}
|
2024-09-06 15:10:01 +03:00
|
|
|
niri_ipc::Action::FocusWindow { id } => Self::FocusWindow(id),
|
2025-02-05 17:18:21 +03:00
|
|
|
niri_ipc::Action::FocusWindowInColumn { index } => Self::FocusWindowInColumn(index),
|
2024-11-21 12:48:51 +01:00
|
|
|
niri_ipc::Action::FocusWindowPrevious {} => Self::FocusWindowPrevious,
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::FocusColumnLeft {} => Self::FocusColumnLeft,
|
|
|
|
|
niri_ipc::Action::FocusColumnRight {} => Self::FocusColumnRight,
|
|
|
|
|
niri_ipc::Action::FocusColumnFirst {} => Self::FocusColumnFirst,
|
|
|
|
|
niri_ipc::Action::FocusColumnLast {} => Self::FocusColumnLast,
|
|
|
|
|
niri_ipc::Action::FocusColumnRightOrFirst {} => Self::FocusColumnRightOrFirst,
|
|
|
|
|
niri_ipc::Action::FocusColumnLeftOrLast {} => Self::FocusColumnLeftOrLast,
|
2025-03-13 03:05:55 +01:00
|
|
|
niri_ipc::Action::FocusColumn { index } => Self::FocusColumn(index),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::FocusWindowOrMonitorUp {} => Self::FocusWindowOrMonitorUp,
|
|
|
|
|
niri_ipc::Action::FocusWindowOrMonitorDown {} => Self::FocusWindowOrMonitorDown,
|
|
|
|
|
niri_ipc::Action::FocusColumnOrMonitorLeft {} => Self::FocusColumnOrMonitorLeft,
|
|
|
|
|
niri_ipc::Action::FocusColumnOrMonitorRight {} => Self::FocusColumnOrMonitorRight,
|
|
|
|
|
niri_ipc::Action::FocusWindowDown {} => Self::FocusWindowDown,
|
|
|
|
|
niri_ipc::Action::FocusWindowUp {} => Self::FocusWindowUp,
|
|
|
|
|
niri_ipc::Action::FocusWindowDownOrColumnLeft {} => Self::FocusWindowDownOrColumnLeft,
|
|
|
|
|
niri_ipc::Action::FocusWindowDownOrColumnRight {} => Self::FocusWindowDownOrColumnRight,
|
|
|
|
|
niri_ipc::Action::FocusWindowUpOrColumnLeft {} => Self::FocusWindowUpOrColumnLeft,
|
|
|
|
|
niri_ipc::Action::FocusWindowUpOrColumnRight {} => Self::FocusWindowUpOrColumnRight,
|
|
|
|
|
niri_ipc::Action::FocusWindowOrWorkspaceDown {} => Self::FocusWindowOrWorkspaceDown,
|
|
|
|
|
niri_ipc::Action::FocusWindowOrWorkspaceUp {} => Self::FocusWindowOrWorkspaceUp,
|
2025-02-05 17:18:21 +03:00
|
|
|
niri_ipc::Action::FocusWindowTop {} => Self::FocusWindowTop,
|
|
|
|
|
niri_ipc::Action::FocusWindowBottom {} => Self::FocusWindowBottom,
|
|
|
|
|
niri_ipc::Action::FocusWindowDownOrTop {} => Self::FocusWindowDownOrTop,
|
|
|
|
|
niri_ipc::Action::FocusWindowUpOrBottom {} => Self::FocusWindowUpOrBottom,
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveColumnLeft {} => Self::MoveColumnLeft,
|
|
|
|
|
niri_ipc::Action::MoveColumnRight {} => Self::MoveColumnRight,
|
|
|
|
|
niri_ipc::Action::MoveColumnToFirst {} => Self::MoveColumnToFirst,
|
|
|
|
|
niri_ipc::Action::MoveColumnToLast {} => Self::MoveColumnToLast,
|
2025-03-13 03:09:06 +01:00
|
|
|
niri_ipc::Action::MoveColumnToIndex { index } => Self::MoveColumnToIndex(index),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveColumnLeftOrToMonitorLeft {} => {
|
|
|
|
|
Self::MoveColumnLeftOrToMonitorLeft
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::MoveColumnRightOrToMonitorRight {} => {
|
2024-07-09 21:52:48 -07:00
|
|
|
Self::MoveColumnRightOrToMonitorRight
|
|
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveWindowDown {} => Self::MoveWindowDown,
|
|
|
|
|
niri_ipc::Action::MoveWindowUp {} => Self::MoveWindowUp,
|
|
|
|
|
niri_ipc::Action::MoveWindowDownOrToWorkspaceDown {} => {
|
2024-02-10 09:33:32 +04:00
|
|
|
Self::MoveWindowDownOrToWorkspaceDown
|
|
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveWindowUpOrToWorkspaceUp {} => Self::MoveWindowUpOrToWorkspaceUp,
|
2024-10-11 19:52:03 +03:00
|
|
|
niri_ipc::Action::ConsumeOrExpelWindowLeft { id: None } => {
|
|
|
|
|
Self::ConsumeOrExpelWindowLeft
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::ConsumeOrExpelWindowLeft { id: Some(id) } => {
|
|
|
|
|
Self::ConsumeOrExpelWindowLeftById(id)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::ConsumeOrExpelWindowRight { id: None } => {
|
|
|
|
|
Self::ConsumeOrExpelWindowRight
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::ConsumeOrExpelWindowRight { id: Some(id) } => {
|
|
|
|
|
Self::ConsumeOrExpelWindowRightById(id)
|
|
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::ConsumeWindowIntoColumn {} => Self::ConsumeWindowIntoColumn,
|
|
|
|
|
niri_ipc::Action::ExpelWindowFromColumn {} => Self::ExpelWindowFromColumn,
|
2025-01-09 08:29:36 +00:00
|
|
|
niri_ipc::Action::SwapWindowRight {} => Self::SwapWindowRight,
|
|
|
|
|
niri_ipc::Action::SwapWindowLeft {} => Self::SwapWindowLeft,
|
2025-02-01 10:46:52 +03:00
|
|
|
niri_ipc::Action::ToggleColumnTabbedDisplay {} => Self::ToggleColumnTabbedDisplay,
|
2025-02-06 08:42:09 +03:00
|
|
|
niri_ipc::Action::SetColumnDisplay { display } => Self::SetColumnDisplay(display),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::CenterColumn {} => Self::CenterColumn,
|
2024-12-29 22:44:19 +03:00
|
|
|
niri_ipc::Action::CenterWindow { id: None } => Self::CenterWindow,
|
|
|
|
|
niri_ipc::Action::CenterWindow { id: Some(id) } => Self::CenterWindowById(id),
|
2025-05-12 14:11:38 +03:00
|
|
|
niri_ipc::Action::CenterVisibleColumns {} => Self::CenterVisibleColumns,
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::FocusWorkspaceDown {} => Self::FocusWorkspaceDown,
|
|
|
|
|
niri_ipc::Action::FocusWorkspaceUp {} => Self::FocusWorkspaceUp,
|
2024-05-11 22:40:30 +02:00
|
|
|
niri_ipc::Action::FocusWorkspace { reference } => {
|
|
|
|
|
Self::FocusWorkspace(WorkspaceReference::from(reference))
|
|
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::FocusWorkspacePrevious {} => Self::FocusWorkspacePrevious,
|
|
|
|
|
niri_ipc::Action::MoveWindowToWorkspaceDown {} => Self::MoveWindowToWorkspaceDown,
|
|
|
|
|
niri_ipc::Action::MoveWindowToWorkspaceUp {} => Self::MoveWindowToWorkspaceUp,
|
2024-09-06 15:10:01 +03:00
|
|
|
niri_ipc::Action::MoveWindowToWorkspace {
|
|
|
|
|
window_id: None,
|
|
|
|
|
reference,
|
2025-03-29 02:40:08 -04:00
|
|
|
focus,
|
|
|
|
|
} => Self::MoveWindowToWorkspace(WorkspaceReference::from(reference), focus),
|
2024-09-06 15:10:01 +03:00
|
|
|
niri_ipc::Action::MoveWindowToWorkspace {
|
|
|
|
|
window_id: Some(window_id),
|
|
|
|
|
reference,
|
2025-03-29 02:40:08 -04:00
|
|
|
focus,
|
2024-09-06 15:10:01 +03:00
|
|
|
} => Self::MoveWindowToWorkspaceById {
|
|
|
|
|
window_id,
|
|
|
|
|
reference: WorkspaceReference::from(reference),
|
2025-03-29 02:40:08 -04:00
|
|
|
focus,
|
2024-09-06 15:10:01 +03:00
|
|
|
},
|
2025-05-01 20:36:33 +03:00
|
|
|
niri_ipc::Action::MoveColumnToWorkspaceDown { focus } => {
|
|
|
|
|
Self::MoveColumnToWorkspaceDown(focus)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::MoveColumnToWorkspaceUp { focus } => {
|
|
|
|
|
Self::MoveColumnToWorkspaceUp(focus)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::MoveColumnToWorkspace { reference, focus } => {
|
|
|
|
|
Self::MoveColumnToWorkspace(WorkspaceReference::from(reference), focus)
|
2024-05-11 22:40:30 +02:00
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveWorkspaceDown {} => Self::MoveWorkspaceDown,
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceUp {} => Self::MoveWorkspaceUp,
|
2025-01-10 06:03:19 +00:00
|
|
|
niri_ipc::Action::SetWorkspaceName {
|
|
|
|
|
name,
|
|
|
|
|
workspace: None,
|
|
|
|
|
} => Self::SetWorkspaceName(name),
|
|
|
|
|
niri_ipc::Action::SetWorkspaceName {
|
|
|
|
|
name,
|
|
|
|
|
workspace: Some(reference),
|
|
|
|
|
} => Self::SetWorkspaceNameByRef {
|
|
|
|
|
name,
|
|
|
|
|
reference: WorkspaceReference::from(reference),
|
|
|
|
|
},
|
|
|
|
|
niri_ipc::Action::UnsetWorkspaceName { reference: None } => Self::UnsetWorkspaceName,
|
|
|
|
|
niri_ipc::Action::UnsetWorkspaceName {
|
|
|
|
|
reference: Some(reference),
|
|
|
|
|
} => Self::UnsetWorkSpaceNameByRef(WorkspaceReference::from(reference)),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::FocusMonitorLeft {} => Self::FocusMonitorLeft,
|
|
|
|
|
niri_ipc::Action::FocusMonitorRight {} => Self::FocusMonitorRight,
|
|
|
|
|
niri_ipc::Action::FocusMonitorDown {} => Self::FocusMonitorDown,
|
|
|
|
|
niri_ipc::Action::FocusMonitorUp {} => Self::FocusMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
niri_ipc::Action::FocusMonitorPrevious {} => Self::FocusMonitorPrevious,
|
|
|
|
|
niri_ipc::Action::FocusMonitorNext {} => Self::FocusMonitorNext,
|
2025-02-20 20:25:43 +01:00
|
|
|
niri_ipc::Action::FocusMonitor { output } => Self::FocusMonitor(output),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveWindowToMonitorLeft {} => Self::MoveWindowToMonitorLeft,
|
|
|
|
|
niri_ipc::Action::MoveWindowToMonitorRight {} => Self::MoveWindowToMonitorRight,
|
|
|
|
|
niri_ipc::Action::MoveWindowToMonitorDown {} => Self::MoveWindowToMonitorDown,
|
|
|
|
|
niri_ipc::Action::MoveWindowToMonitorUp {} => Self::MoveWindowToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
niri_ipc::Action::MoveWindowToMonitorPrevious {} => Self::MoveWindowToMonitorPrevious,
|
|
|
|
|
niri_ipc::Action::MoveWindowToMonitorNext {} => Self::MoveWindowToMonitorNext,
|
2025-03-02 12:09:44 +01:00
|
|
|
niri_ipc::Action::MoveWindowToMonitor { id: None, output } => {
|
|
|
|
|
Self::MoveWindowToMonitor(output)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::MoveWindowToMonitor {
|
|
|
|
|
id: Some(id),
|
|
|
|
|
output,
|
|
|
|
|
} => Self::MoveWindowToMonitorById { id, output },
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MoveColumnToMonitorLeft {} => Self::MoveColumnToMonitorLeft,
|
|
|
|
|
niri_ipc::Action::MoveColumnToMonitorRight {} => Self::MoveColumnToMonitorRight,
|
|
|
|
|
niri_ipc::Action::MoveColumnToMonitorDown {} => Self::MoveColumnToMonitorDown,
|
|
|
|
|
niri_ipc::Action::MoveColumnToMonitorUp {} => Self::MoveColumnToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
niri_ipc::Action::MoveColumnToMonitorPrevious {} => Self::MoveColumnToMonitorPrevious,
|
|
|
|
|
niri_ipc::Action::MoveColumnToMonitorNext {} => Self::MoveColumnToMonitorNext,
|
2025-02-20 23:09:59 +01:00
|
|
|
niri_ipc::Action::MoveColumnToMonitor { output } => Self::MoveColumnToMonitor(output),
|
2024-12-28 10:12:50 +03:00
|
|
|
niri_ipc::Action::SetWindowWidth { id: None, change } => Self::SetWindowWidth(change),
|
|
|
|
|
niri_ipc::Action::SetWindowWidth {
|
|
|
|
|
id: Some(id),
|
|
|
|
|
change,
|
|
|
|
|
} => Self::SetWindowWidthById { id, change },
|
2024-09-06 15:10:01 +03:00
|
|
|
niri_ipc::Action::SetWindowHeight { id: None, change } => Self::SetWindowHeight(change),
|
|
|
|
|
niri_ipc::Action::SetWindowHeight {
|
|
|
|
|
id: Some(id),
|
|
|
|
|
change,
|
|
|
|
|
} => Self::SetWindowHeightById { id, change },
|
|
|
|
|
niri_ipc::Action::ResetWindowHeight { id: None } => Self::ResetWindowHeight,
|
|
|
|
|
niri_ipc::Action::ResetWindowHeight { id: Some(id) } => Self::ResetWindowHeightById(id),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::SwitchPresetColumnWidth {} => Self::SwitchPresetColumnWidth,
|
2024-12-30 09:05:35 +03:00
|
|
|
niri_ipc::Action::SwitchPresetWindowWidth { id: None } => Self::SwitchPresetWindowWidth,
|
|
|
|
|
niri_ipc::Action::SwitchPresetWindowWidth { id: Some(id) } => {
|
|
|
|
|
Self::SwitchPresetWindowWidthById(id)
|
|
|
|
|
}
|
2024-09-12 11:53:10 +03:00
|
|
|
niri_ipc::Action::SwitchPresetWindowHeight { id: None } => {
|
|
|
|
|
Self::SwitchPresetWindowHeight
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::SwitchPresetWindowHeight { id: Some(id) } => {
|
|
|
|
|
Self::SwitchPresetWindowHeightById(id)
|
|
|
|
|
}
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::MaximizeColumn {} => Self::MaximizeColumn,
|
2024-02-10 09:33:32 +04:00
|
|
|
niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change),
|
2025-02-17 21:22:10 +03:00
|
|
|
niri_ipc::Action::ExpandColumnToAvailableWidth {} => Self::ExpandColumnToAvailableWidth,
|
2024-02-10 09:33:32 +04:00
|
|
|
niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout),
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::ShowHotkeyOverlay {} => Self::ShowHotkeyOverlay,
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorLeft {} => Self::MoveWorkspaceToMonitorLeft,
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorRight {} => Self::MoveWorkspaceToMonitorRight,
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorDown {} => Self::MoveWorkspaceToMonitorDown,
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorUp {} => Self::MoveWorkspaceToMonitorUp,
|
2024-12-15 16:04:42 +01:00
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorPrevious {} => {
|
|
|
|
|
Self::MoveWorkspaceToMonitorPrevious
|
|
|
|
|
}
|
2025-01-25 10:49:51 +02:00
|
|
|
niri_ipc::Action::MoveWorkspaceToIndex {
|
|
|
|
|
index,
|
|
|
|
|
reference: Some(reference),
|
|
|
|
|
} => Self::MoveWorkspaceToIndexByRef {
|
|
|
|
|
new_idx: index,
|
|
|
|
|
reference: WorkspaceReference::from(reference),
|
|
|
|
|
},
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToIndex {
|
|
|
|
|
index,
|
|
|
|
|
reference: None,
|
|
|
|
|
} => Self::MoveWorkspaceToIndex(index),
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitor {
|
|
|
|
|
output,
|
|
|
|
|
reference: Some(reference),
|
|
|
|
|
} => Self::MoveWorkspaceToMonitorByRef {
|
|
|
|
|
output_name: output,
|
|
|
|
|
reference: WorkspaceReference::from(reference),
|
|
|
|
|
},
|
|
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitor {
|
|
|
|
|
output,
|
|
|
|
|
reference: None,
|
|
|
|
|
} => Self::MoveWorkspaceToMonitor(output),
|
2024-12-15 16:04:42 +01:00
|
|
|
niri_ipc::Action::MoveWorkspaceToMonitorNext {} => Self::MoveWorkspaceToMonitorNext,
|
2024-09-06 18:30:39 +03:00
|
|
|
niri_ipc::Action::ToggleDebugTint {} => Self::ToggleDebugTint,
|
|
|
|
|
niri_ipc::Action::DebugToggleOpaqueRegions {} => Self::DebugToggleOpaqueRegions,
|
|
|
|
|
niri_ipc::Action::DebugToggleDamage {} => Self::DebugToggleDamage,
|
2024-11-29 21:11:02 +03:00
|
|
|
niri_ipc::Action::ToggleWindowFloating { id: None } => Self::ToggleWindowFloating,
|
|
|
|
|
niri_ipc::Action::ToggleWindowFloating { id: Some(id) } => {
|
|
|
|
|
Self::ToggleWindowFloatingById(id)
|
|
|
|
|
}
|
2024-12-28 10:13:30 +03:00
|
|
|
niri_ipc::Action::MoveWindowToFloating { id: None } => Self::MoveWindowToFloating,
|
|
|
|
|
niri_ipc::Action::MoveWindowToFloating { id: Some(id) } => {
|
|
|
|
|
Self::MoveWindowToFloatingById(id)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::MoveWindowToTiling { id: None } => Self::MoveWindowToTiling,
|
|
|
|
|
niri_ipc::Action::MoveWindowToTiling { id: Some(id) } => {
|
|
|
|
|
Self::MoveWindowToTilingById(id)
|
|
|
|
|
}
|
2024-12-28 10:14:02 +03:00
|
|
|
niri_ipc::Action::FocusFloating {} => Self::FocusFloating,
|
|
|
|
|
niri_ipc::Action::FocusTiling {} => Self::FocusTiling,
|
2024-11-29 21:11:02 +03:00
|
|
|
niri_ipc::Action::SwitchFocusBetweenFloatingAndTiling {} => {
|
|
|
|
|
Self::SwitchFocusBetweenFloatingAndTiling
|
|
|
|
|
}
|
2024-12-28 11:40:16 +03:00
|
|
|
niri_ipc::Action::MoveFloatingWindow { id, x, y } => {
|
|
|
|
|
Self::MoveFloatingWindowById { id, x, y }
|
|
|
|
|
}
|
2025-01-23 10:40:52 +03:00
|
|
|
niri_ipc::Action::ToggleWindowRuleOpacity { id: None } => Self::ToggleWindowRuleOpacity,
|
|
|
|
|
niri_ipc::Action::ToggleWindowRuleOpacity { id: Some(id) } => {
|
|
|
|
|
Self::ToggleWindowRuleOpacityById(id)
|
|
|
|
|
}
|
2025-03-15 11:23:01 +03:00
|
|
|
niri_ipc::Action::SetDynamicCastWindow { id: None } => Self::SetDynamicCastWindow,
|
|
|
|
|
niri_ipc::Action::SetDynamicCastWindow { id: Some(id) } => {
|
|
|
|
|
Self::SetDynamicCastWindowById(id)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::SetDynamicCastMonitor { output } => {
|
|
|
|
|
Self::SetDynamicCastMonitor(output)
|
|
|
|
|
}
|
|
|
|
|
niri_ipc::Action::ClearDynamicCastTarget {} => Self::ClearDynamicCastTarget,
|
2025-04-25 09:36:50 +03:00
|
|
|
niri_ipc::Action::ToggleOverview {} => Self::ToggleOverview,
|
|
|
|
|
niri_ipc::Action::OpenOverview {} => Self::OpenOverview,
|
|
|
|
|
niri_ipc::Action::CloseOverview {} => Self::CloseOverview,
|
2025-05-25 08:41:33 +03:00
|
|
|
niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id),
|
|
|
|
|
niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id),
|
|
|
|
|
niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id),
|
2024-02-10 09:33:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
2023-11-02 00:09:31 +04:00
|
|
|
}
|
|
|
|
|
|
2024-05-11 22:40:30 +02:00
|
|
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
|
|
|
pub enum WorkspaceReference {
|
2024-09-02 09:20:23 +03:00
|
|
|
Id(u64),
|
2024-05-11 22:40:30 +02:00
|
|
|
Index(u8),
|
|
|
|
|
Name(String),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<WorkspaceReferenceArg> for WorkspaceReference {
|
|
|
|
|
fn from(reference: WorkspaceReferenceArg) -> WorkspaceReference {
|
|
|
|
|
match reference {
|
2024-09-02 09:20:23 +03:00
|
|
|
WorkspaceReferenceArg::Id(id) => Self::Id(id),
|
2024-05-11 22:40:30 +02:00
|
|
|
WorkspaceReferenceArg::Index(i) => Self::Index(i),
|
|
|
|
|
WorkspaceReferenceArg::Name(n) => Self::Name(n),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceReference {
|
|
|
|
|
fn type_check(
|
|
|
|
|
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(type_name) = &type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn raw_decode(
|
|
|
|
|
val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<WorkspaceReference, DecodeError<S>> {
|
|
|
|
|
match &**val {
|
|
|
|
|
knuffel::ast::Literal::String(ref s) => Ok(WorkspaceReference::Name(s.clone().into())),
|
|
|
|
|
knuffel::ast::Literal::Int(ref value) => match value.try_into() {
|
|
|
|
|
Ok(v) => Ok(WorkspaceReference::Index(v)),
|
|
|
|
|
Err(e) => {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(val, e));
|
|
|
|
|
Ok(WorkspaceReference::Index(0))
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
ctx.emit_error(DecodeError::unsupported(
|
|
|
|
|
val,
|
|
|
|
|
"Unsupported value, only numbers and strings are recognized",
|
|
|
|
|
));
|
|
|
|
|
Ok(WorkspaceReference::Index(0))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-17 09:16:28 +03:00
|
|
|
impl<S: knuffel::traits::ErrorSpan, const MIN: i32, const MAX: i32> knuffel::DecodeScalar<S>
|
|
|
|
|
for FloatOrInt<MIN, MAX>
|
|
|
|
|
{
|
|
|
|
|
fn type_check(
|
|
|
|
|
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(type_name) = &type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn raw_decode(
|
|
|
|
|
val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
match &**val {
|
|
|
|
|
knuffel::ast::Literal::Int(ref value) => match value.try_into() {
|
|
|
|
|
Ok(v) => {
|
|
|
|
|
if (MIN..=MAX).contains(&v) {
|
|
|
|
|
Ok(FloatOrInt(f64::from(v)))
|
|
|
|
|
} else {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(
|
|
|
|
|
val,
|
|
|
|
|
format!("value must be between {MIN} and {MAX}"),
|
|
|
|
|
));
|
|
|
|
|
Ok(FloatOrInt::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(val, e));
|
|
|
|
|
Ok(FloatOrInt::default())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
knuffel::ast::Literal::Decimal(ref value) => match value.try_into() {
|
|
|
|
|
Ok(v) => {
|
|
|
|
|
if (f64::from(MIN)..=f64::from(MAX)).contains(&v) {
|
|
|
|
|
Ok(FloatOrInt(v))
|
|
|
|
|
} else {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(
|
|
|
|
|
val,
|
|
|
|
|
format!("value must be between {MIN} and {MAX}"),
|
|
|
|
|
));
|
|
|
|
|
Ok(FloatOrInt::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(val, e));
|
|
|
|
|
Ok(FloatOrInt::default())
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
ctx.emit_error(DecodeError::unsupported(
|
|
|
|
|
val,
|
|
|
|
|
"Unsupported value, only numbers are recognized",
|
|
|
|
|
));
|
|
|
|
|
Ok(FloatOrInt::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 17:05:15 +04:00
|
|
|
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
2023-09-06 15:49:46 +04:00
|
|
|
pub struct DebugConfig {
|
2024-03-24 09:03:59 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub preview_render: Option<PreviewRender>,
|
2023-09-08 17:54:02 +04:00
|
|
|
#[knuffel(child)]
|
2023-09-20 09:28:23 +04:00
|
|
|
pub dbus_interfaces_in_non_session_instances: bool,
|
2023-09-14 09:33:42 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub wait_for_frame_completion_before_queueing: bool,
|
2023-09-14 22:33:49 +04:00
|
|
|
#[knuffel(child)]
|
2023-09-14 22:43:12 +04:00
|
|
|
pub enable_overlay_planes: bool,
|
2024-01-03 08:42:04 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_cursor_plane: bool,
|
2024-04-25 22:10:52 +04:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_direct_scanout: bool,
|
2025-01-04 13:02:05 +03:00
|
|
|
#[knuffel(child)]
|
2025-08-07 14:06:11 -04:00
|
|
|
pub keep_max_bpc_unchanged: bool,
|
|
|
|
|
#[knuffel(child)]
|
2025-01-04 13:02:05 +03:00
|
|
|
pub restrict_primary_scanout_to_matching_format: bool,
|
2024-01-06 09:14:48 +04:00
|
|
|
#[knuffel(child, unwrap(argument))]
|
|
|
|
|
pub render_drm_device: Option<PathBuf>,
|
2024-03-01 07:49:10 +04:00
|
|
|
#[knuffel(child)]
|
2024-12-17 17:04:23 +03:00
|
|
|
pub force_pipewire_invalid_modifier: bool,
|
|
|
|
|
#[knuffel(child)]
|
2024-03-01 07:49:10 +04:00
|
|
|
pub emulate_zero_presentation_time: bool,
|
2024-08-22 14:36:47 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_resize_throttling: bool,
|
2024-08-22 14:44:11 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_transactions: bool,
|
2024-11-05 09:40:54 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub keep_laptop_panel_on_when_lid_is_closed: bool,
|
2024-11-06 08:42:22 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub disable_monitor_names: bool,
|
2024-11-29 11:37:33 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub strict_new_window_focus_policy: bool,
|
2025-02-26 19:24:37 +03:00
|
|
|
#[knuffel(child)]
|
|
|
|
|
pub honor_xdg_activation_with_invalid_serial: bool,
|
2025-06-04 01:56:21 +10:00
|
|
|
#[knuffel(child)]
|
2025-06-11 09:05:14 +03:00
|
|
|
pub deactivate_unfocused_windows: bool,
|
|
|
|
|
#[knuffel(child)]
|
2025-06-04 01:56:21 +10:00
|
|
|
pub skip_cursor_only_updates_during_vrr: bool,
|
2023-09-06 15:49:46 +04:00
|
|
|
}
|
|
|
|
|
|
2024-03-24 09:03:59 +04:00
|
|
|
#[derive(knuffel::DecodeScalar, Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum PreviewRender {
|
|
|
|
|
Screencast,
|
|
|
|
|
ScreenCapture,
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-05 15:27:28 +02:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum ConfigPath {
|
|
|
|
|
/// Explicitly set config path.
|
|
|
|
|
///
|
|
|
|
|
/// Load the config only from this path, never create it.
|
|
|
|
|
Explicit(PathBuf),
|
|
|
|
|
|
|
|
|
|
/// Default config path.
|
|
|
|
|
///
|
|
|
|
|
/// Prioritize the user path, fallback to the system path, fallback to creating the user path
|
|
|
|
|
/// at compositor startup.
|
|
|
|
|
Regular {
|
|
|
|
|
/// User config path, usually `$XDG_CONFIG_HOME/niri/config.kdl`.
|
|
|
|
|
user_path: PathBuf,
|
|
|
|
|
/// System config path, usually `/etc/niri/config.kdl`.
|
|
|
|
|
system_path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ConfigPath {
|
|
|
|
|
/// Load the config, or return an error if it doesn't exist.
|
|
|
|
|
pub fn load(&self) -> miette::Result<Config> {
|
|
|
|
|
let _span = tracy_client::span!("ConfigPath::load");
|
|
|
|
|
|
|
|
|
|
self.load_inner(|user_path, system_path| {
|
|
|
|
|
Err(miette::miette!(
|
|
|
|
|
"no config file found; create one at {user_path:?} or {system_path:?}",
|
|
|
|
|
))
|
|
|
|
|
})
|
|
|
|
|
.context("error loading config")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Load the config, or create it if it doesn't exist.
|
|
|
|
|
///
|
|
|
|
|
/// Returns a tuple containing the path that was created, if any, and the loaded config.
|
|
|
|
|
///
|
|
|
|
|
/// If the config was created, but for some reason could not be read afterwards,
|
|
|
|
|
/// this may return `(Some(_), Err(_))`.
|
|
|
|
|
pub fn load_or_create(&self) -> (Option<&Path>, miette::Result<Config>) {
|
|
|
|
|
let _span = tracy_client::span!("ConfigPath::load_or_create");
|
|
|
|
|
|
|
|
|
|
let mut created_at = None;
|
|
|
|
|
|
|
|
|
|
let result = self
|
|
|
|
|
.load_inner(|user_path, _| {
|
|
|
|
|
Self::create(user_path, &mut created_at)
|
|
|
|
|
.map(|()| user_path)
|
|
|
|
|
.with_context(|| format!("error creating config at {user_path:?}"))
|
|
|
|
|
})
|
|
|
|
|
.context("error loading config");
|
|
|
|
|
|
|
|
|
|
(created_at, result)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn load_inner<'a>(
|
|
|
|
|
&'a self,
|
|
|
|
|
maybe_create: impl FnOnce(&'a Path, &'a Path) -> miette::Result<&'a Path>,
|
|
|
|
|
) -> miette::Result<Config> {
|
|
|
|
|
let path = match self {
|
|
|
|
|
ConfigPath::Explicit(path) => path.as_path(),
|
|
|
|
|
ConfigPath::Regular {
|
|
|
|
|
user_path,
|
|
|
|
|
system_path,
|
|
|
|
|
} => {
|
|
|
|
|
if user_path.exists() {
|
|
|
|
|
user_path.as_path()
|
|
|
|
|
} else if system_path.exists() {
|
|
|
|
|
system_path.as_path()
|
|
|
|
|
} else {
|
|
|
|
|
maybe_create(user_path.as_path(), system_path.as_path())?
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
Config::load(path)
|
2024-01-07 09:07:22 +04:00
|
|
|
}
|
|
|
|
|
|
2025-08-05 15:27:28 +02:00
|
|
|
fn create<'a>(path: &'a Path, created_at: &mut Option<&'a Path>) -> miette::Result<()> {
|
|
|
|
|
if let Some(default_parent) = path.parent() {
|
|
|
|
|
fs::create_dir_all(default_parent)
|
|
|
|
|
.into_diagnostic()
|
|
|
|
|
.with_context(|| format!("error creating config directory {default_parent:?}"))?;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create the config and fill it with the default config if it doesn't exist.
|
|
|
|
|
let mut new_file = match File::options()
|
|
|
|
|
.read(true)
|
|
|
|
|
.write(true)
|
|
|
|
|
.create_new(true)
|
|
|
|
|
.open(path)
|
|
|
|
|
{
|
|
|
|
|
Err(err) if err.kind() == std::io::ErrorKind::AlreadyExists => return Ok(()),
|
|
|
|
|
res => res,
|
|
|
|
|
}
|
|
|
|
|
.into_diagnostic()
|
|
|
|
|
.with_context(|| format!("error opening config file at {path:?}"))?;
|
|
|
|
|
|
|
|
|
|
*created_at = Some(path);
|
|
|
|
|
|
|
|
|
|
let default = include_bytes!("../../resources/default-config.kdl");
|
|
|
|
|
|
|
|
|
|
new_file
|
|
|
|
|
.write_all(default)
|
|
|
|
|
.into_diagnostic()
|
|
|
|
|
.with_context(|| format!("error writing default config to {path:?}"))?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Config {
|
|
|
|
|
pub fn load(path: &Path) -> miette::Result<Self> {
|
|
|
|
|
let contents = fs::read_to_string(path)
|
2023-09-05 12:58:51 +04:00
|
|
|
.into_diagnostic()
|
|
|
|
|
.with_context(|| format!("error reading {path:?}"))?;
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
let config = Self::parse(
|
|
|
|
|
path.file_name()
|
|
|
|
|
.and_then(OsStr::to_str)
|
|
|
|
|
.unwrap_or("config.kdl"),
|
|
|
|
|
&contents,
|
|
|
|
|
)
|
|
|
|
|
.context("error parsing")?;
|
2023-09-05 12:58:51 +04:00
|
|
|
debug!("loaded config from {path:?}");
|
2024-01-18 11:02:15 +04:00
|
|
|
Ok(config)
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn parse(filename: &str, text: &str) -> Result<Self, knuffel::Error> {
|
2024-01-16 12:53:01 +04:00
|
|
|
let _span = tracy_client::span!("Config::parse");
|
2023-09-05 12:58:51 +04:00
|
|
|
knuffel::parse(filename, text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Config {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Config::parse(
|
|
|
|
|
"default-config.kdl",
|
2024-01-07 09:07:22 +04:00
|
|
|
include_str!("../../resources/default-config.kdl"),
|
2023-09-05 12:58:51 +04:00
|
|
|
)
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-24 21:49:07 +04:00
|
|
|
impl BorderRule {
|
|
|
|
|
pub fn merge_with(&mut self, other: &Self) {
|
2024-12-27 15:40:48 +03:00
|
|
|
if other.off {
|
|
|
|
|
self.off = true;
|
|
|
|
|
self.on = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if other.on {
|
|
|
|
|
self.off = false;
|
|
|
|
|
self.on = true;
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
|
|
|
|
|
if let Some(x) = other.width {
|
|
|
|
|
self.width = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.active_color {
|
|
|
|
|
self.active_color = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.inactive_color {
|
|
|
|
|
self.inactive_color = Some(x);
|
|
|
|
|
}
|
2025-03-22 19:04:24 +01:00
|
|
|
if let Some(x) = other.urgent_color {
|
|
|
|
|
self.urgent_color = Some(x);
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
if let Some(x) = other.active_gradient {
|
|
|
|
|
self.active_gradient = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.inactive_gradient {
|
|
|
|
|
self.inactive_gradient = Some(x);
|
|
|
|
|
}
|
2025-03-22 19:04:24 +01:00
|
|
|
if let Some(x) = other.urgent_gradient {
|
|
|
|
|
self.urgent_gradient = Some(x);
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn resolve_against(&self, mut config: Border) -> Border {
|
|
|
|
|
config.off |= self.off;
|
|
|
|
|
if self.on {
|
|
|
|
|
config.off = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(x) = self.width {
|
|
|
|
|
config.width = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.active_color {
|
|
|
|
|
config.active_color = x;
|
|
|
|
|
config.active_gradient = None;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.inactive_color {
|
|
|
|
|
config.inactive_color = x;
|
|
|
|
|
config.inactive_gradient = None;
|
|
|
|
|
}
|
2025-03-22 19:04:24 +01:00
|
|
|
if let Some(x) = self.urgent_color {
|
|
|
|
|
config.urgent_color = x;
|
|
|
|
|
config.urgent_gradient = None;
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
if let Some(x) = self.active_gradient {
|
|
|
|
|
config.active_gradient = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.inactive_gradient {
|
|
|
|
|
config.inactive_gradient = Some(x);
|
|
|
|
|
}
|
2025-03-22 19:04:24 +01:00
|
|
|
if let Some(x) = self.urgent_gradient {
|
|
|
|
|
config.urgent_gradient = Some(x);
|
|
|
|
|
}
|
2024-04-24 21:49:07 +04:00
|
|
|
|
|
|
|
|
config
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-15 14:16:05 +03:00
|
|
|
impl ShadowRule {
|
|
|
|
|
pub fn merge_with(&mut self, other: &Self) {
|
|
|
|
|
if other.off {
|
|
|
|
|
self.off = true;
|
|
|
|
|
self.on = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if other.on {
|
|
|
|
|
self.off = false;
|
|
|
|
|
self.on = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(x) = other.offset {
|
|
|
|
|
self.offset = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.softness {
|
|
|
|
|
self.softness = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.spread {
|
|
|
|
|
self.spread = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.draw_behind_window {
|
|
|
|
|
self.draw_behind_window = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.color {
|
|
|
|
|
self.color = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.inactive_color {
|
|
|
|
|
self.inactive_color = Some(x);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn resolve_against(&self, mut config: Shadow) -> Shadow {
|
|
|
|
|
config.on |= self.on;
|
|
|
|
|
if self.off {
|
|
|
|
|
config.on = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(x) = self.offset {
|
|
|
|
|
config.offset = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.softness {
|
|
|
|
|
config.softness = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.spread {
|
|
|
|
|
config.spread = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.draw_behind_window {
|
|
|
|
|
config.draw_behind_window = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.color {
|
|
|
|
|
config.color = x;
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = self.inactive_color {
|
|
|
|
|
config.inactive_color = Some(x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 08:41:42 +03:00
|
|
|
impl TabIndicatorRule {
|
|
|
|
|
pub fn merge_with(&mut self, other: &Self) {
|
|
|
|
|
if let Some(x) = other.active_color {
|
|
|
|
|
self.active_color = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.inactive_color {
|
|
|
|
|
self.inactive_color = Some(x);
|
|
|
|
|
}
|
2025-05-10 22:34:53 +03:00
|
|
|
if let Some(x) = other.urgent_color {
|
|
|
|
|
self.urgent_color = Some(x);
|
|
|
|
|
}
|
2025-02-02 08:41:42 +03:00
|
|
|
if let Some(x) = other.active_gradient {
|
|
|
|
|
self.active_gradient = Some(x);
|
|
|
|
|
}
|
|
|
|
|
if let Some(x) = other.inactive_gradient {
|
|
|
|
|
self.inactive_gradient = Some(x);
|
|
|
|
|
}
|
2025-05-10 22:34:53 +03:00
|
|
|
if let Some(x) = other.urgent_gradient {
|
|
|
|
|
self.urgent_gradient = Some(x);
|
|
|
|
|
}
|
2025-02-02 08:41:42 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:06:08 +04:00
|
|
|
impl CornerRadius {
|
|
|
|
|
pub fn fit_to(self, width: f32, height: f32) -> Self {
|
|
|
|
|
// Like in CSS: https://drafts.csswg.org/css-backgrounds/#corner-overlap
|
|
|
|
|
let reduction = f32::min(
|
|
|
|
|
f32::min(
|
|
|
|
|
width / (self.top_left + self.top_right),
|
|
|
|
|
width / (self.bottom_left + self.bottom_right),
|
|
|
|
|
),
|
|
|
|
|
f32::min(
|
|
|
|
|
height / (self.top_left + self.bottom_left),
|
|
|
|
|
height / (self.top_right + self.bottom_right),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
let reduction = f32::min(1., reduction);
|
|
|
|
|
|
|
|
|
|
Self {
|
|
|
|
|
top_left: self.top_left * reduction,
|
|
|
|
|
top_right: self.top_right * reduction,
|
|
|
|
|
bottom_right: self.bottom_right * reduction,
|
|
|
|
|
bottom_left: self.bottom_left * reduction,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-05 07:43:21 +04:00
|
|
|
pub fn expanded_by(mut self, width: f32) -> Self {
|
2025-03-09 18:55:17 +01:00
|
|
|
// Radius = 0 is preserved, so that square corners remain square.
|
2024-05-05 07:43:21 +04:00
|
|
|
if self.top_left > 0. {
|
|
|
|
|
self.top_left += width;
|
2024-05-01 19:06:08 +04:00
|
|
|
}
|
2024-05-05 07:43:21 +04:00
|
|
|
if self.top_right > 0. {
|
|
|
|
|
self.top_right += width;
|
|
|
|
|
}
|
|
|
|
|
if self.bottom_right > 0. {
|
|
|
|
|
self.bottom_right += width;
|
2024-05-01 19:06:08 +04:00
|
|
|
}
|
2024-05-05 07:43:21 +04:00
|
|
|
if self.bottom_left > 0. {
|
|
|
|
|
self.bottom_left += width;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-09 18:55:17 +01:00
|
|
|
if width < 0. {
|
|
|
|
|
self.top_left = self.top_left.max(0.);
|
|
|
|
|
self.top_right = self.top_right.max(0.);
|
|
|
|
|
self.bottom_left = self.bottom_left.max(0.);
|
|
|
|
|
self.bottom_right = self.bottom_right.max(0.);
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-05 07:43:21 +04:00
|
|
|
self
|
2024-05-01 19:06:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn scaled_by(self, scale: f32) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
top_left: self.top_left * scale,
|
|
|
|
|
top_right: self.top_right * scale,
|
|
|
|
|
bottom_right: self.bottom_right * scale,
|
|
|
|
|
bottom_left: self.bottom_left * scale,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
impl FromStr for GradientInterpolation {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let mut iter = s.split_whitespace();
|
|
|
|
|
let in_part1 = iter.next();
|
|
|
|
|
let in_part2 = iter.next();
|
|
|
|
|
let in_part3 = iter.next();
|
|
|
|
|
|
|
|
|
|
let Some(in_part1) = in_part1 else {
|
|
|
|
|
return Err(miette!("missing color space"));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let color = match in_part1 {
|
|
|
|
|
"srgb" => GradientColorSpace::Srgb,
|
|
|
|
|
"srgb-linear" => GradientColorSpace::SrgbLinear,
|
|
|
|
|
"oklab" => GradientColorSpace::Oklab,
|
|
|
|
|
"oklch" => GradientColorSpace::Oklch,
|
|
|
|
|
x => {
|
|
|
|
|
return Err(miette!(
|
|
|
|
|
"invalid color space {x}; can be srgb, srgb-linear, oklab or oklch"
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let interpolation = if let Some(in_part2) = in_part2 {
|
|
|
|
|
if color != GradientColorSpace::Oklch {
|
|
|
|
|
return Err(miette!("only oklch color space can have hue interpolation"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if in_part3 != Some("hue") {
|
|
|
|
|
return Err(miette!(
|
|
|
|
|
"interpolation must end with \"hue\", like \"oklch shorter hue\""
|
|
|
|
|
));
|
|
|
|
|
} else if iter.next().is_some() {
|
|
|
|
|
return Err(miette!("unexpected text after hue interpolation"));
|
|
|
|
|
} else {
|
|
|
|
|
match in_part2 {
|
|
|
|
|
"shorter" => HueInterpolation::Shorter,
|
|
|
|
|
"longer" => HueInterpolation::Longer,
|
|
|
|
|
"increasing" => HueInterpolation::Increasing,
|
|
|
|
|
"decreasing" => HueInterpolation::Decreasing,
|
|
|
|
|
x => {
|
|
|
|
|
return Err(miette!(
|
|
|
|
|
"invalid hue interpolation {x}; \
|
|
|
|
|
can be shorter, longer, increasing, decreasing"
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
HueInterpolation::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
color_space: color,
|
|
|
|
|
hue_interpolation: interpolation,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-21 21:27:44 +04:00
|
|
|
impl FromStr for Color {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
2025-06-11 00:34:33 -06:00
|
|
|
let color = csscolorparser::parse(s)
|
|
|
|
|
.into_diagnostic()?
|
|
|
|
|
.clamp()
|
|
|
|
|
.to_array();
|
2024-08-08 15:12:48 +03:00
|
|
|
Ok(Self::from_array_unpremul(color))
|
2024-02-21 21:27:44 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-26 09:02:47 +04:00
|
|
|
#[derive(knuffel::Decode)]
|
|
|
|
|
struct ColorRgba {
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
r: u8,
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
g: u8,
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
b: u8,
|
|
|
|
|
#[knuffel(argument)]
|
|
|
|
|
a: u8,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<ColorRgba> for Color {
|
|
|
|
|
fn from(value: ColorRgba) -> Self {
|
|
|
|
|
let ColorRgba { r, g, b, a } = value;
|
2024-07-16 10:22:03 +03:00
|
|
|
Self::from_array_unpremul([r, g, b, a].map(|x| x as f32 / 255.))
|
2024-02-26 09:02:47 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Manual impl to allow both one-argument string and 4-argument RGBA forms.
|
|
|
|
|
impl<S> knuffel::Decode<S> for Color
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
2024-03-01 12:50:49 +01:00
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-02-26 09:02:47 +04:00
|
|
|
// Check for unexpected type name.
|
|
|
|
|
if let Some(type_name) = &node.type_name {
|
2024-03-01 12:50:49 +01:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
2024-02-26 09:02:47 +04:00
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the first argument.
|
|
|
|
|
let mut iter_args = node.arguments.iter();
|
2024-03-01 12:50:49 +01:00
|
|
|
let val = iter_args
|
|
|
|
|
.next()
|
|
|
|
|
.ok_or_else(|| DecodeError::missing(node, "additional argument is required"))?;
|
2024-02-26 09:02:47 +04:00
|
|
|
|
|
|
|
|
// Check for unexpected type name.
|
|
|
|
|
if let Some(typ) = &val.type_name {
|
2024-03-01 12:50:49 +01:00
|
|
|
ctx.emit_error(DecodeError::TypeName {
|
2024-02-26 09:02:47 +04:00
|
|
|
span: typ.span().clone(),
|
|
|
|
|
found: Some((**typ).clone()),
|
|
|
|
|
expected: knuffel::errors::ExpectedType::no_type(),
|
|
|
|
|
rust_type: "str",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check the argument type.
|
|
|
|
|
let rv = match *val.literal {
|
|
|
|
|
// If it's a string, use FromStr.
|
2024-03-01 12:50:49 +01:00
|
|
|
knuffel::ast::Literal::String(ref s) => {
|
|
|
|
|
Color::from_str(s).map_err(|e| DecodeError::conversion(&val.literal, e))
|
|
|
|
|
}
|
2024-02-26 09:02:47 +04:00
|
|
|
// Otherwise, fall back to the 4-argument RGBA form.
|
|
|
|
|
_ => return ColorRgba::decode_node(node, ctx).map(Color::from),
|
|
|
|
|
}?;
|
|
|
|
|
|
|
|
|
|
// Check for unexpected following arguments.
|
|
|
|
|
if let Some(val) = iter_args.next() {
|
2024-03-01 12:50:49 +01:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
2024-02-26 09:02:47 +04:00
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"unexpected argument",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for unexpected properties and children.
|
|
|
|
|
for name in node.properties.keys() {
|
2024-03-01 12:50:49 +01:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
2024-02-26 09:02:47 +04:00
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
format!("unexpected property `{}`", name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
for child in node.children.as_ref().map(|lst| &lst[..]).unwrap_or(&[]) {
|
2024-03-01 12:50:49 +01:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
2024-02-26 09:02:47 +04:00
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
format!("unexpected node `{}`", child.node_name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(rv)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
fn expect_only_children<S>(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
if let Some(type_name) = &node.type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for val in node.arguments.iter() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"no arguments expected for this node",
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for name in node.properties.keys() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
"no properties expected for this node",
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-04 17:51:11 +04:00
|
|
|
impl FromIterator<Output> for Outputs {
|
|
|
|
|
fn from_iter<T: IntoIterator<Item = Output>>(iter: T) -> Self {
|
|
|
|
|
Self(Vec::from_iter(iter))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Outputs {
|
2024-09-03 12:13:04 +03:00
|
|
|
pub fn find(&self, name: &OutputName) -> Option<&Output> {
|
|
|
|
|
self.0.iter().find(|o| name.matches(&o.name))
|
2024-07-04 17:51:11 +04:00
|
|
|
}
|
|
|
|
|
|
2024-09-03 12:13:04 +03:00
|
|
|
pub fn find_mut(&mut self, name: &OutputName) -> Option<&mut Output> {
|
|
|
|
|
self.0.iter_mut().find(|o| name.matches(&o.name))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl OutputName {
|
|
|
|
|
pub fn from_ipc_output(output: &niri_ipc::Output) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
connector: output.name.clone(),
|
|
|
|
|
make: (output.make != "Unknown").then(|| output.make.clone()),
|
|
|
|
|
model: (output.model != "Unknown").then(|| output.model.clone()),
|
|
|
|
|
serial: output.serial.clone(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns an output description matching what Smithay's `Output::new()` does.
|
|
|
|
|
pub fn format_description(&self) -> String {
|
|
|
|
|
format!(
|
|
|
|
|
"{} - {} - {}",
|
|
|
|
|
self.make.as_deref().unwrap_or("Unknown"),
|
|
|
|
|
self.model.as_deref().unwrap_or("Unknown"),
|
|
|
|
|
self.connector,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns an output name that will match by make/model/serial or, if they are missing, by
|
|
|
|
|
/// connector.
|
|
|
|
|
pub fn format_make_model_serial_or_connector(&self) -> String {
|
|
|
|
|
if self.make.is_none() && self.model.is_none() && self.serial.is_none() {
|
|
|
|
|
self.connector.to_string()
|
|
|
|
|
} else {
|
2024-11-08 09:10:54 +03:00
|
|
|
self.format_make_model_serial()
|
2024-09-03 12:13:04 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-08 09:10:54 +03:00
|
|
|
pub fn format_make_model_serial(&self) -> String {
|
|
|
|
|
let make = self.make.as_deref().unwrap_or("Unknown");
|
|
|
|
|
let model = self.model.as_deref().unwrap_or("Unknown");
|
|
|
|
|
let serial = self.serial.as_deref().unwrap_or("Unknown");
|
|
|
|
|
format!("{make} {model} {serial}")
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-03 12:13:04 +03:00
|
|
|
pub fn matches(&self, target: &str) -> bool {
|
|
|
|
|
// Match by connector.
|
|
|
|
|
if target.eq_ignore_ascii_case(&self.connector) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no other fields are available, don't try to match by them.
|
|
|
|
|
//
|
|
|
|
|
// This is used by niri msg output.
|
|
|
|
|
if self.make.is_none() && self.model.is_none() && self.serial.is_none() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Match by "make model serial" with Unknown if something is missing.
|
|
|
|
|
let make = self.make.as_deref().unwrap_or("Unknown");
|
|
|
|
|
let model = self.model.as_deref().unwrap_or("Unknown");
|
|
|
|
|
let serial = self.serial.as_deref().unwrap_or("Unknown");
|
|
|
|
|
|
|
|
|
|
let Some(target_make) = target.get(..make.len()) else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
let rest = &target[make.len()..];
|
|
|
|
|
if !target_make.eq_ignore_ascii_case(make) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if !rest.starts_with(' ') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
let rest = &rest[1..];
|
|
|
|
|
|
|
|
|
|
let Some(target_model) = rest.get(..model.len()) else {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
let rest = &rest[model.len()..];
|
|
|
|
|
if !target_model.eq_ignore_ascii_case(model) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if !rest.starts_with(' ') {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let rest = &rest[1..];
|
|
|
|
|
if !rest.eq_ignore_ascii_case(serial) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
true
|
2024-07-04 17:51:11 +04:00
|
|
|
}
|
2024-09-05 20:10:01 +03:00
|
|
|
|
|
|
|
|
// Similar in spirit to Ord, but I don't want to derive Eq to avoid mistakes (you should use
|
|
|
|
|
// `Self::match`, not Eq).
|
|
|
|
|
pub fn compare(&self, other: &Self) -> std::cmp::Ordering {
|
|
|
|
|
let self_missing_mms = self.make.is_none() && self.model.is_none() && self.serial.is_none();
|
|
|
|
|
let other_missing_mms =
|
|
|
|
|
other.make.is_none() && other.model.is_none() && other.serial.is_none();
|
|
|
|
|
|
|
|
|
|
match (self_missing_mms, other_missing_mms) {
|
|
|
|
|
(true, true) => self.connector.cmp(&other.connector),
|
|
|
|
|
(true, false) => std::cmp::Ordering::Greater,
|
|
|
|
|
(false, true) => std::cmp::Ordering::Less,
|
|
|
|
|
(false, false) => self
|
|
|
|
|
.make
|
|
|
|
|
.cmp(&other.make)
|
|
|
|
|
.then_with(|| self.model.cmp(&other.model))
|
|
|
|
|
.then_with(|| self.serial.cmp(&other.serial))
|
|
|
|
|
.then_with(|| self.connector.cmp(&other.connector)),
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-04 17:51:11 +04:00
|
|
|
}
|
|
|
|
|
|
2024-09-05 23:37:10 +02:00
|
|
|
impl<S> knuffel::Decode<S> for DefaultPresetSize
|
2024-03-01 12:50:49 +01:00
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
expect_only_children(node, ctx);
|
|
|
|
|
|
|
|
|
|
let mut children = node.children();
|
|
|
|
|
|
|
|
|
|
if let Some(child) = children.next() {
|
|
|
|
|
if let Some(unwanted_child) = children.next() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
unwanted_child,
|
|
|
|
|
"node",
|
|
|
|
|
"expected no more than one child",
|
|
|
|
|
));
|
|
|
|
|
}
|
2024-09-05 23:37:10 +02:00
|
|
|
PresetSize::decode_node(child, ctx).map(Some).map(Self)
|
2024-03-01 12:50:49 +01:00
|
|
|
} else {
|
|
|
|
|
Ok(Self(None))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScalar<S>>(
|
|
|
|
|
name: &str,
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<T, DecodeError<S>> {
|
|
|
|
|
let mut iter_args = node.arguments.iter();
|
|
|
|
|
let val = iter_args.next().ok_or_else(|| {
|
|
|
|
|
DecodeError::missing(node, format!("additional argument `{name}` is required"))
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
let value = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
|
|
|
|
|
|
|
|
|
if let Some(val) = iter_args.next() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"unexpected argument",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
for name in node.properties.keys() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
format!("unexpected property `{}`", name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
for child in node.children() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
format!("unexpected node `{}`", child.node_name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(value)
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
impl<S> knuffel::Decode<S> for WorkspaceSwitchAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
let default = Self::default().0;
|
2024-04-21 20:10:35 +04:00
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for HorizontalViewMovementAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
let default = Self::default().0;
|
2024-04-21 20:10:35 +04:00
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for WindowMovementAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
let default = Self::default().0;
|
2024-04-21 20:10:35 +04:00
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-11 22:40:30 +02:00
|
|
|
impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
|
|
|
|
|
fn type_check(
|
|
|
|
|
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(type_name) = &type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn raw_decode(
|
|
|
|
|
val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<WorkspaceName, DecodeError<S>> {
|
|
|
|
|
#[derive(Debug)]
|
2024-05-16 10:54:24 +04:00
|
|
|
struct WorkspaceNameSet(Vec<String>);
|
2024-05-11 22:40:30 +02:00
|
|
|
match &**val {
|
|
|
|
|
knuffel::ast::Literal::String(ref s) => {
|
2024-05-16 10:54:24 +04:00
|
|
|
let mut name_set: Vec<String> = match ctx.get::<WorkspaceNameSet>() {
|
2024-05-11 22:40:30 +02:00
|
|
|
Some(h) => h.0.clone(),
|
2024-05-16 10:54:24 +04:00
|
|
|
None => Vec::new(),
|
2024-05-11 22:40:30 +02:00
|
|
|
};
|
2024-05-16 10:54:24 +04:00
|
|
|
|
|
|
|
|
if name_set.iter().any(|name| name.eq_ignore_ascii_case(s)) {
|
2024-05-11 22:40:30 +02:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
val,
|
|
|
|
|
"named workspace",
|
2025-07-13 12:49:00 +03:00
|
|
|
format!("duplicate named workspace: {s}"),
|
2024-05-11 22:40:30 +02:00
|
|
|
));
|
|
|
|
|
return Ok(Self(String::new()));
|
|
|
|
|
}
|
2024-05-16 10:54:24 +04:00
|
|
|
|
|
|
|
|
name_set.push(s.to_string());
|
2024-05-11 22:40:30 +02:00
|
|
|
ctx.set(WorkspaceNameSet(name_set));
|
|
|
|
|
Ok(Self(s.clone().into()))
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
ctx.emit_error(DecodeError::unsupported(
|
|
|
|
|
val,
|
|
|
|
|
"workspace names must be strings",
|
|
|
|
|
));
|
|
|
|
|
Ok(Self(String::new()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
impl<S> knuffel::Decode<S> for WindowOpenAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-05-15 19:38:29 +04:00
|
|
|
let default = Self::default().anim;
|
|
|
|
|
let mut custom_shader = None;
|
|
|
|
|
let anim = Animation::decode_node(node, ctx, default, |child, ctx| {
|
|
|
|
|
if &**child.node_name == "custom-shader" {
|
|
|
|
|
custom_shader = parse_arg_node("custom-shader", child, ctx)?;
|
|
|
|
|
Ok(true)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
anim,
|
|
|
|
|
custom_shader,
|
|
|
|
|
})
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for WindowCloseAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-05-12 09:52:21 +04:00
|
|
|
let default = Self::default().anim;
|
|
|
|
|
let mut custom_shader = None;
|
|
|
|
|
let anim = Animation::decode_node(node, ctx, default, |child, ctx| {
|
|
|
|
|
if &**child.node_name == "custom-shader" {
|
|
|
|
|
custom_shader = parse_arg_node("custom-shader", child, ctx)?;
|
|
|
|
|
Ok(true)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
anim,
|
|
|
|
|
custom_shader,
|
|
|
|
|
})
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for WindowResizeAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-04-21 20:10:35 +04:00
|
|
|
let default = Self::default().anim;
|
|
|
|
|
let mut custom_shader = None;
|
|
|
|
|
let anim = Animation::decode_node(node, ctx, default, |child, ctx| {
|
|
|
|
|
if &**child.node_name == "custom-shader" {
|
|
|
|
|
custom_shader = parse_arg_node("custom-shader", child, ctx)?;
|
|
|
|
|
Ok(true)
|
|
|
|
|
} else {
|
|
|
|
|
Ok(false)
|
|
|
|
|
}
|
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(Self {
|
|
|
|
|
anim,
|
|
|
|
|
custom_shader,
|
|
|
|
|
})
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for ConfigNotificationOpenCloseAnim
|
2024-03-05 13:32:30 +04:00
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-04-17 14:06:32 +04:00
|
|
|
let default = Self::default().0;
|
2024-04-21 20:10:35 +04:00
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
2024-04-17 14:06:32 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-08 11:24:08 +04:00
|
|
|
impl<S> knuffel::Decode<S> for ScreenshotUiOpenAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
let default = Self::default().0;
|
|
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-25 09:36:50 +03:00
|
|
|
impl<S> knuffel::Decode<S> for OverviewOpenCloseAnim
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
let default = Self::default().0;
|
|
|
|
|
Ok(Self(Animation::decode_node(node, ctx, default, |_, _| {
|
|
|
|
|
Ok(false)
|
|
|
|
|
})?))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
impl Animation {
|
2024-07-05 20:12:56 +04:00
|
|
|
pub fn new_off() -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
off: true,
|
|
|
|
|
kind: AnimationKind::Easing(EasingParams {
|
|
|
|
|
duration_ms: 0,
|
|
|
|
|
curve: AnimationCurve::Linear,
|
|
|
|
|
}),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-17 14:06:32 +04:00
|
|
|
fn decode_node<S: knuffel::traits::ErrorSpan>(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
default: Self,
|
2024-04-21 20:10:35 +04:00
|
|
|
mut process_children: impl FnMut(
|
|
|
|
|
&knuffel::ast::SpannedNode<S>,
|
|
|
|
|
&mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<bool, DecodeError<S>>,
|
2024-04-17 14:06:32 +04:00
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
#[derive(Default, PartialEq)]
|
|
|
|
|
struct OptionalEasingParams {
|
|
|
|
|
duration_ms: Option<u32>,
|
|
|
|
|
curve: Option<AnimationCurve>,
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
expect_only_children(node, ctx);
|
|
|
|
|
|
|
|
|
|
let mut off = false;
|
2024-04-17 14:06:32 +04:00
|
|
|
let mut easing_params = OptionalEasingParams::default();
|
2024-03-05 13:32:30 +04:00
|
|
|
let mut spring_params = None;
|
|
|
|
|
|
|
|
|
|
for child in node.children() {
|
|
|
|
|
match &**child.node_name {
|
|
|
|
|
"off" => {
|
|
|
|
|
knuffel::decode::check_flag_node(child, ctx);
|
|
|
|
|
if off {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&child.node_name,
|
|
|
|
|
"node",
|
|
|
|
|
"duplicate node `off`, single node expected",
|
|
|
|
|
));
|
|
|
|
|
} else {
|
|
|
|
|
off = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
"spring" => {
|
2024-04-17 14:06:32 +04:00
|
|
|
if easing_params != OptionalEasingParams::default() {
|
2024-03-05 13:32:30 +04:00
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
"cannot set both spring and easing parameters at once",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if spring_params.is_some() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&child.node_name,
|
|
|
|
|
"node",
|
|
|
|
|
"duplicate node `spring`, single node expected",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
spring_params = Some(SpringParams::decode_node(child, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
"duration-ms" => {
|
|
|
|
|
if spring_params.is_some() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
"cannot set both spring and easing parameters at once",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if easing_params.duration_ms.is_some() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&child.node_name,
|
|
|
|
|
"node",
|
|
|
|
|
"duplicate node `duration-ms`, single node expected",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
easing_params.duration_ms = Some(parse_arg_node("duration-ms", child, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
"curve" => {
|
|
|
|
|
if spring_params.is_some() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
"cannot set both spring and easing parameters at once",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if easing_params.curve.is_some() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&child.node_name,
|
|
|
|
|
"node",
|
|
|
|
|
"duplicate node `curve`, single node expected",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
easing_params.curve = Some(parse_arg_node("curve", child, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
name_str => {
|
2024-04-21 20:10:35 +04:00
|
|
|
if !process_children(child, ctx)? {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
format!("unexpected node `{}`", name_str.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
2024-03-05 13:32:30 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let kind = if let Some(spring_params) = spring_params {
|
2024-04-17 14:06:32 +04:00
|
|
|
// Configured spring.
|
2024-03-05 13:32:30 +04:00
|
|
|
AnimationKind::Spring(spring_params)
|
2024-04-17 14:06:32 +04:00
|
|
|
} else if easing_params == OptionalEasingParams::default() {
|
|
|
|
|
// Did not configure anything.
|
|
|
|
|
default.kind
|
2024-03-05 13:32:30 +04:00
|
|
|
} else {
|
2024-04-17 14:06:32 +04:00
|
|
|
// Configured easing.
|
|
|
|
|
let default = if let AnimationKind::Easing(easing) = default.kind {
|
|
|
|
|
easing
|
|
|
|
|
} else {
|
|
|
|
|
// Generic fallback values for when the default animation is spring, but the user
|
|
|
|
|
// configured an easing animation.
|
|
|
|
|
EasingParams {
|
|
|
|
|
duration_ms: 250,
|
|
|
|
|
curve: AnimationCurve::EaseOutCubic,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AnimationKind::Easing(EasingParams {
|
|
|
|
|
duration_ms: easing_params.duration_ms.unwrap_or(default.duration_ms),
|
|
|
|
|
curve: easing_params.curve.unwrap_or(default.curve),
|
|
|
|
|
})
|
2024-03-05 13:32:30 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(Self { off, kind })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for SpringParams
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
if let Some(type_name) = &node.type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if let Some(val) = node.arguments.first() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"unexpected argument",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
for child in node.children() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
format!("unexpected node `{}`", child.node_name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut damping_ratio = None;
|
|
|
|
|
let mut stiffness = None;
|
|
|
|
|
let mut epsilon = None;
|
|
|
|
|
for (name, val) in &node.properties {
|
|
|
|
|
match &***name {
|
|
|
|
|
"damping-ratio" => {
|
|
|
|
|
damping_ratio = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
"stiffness" => {
|
|
|
|
|
stiffness = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
"epsilon" => {
|
|
|
|
|
epsilon = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);
|
|
|
|
|
}
|
|
|
|
|
name_str => {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
format!("unexpected property `{}`", name_str.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let damping_ratio = damping_ratio
|
|
|
|
|
.ok_or_else(|| DecodeError::missing(node, "property `damping-ratio` is required"))?;
|
|
|
|
|
let stiffness = stiffness
|
|
|
|
|
.ok_or_else(|| DecodeError::missing(node, "property `stiffness` is required"))?;
|
|
|
|
|
let epsilon =
|
|
|
|
|
epsilon.ok_or_else(|| DecodeError::missing(node, "property `epsilon` is required"))?;
|
|
|
|
|
|
|
|
|
|
if !(0.1..=10.).contains(&damping_ratio) {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(
|
|
|
|
|
node,
|
|
|
|
|
"damping-ratio must be between 0.1 and 10.0",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
if stiffness < 1 {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(node, "stiffness must be >= 1"));
|
|
|
|
|
}
|
|
|
|
|
if !(0.00001..=0.1).contains(&epsilon) {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(
|
|
|
|
|
node,
|
|
|
|
|
"epsilon must be between 0.00001 and 0.1",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(SpringParams {
|
|
|
|
|
damping_ratio,
|
|
|
|
|
stiffness,
|
|
|
|
|
epsilon,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 19:06:08 +04:00
|
|
|
impl<S> knuffel::Decode<S> for CornerRadius
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
// Check for unexpected type name.
|
|
|
|
|
if let Some(type_name) = &node.type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let decode_radius = |ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
val: &knuffel::ast::Value<S>| {
|
|
|
|
|
// Check for unexpected type name.
|
|
|
|
|
if let Some(typ) = &val.type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::TypeName {
|
|
|
|
|
span: typ.span().clone(),
|
|
|
|
|
found: Some((**typ).clone()),
|
|
|
|
|
expected: knuffel::errors::ExpectedType::no_type(),
|
|
|
|
|
rust_type: "str",
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Decode both integers and floats.
|
|
|
|
|
let radius = match *val.literal {
|
|
|
|
|
knuffel::ast::Literal::Int(ref x) => f32::from(match x.try_into() {
|
|
|
|
|
Ok(x) => x,
|
|
|
|
|
Err(err) => {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(&val.literal, err));
|
|
|
|
|
0i16
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
knuffel::ast::Literal::Decimal(ref x) => match x.try_into() {
|
|
|
|
|
Ok(x) => x,
|
|
|
|
|
Err(err) => {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(&val.literal, err));
|
|
|
|
|
0.
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
_ => {
|
|
|
|
|
ctx.emit_error(DecodeError::scalar_kind(
|
|
|
|
|
knuffel::decode::Kind::Int,
|
|
|
|
|
&val.literal,
|
|
|
|
|
));
|
|
|
|
|
0.
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if radius < 0. {
|
|
|
|
|
ctx.emit_error(DecodeError::conversion(&val.literal, "radius must be >= 0"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
radius
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Get the first argument.
|
|
|
|
|
let mut iter_args = node.arguments.iter();
|
|
|
|
|
let val = iter_args
|
|
|
|
|
.next()
|
|
|
|
|
.ok_or_else(|| DecodeError::missing(node, "additional argument is required"))?;
|
|
|
|
|
|
|
|
|
|
let top_left = decode_radius(ctx, val);
|
|
|
|
|
|
|
|
|
|
let mut rv = CornerRadius {
|
|
|
|
|
top_left,
|
|
|
|
|
top_right: top_left,
|
|
|
|
|
bottom_right: top_left,
|
|
|
|
|
bottom_left: top_left,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Some(val) = iter_args.next() {
|
|
|
|
|
rv.top_right = decode_radius(ctx, val);
|
|
|
|
|
|
|
|
|
|
let val = iter_args.next().ok_or_else(|| {
|
|
|
|
|
DecodeError::missing(node, "either 1 or 4 arguments are required")
|
|
|
|
|
})?;
|
|
|
|
|
rv.bottom_right = decode_radius(ctx, val);
|
|
|
|
|
|
|
|
|
|
let val = iter_args.next().ok_or_else(|| {
|
|
|
|
|
DecodeError::missing(node, "either 1 or 4 arguments are required")
|
|
|
|
|
})?;
|
|
|
|
|
rv.bottom_left = decode_radius(ctx, val);
|
|
|
|
|
|
|
|
|
|
// Check for unexpected following arguments.
|
|
|
|
|
if let Some(val) = iter_args.next() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"unexpected argument",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for unexpected properties and children.
|
|
|
|
|
for name in node.properties.keys() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
format!("unexpected property `{}`", name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
for child in node.children.as_ref().map(|lst| &lst[..]).unwrap_or(&[]) {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
child,
|
|
|
|
|
"node",
|
|
|
|
|
format!("unexpected node `{}`", child.node_name.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(rv)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
impl<S> knuffel::Decode<S> for Binds
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
|
|
|
|
expect_only_children(node, ctx);
|
|
|
|
|
|
|
|
|
|
let mut seen_keys = HashSet::new();
|
|
|
|
|
|
|
|
|
|
let mut binds = Vec::new();
|
|
|
|
|
|
|
|
|
|
for child in node.children() {
|
|
|
|
|
match Bind::decode_node(child, ctx) {
|
|
|
|
|
Err(e) => {
|
|
|
|
|
ctx.emit_error(e);
|
|
|
|
|
}
|
|
|
|
|
Ok(bind) => {
|
|
|
|
|
if seen_keys.insert(bind.key) {
|
|
|
|
|
binds.push(bind);
|
|
|
|
|
} else {
|
|
|
|
|
// ideally, this error should point to the previous instance of this keybind
|
|
|
|
|
//
|
|
|
|
|
// i (sodiboo) have tried to implement this in various ways:
|
|
|
|
|
// miette!(), #[derive(Diagnostic)]
|
|
|
|
|
// DecodeError::Custom, DecodeError::Conversion
|
|
|
|
|
// nothing seems to work, and i suspect it's not possible.
|
|
|
|
|
//
|
|
|
|
|
// DecodeError is fairly restrictive.
|
|
|
|
|
// even DecodeError::Custom just wraps a std::error::Error
|
|
|
|
|
// and this erases all rich information from miette. (why???)
|
|
|
|
|
//
|
|
|
|
|
// why does knuffel do this?
|
|
|
|
|
// from what i can tell, it doesn't even use DecodeError for much.
|
|
|
|
|
// it only ever converts them to a Report anyways!
|
|
|
|
|
// https://github.com/tailhook/knuffel/blob/c44c6b0c0f31ea6d1174d5d2ed41064922ea44ca/src/wrappers.rs#L55-L58
|
|
|
|
|
//
|
|
|
|
|
// besides like, allowing downstream users (such as us!)
|
|
|
|
|
// to match on parse failure, i don't understand why
|
|
|
|
|
// it doesn't just use a generic error type
|
|
|
|
|
//
|
|
|
|
|
// even the matching isn't consistent,
|
|
|
|
|
// because errors can also be omitted as ctx.emit_error.
|
|
|
|
|
// why does *that one* especially, require a DecodeError?
|
|
|
|
|
//
|
|
|
|
|
// anyways if you can make it format nicely, definitely do fix this
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&child.node_name,
|
|
|
|
|
"keybind",
|
|
|
|
|
"duplicate keybind",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(Self(binds))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<S> knuffel::Decode<S> for Bind
|
|
|
|
|
where
|
|
|
|
|
S: knuffel::traits::ErrorSpan,
|
|
|
|
|
{
|
|
|
|
|
fn decode_node(
|
|
|
|
|
node: &knuffel::ast::SpannedNode<S>,
|
|
|
|
|
ctx: &mut knuffel::decode::Context<S>,
|
|
|
|
|
) -> Result<Self, DecodeError<S>> {
|
2024-03-22 20:47:40 +04:00
|
|
|
if let Some(type_name) = &node.type_name {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
type_name,
|
|
|
|
|
"type name",
|
|
|
|
|
"no type name expected for this node",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for val in node.arguments.iter() {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
&val.literal,
|
|
|
|
|
"argument",
|
|
|
|
|
"no arguments expected for this node",
|
2024-04-19 10:49:46 +04:00
|
|
|
));
|
2024-03-22 20:47:40 +04:00
|
|
|
}
|
2024-03-01 12:50:49 +01:00
|
|
|
|
|
|
|
|
let key = node
|
|
|
|
|
.node_name
|
|
|
|
|
.parse::<Key>()
|
|
|
|
|
.map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?;
|
|
|
|
|
|
2024-06-30 22:37:44 +05:00
|
|
|
let mut repeat = true;
|
2024-03-22 20:47:40 +04:00
|
|
|
let mut cooldown = None;
|
2024-04-19 10:49:46 +04:00
|
|
|
let mut allow_when_locked = false;
|
|
|
|
|
let mut allow_when_locked_node = None;
|
2025-01-18 15:26:42 +01:00
|
|
|
let mut allow_inhibiting = true;
|
2025-02-13 08:45:23 +03:00
|
|
|
let mut hotkey_overlay_title = None;
|
2024-03-22 20:47:40 +04:00
|
|
|
for (name, val) in &node.properties {
|
|
|
|
|
match &***name {
|
2024-06-30 22:37:44 +05:00
|
|
|
"repeat" => {
|
|
|
|
|
repeat = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
|
|
|
|
}
|
2024-03-22 20:47:40 +04:00
|
|
|
"cooldown-ms" => {
|
|
|
|
|
cooldown = Some(Duration::from_millis(
|
|
|
|
|
knuffel::traits::DecodeScalar::decode(val, ctx)?,
|
|
|
|
|
));
|
|
|
|
|
}
|
2024-04-19 10:49:46 +04:00
|
|
|
"allow-when-locked" => {
|
|
|
|
|
allow_when_locked = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
|
|
|
|
allow_when_locked_node = Some(name);
|
|
|
|
|
}
|
2025-01-18 15:26:42 +01:00
|
|
|
"allow-inhibiting" => {
|
|
|
|
|
allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
|
|
|
|
}
|
2025-02-13 08:45:23 +03:00
|
|
|
"hotkey-overlay-title" => {
|
|
|
|
|
hotkey_overlay_title = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?);
|
|
|
|
|
}
|
2024-03-22 20:47:40 +04:00
|
|
|
name_str => {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
name,
|
|
|
|
|
"property",
|
|
|
|
|
format!("unexpected property `{}`", name_str.escape_default()),
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-01 12:50:49 +01:00
|
|
|
let mut children = node.children();
|
|
|
|
|
|
|
|
|
|
// If the action is invalid but the key is fine, we still want to return something.
|
|
|
|
|
// That way, the parent can handle the existence of duplicate keybinds,
|
|
|
|
|
// even if their contents are not valid.
|
|
|
|
|
let dummy = Self {
|
|
|
|
|
key,
|
|
|
|
|
action: Action::Spawn(vec![]),
|
2024-06-30 22:37:44 +05:00
|
|
|
repeat: true,
|
2024-03-22 20:47:40 +04:00
|
|
|
cooldown: None,
|
2024-04-19 10:49:46 +04:00
|
|
|
allow_when_locked: false,
|
2025-01-18 15:26:42 +01:00
|
|
|
allow_inhibiting: true,
|
2025-02-13 08:45:23 +03:00
|
|
|
hotkey_overlay_title: None,
|
2024-03-01 12:50:49 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if let Some(child) = children.next() {
|
|
|
|
|
for unwanted_child in children {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
unwanted_child,
|
|
|
|
|
"node",
|
|
|
|
|
"only one action is allowed per keybind",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
match Action::decode_node(child, ctx) {
|
2024-04-19 10:49:46 +04:00
|
|
|
Ok(action) => {
|
|
|
|
|
if !matches!(action, Action::Spawn(_)) {
|
|
|
|
|
if let Some(node) = allow_when_locked_node {
|
|
|
|
|
ctx.emit_error(DecodeError::unexpected(
|
|
|
|
|
node,
|
|
|
|
|
"property",
|
|
|
|
|
"allow-when-locked can only be set on spawn binds",
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-18 15:26:42 +01:00
|
|
|
// The toggle-inhibit action must always be uninhibitable.
|
|
|
|
|
// Otherwise, it would be impossible to trigger it.
|
|
|
|
|
if matches!(action, Action::ToggleKeyboardShortcutsInhibit) {
|
|
|
|
|
allow_inhibiting = false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-19 10:49:46 +04:00
|
|
|
Ok(Self {
|
|
|
|
|
key,
|
|
|
|
|
action,
|
2024-06-30 22:37:44 +05:00
|
|
|
repeat,
|
2024-04-19 10:49:46 +04:00
|
|
|
cooldown,
|
|
|
|
|
allow_when_locked,
|
2025-01-18 15:26:42 +01:00
|
|
|
allow_inhibiting,
|
2025-02-13 08:45:23 +03:00
|
|
|
hotkey_overlay_title,
|
2024-04-19 10:49:46 +04:00
|
|
|
})
|
|
|
|
|
}
|
2024-03-01 12:50:49 +01:00
|
|
|
Err(e) => {
|
|
|
|
|
ctx.emit_error(e);
|
|
|
|
|
Ok(dummy)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ctx.emit_error(DecodeError::missing(
|
|
|
|
|
node,
|
|
|
|
|
"expected an action for this keybind",
|
|
|
|
|
));
|
|
|
|
|
Ok(dummy)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-05 09:34:25 -05:00
|
|
|
impl FromStr for ModKey {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match &*s.to_ascii_lowercase() {
|
|
|
|
|
"ctrl" | "control" => Ok(Self::Ctrl),
|
|
|
|
|
"shift" => Ok(Self::Shift),
|
|
|
|
|
"alt" => Ok(Self::Alt),
|
|
|
|
|
"super" | "win" => Ok(Self::Super),
|
|
|
|
|
"iso_level3_shift" | "mod5" => Ok(Self::IsoLevel3Shift),
|
|
|
|
|
"iso_level5_shift" | "mod3" => Ok(Self::IsoLevel5Shift),
|
|
|
|
|
_ => Err(miette!("invalid Mod key: {s}")),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
impl FromStr for Key {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let mut modifiers = Modifiers::empty();
|
|
|
|
|
|
|
|
|
|
let mut split = s.split('+');
|
|
|
|
|
let key = split.next_back().unwrap();
|
|
|
|
|
|
|
|
|
|
for part in split {
|
|
|
|
|
let part = part.trim();
|
|
|
|
|
if part.eq_ignore_ascii_case("mod") {
|
|
|
|
|
modifiers |= Modifiers::COMPOSITOR
|
|
|
|
|
} else if part.eq_ignore_ascii_case("ctrl") || part.eq_ignore_ascii_case("control") {
|
|
|
|
|
modifiers |= Modifiers::CTRL;
|
|
|
|
|
} else if part.eq_ignore_ascii_case("shift") {
|
|
|
|
|
modifiers |= Modifiers::SHIFT;
|
|
|
|
|
} else if part.eq_ignore_ascii_case("alt") {
|
|
|
|
|
modifiers |= Modifiers::ALT;
|
|
|
|
|
} else if part.eq_ignore_ascii_case("super") || part.eq_ignore_ascii_case("win") {
|
|
|
|
|
modifiers |= Modifiers::SUPER;
|
2024-03-22 22:09:25 +01:00
|
|
|
} else if part.eq_ignore_ascii_case("iso_level3_shift")
|
|
|
|
|
|| part.eq_ignore_ascii_case("mod5")
|
|
|
|
|
{
|
|
|
|
|
modifiers |= Modifiers::ISO_LEVEL3_SHIFT;
|
2024-07-31 11:00:35 -04:00
|
|
|
} else if part.eq_ignore_ascii_case("iso_level5_shift")
|
|
|
|
|
|| part.eq_ignore_ascii_case("mod3")
|
|
|
|
|
{
|
|
|
|
|
modifiers |= Modifiers::ISO_LEVEL5_SHIFT;
|
2023-09-05 12:58:51 +04:00
|
|
|
} else {
|
|
|
|
|
return Err(miette!("invalid modifier: {part}"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-13 02:40:36 +02:00
|
|
|
let trigger = if key.eq_ignore_ascii_case("MouseLeft") {
|
|
|
|
|
Trigger::MouseLeft
|
|
|
|
|
} else if key.eq_ignore_ascii_case("MouseRight") {
|
|
|
|
|
Trigger::MouseRight
|
|
|
|
|
} else if key.eq_ignore_ascii_case("MouseMiddle") {
|
|
|
|
|
Trigger::MouseMiddle
|
|
|
|
|
} else if key.eq_ignore_ascii_case("MouseBack") {
|
|
|
|
|
Trigger::MouseBack
|
|
|
|
|
} else if key.eq_ignore_ascii_case("MouseForward") {
|
|
|
|
|
Trigger::MouseForward
|
|
|
|
|
} else if key.eq_ignore_ascii_case("WheelScrollDown") {
|
2024-03-23 08:49:58 +04:00
|
|
|
Trigger::WheelScrollDown
|
|
|
|
|
} else if key.eq_ignore_ascii_case("WheelScrollUp") {
|
|
|
|
|
Trigger::WheelScrollUp
|
|
|
|
|
} else if key.eq_ignore_ascii_case("WheelScrollLeft") {
|
|
|
|
|
Trigger::WheelScrollLeft
|
|
|
|
|
} else if key.eq_ignore_ascii_case("WheelScrollRight") {
|
|
|
|
|
Trigger::WheelScrollRight
|
2024-03-23 20:23:21 +04:00
|
|
|
} else if key.eq_ignore_ascii_case("TouchpadScrollDown") {
|
|
|
|
|
Trigger::TouchpadScrollDown
|
|
|
|
|
} else if key.eq_ignore_ascii_case("TouchpadScrollUp") {
|
|
|
|
|
Trigger::TouchpadScrollUp
|
|
|
|
|
} else if key.eq_ignore_ascii_case("TouchpadScrollLeft") {
|
|
|
|
|
Trigger::TouchpadScrollLeft
|
|
|
|
|
} else if key.eq_ignore_ascii_case("TouchpadScrollRight") {
|
|
|
|
|
Trigger::TouchpadScrollRight
|
2024-03-22 10:36:19 +04:00
|
|
|
} else {
|
|
|
|
|
let keysym = keysym_from_name(key, KEYSYM_CASE_INSENSITIVE);
|
|
|
|
|
if keysym.raw() == KEY_NoSymbol {
|
|
|
|
|
return Err(miette!("invalid key: {key}"));
|
|
|
|
|
}
|
|
|
|
|
Trigger::Keysym(keysym)
|
|
|
|
|
};
|
2023-09-05 12:58:51 +04:00
|
|
|
|
2024-03-22 10:36:19 +04:00
|
|
|
Ok(Key { trigger, modifiers })
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-13 21:26:03 -07:00
|
|
|
impl FromStr for ClickMethod {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"clickfinger" => Ok(Self::Clickfinger),
|
|
|
|
|
"button-areas" => Ok(Self::ButtonAreas),
|
|
|
|
|
_ => Err(miette!(
|
|
|
|
|
r#"invalid click method, can be "button-areas" or "clickfinger""#
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 10:15:29 +04:00
|
|
|
impl FromStr for AccelProfile {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"adaptive" => Ok(Self::Adaptive),
|
|
|
|
|
"flat" => Ok(Self::Flat),
|
|
|
|
|
_ => Err(miette!(
|
|
|
|
|
r#"invalid accel profile, can be "adaptive" or "flat""#
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-26 14:41:00 +09:00
|
|
|
impl FromStr for ScrollMethod {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"no-scroll" => Ok(Self::NoScroll),
|
|
|
|
|
"two-finger" => Ok(Self::TwoFinger),
|
|
|
|
|
"edge" => Ok(Self::Edge),
|
|
|
|
|
"on-button-down" => Ok(Self::OnButtonDown),
|
|
|
|
|
_ => Err(miette!(
|
2024-05-28 14:38:24 +09:00
|
|
|
r#"invalid scroll method, can be "no-scroll", "two-finger", "edge", or "on-button-down""#
|
2024-05-26 14:41:00 +09:00
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-08 10:32:04 +04:00
|
|
|
impl FromStr for TapButtonMap {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
match s {
|
|
|
|
|
"left-right-middle" => Ok(Self::LeftRightMiddle),
|
|
|
|
|
"left-middle-right" => Ok(Self::LeftMiddleRight),
|
|
|
|
|
_ => Err(miette!(
|
|
|
|
|
r#"invalid tap button map, can be "left-right-middle" or "left-middle-right""#
|
|
|
|
|
)),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-05 20:12:56 +04:00
|
|
|
impl FromStr for Percent {
|
|
|
|
|
type Err = miette::Error;
|
|
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
|
|
|
let Some((value, empty)) = s.split_once('%') else {
|
|
|
|
|
return Err(miette!("value must end with '%'"));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if !empty.is_empty() {
|
|
|
|
|
return Err(miette!("trailing characters after '%' are not allowed"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let value: f64 = value.parse().map_err(|_| miette!("error parsing value"))?;
|
|
|
|
|
Ok(Percent(value / 100.))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
2024-12-27 15:39:37 +03:00
|
|
|
use insta::{assert_debug_snapshot, assert_snapshot};
|
2024-12-28 11:40:16 +03:00
|
|
|
use niri_ipc::PositionChange;
|
2024-06-11 09:10:46 +03:00
|
|
|
use pretty_assertions::assert_eq;
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[track_caller]
|
2025-02-16 08:46:38 +03:00
|
|
|
fn do_parse(text: &str) -> Config {
|
|
|
|
|
Config::parse("test.kdl", text)
|
2023-09-05 12:58:51 +04:00
|
|
|
.map_err(miette::Report::new)
|
2025-02-16 08:46:38 +03:00
|
|
|
.unwrap()
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse() {
|
2025-02-16 08:46:38 +03:00
|
|
|
let parsed = do_parse(
|
2024-02-21 21:27:44 +04:00
|
|
|
r##"
|
2023-09-05 12:58:51 +04:00
|
|
|
input {
|
|
|
|
|
keyboard {
|
2023-09-16 20:01:52 +04:00
|
|
|
repeat-delay 600
|
2023-09-16 12:37:23 +04:00
|
|
|
repeat-rate 25
|
2023-11-02 00:10:22 +04:00
|
|
|
track-layout "window"
|
2023-09-05 12:58:51 +04:00
|
|
|
xkb {
|
|
|
|
|
layout "us,ru"
|
|
|
|
|
options "grp:win_space_toggle"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
touchpad {
|
|
|
|
|
tap
|
2024-01-08 10:24:00 +04:00
|
|
|
dwt
|
2024-02-03 08:20:15 +04:00
|
|
|
dwtp
|
2025-03-01 23:01:34 -08:00
|
|
|
drag true
|
2024-03-13 21:26:03 -07:00
|
|
|
click-method "clickfinger"
|
2023-09-05 12:58:51 +04:00
|
|
|
accel-speed 0.2
|
2024-01-08 10:15:29 +04:00
|
|
|
accel-profile "flat"
|
2024-05-26 14:41:00 +09:00
|
|
|
scroll-method "two-finger"
|
2024-10-17 01:43:47 -04:00
|
|
|
scroll-button 272
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll-button-lock
|
2024-01-08 10:32:04 +04:00
|
|
|
tap-button-map "left-middle-right"
|
2024-05-23 16:01:54 +09:00
|
|
|
disabled-on-external-mouse
|
2024-11-03 20:43:03 +02:00
|
|
|
scroll-factor 0.9
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
2023-10-03 17:02:07 +04:00
|
|
|
|
2024-01-08 11:53:34 +04:00
|
|
|
mouse {
|
|
|
|
|
natural-scroll
|
|
|
|
|
accel-speed 0.4
|
|
|
|
|
accel-profile "flat"
|
2024-05-26 14:41:00 +09:00
|
|
|
scroll-method "no-scroll"
|
2024-10-17 01:43:47 -04:00
|
|
|
scroll-button 273
|
2024-07-13 07:34:22 +03:00
|
|
|
middle-emulation
|
2024-11-03 20:43:03 +02:00
|
|
|
scroll-factor 0.2
|
2024-01-08 11:53:34 +04:00
|
|
|
}
|
|
|
|
|
|
2024-02-14 16:24:46 +01:00
|
|
|
trackpoint {
|
2024-05-23 16:01:54 +09:00
|
|
|
off
|
2024-02-14 16:24:46 +01:00
|
|
|
natural-scroll
|
|
|
|
|
accel-speed 0.0
|
|
|
|
|
accel-profile "flat"
|
2024-05-26 14:41:00 +09:00
|
|
|
scroll-method "on-button-down"
|
2024-10-17 01:43:47 -04:00
|
|
|
scroll-button 274
|
2024-02-14 16:24:46 +01:00
|
|
|
}
|
|
|
|
|
|
2024-10-16 16:51:56 +03:00
|
|
|
trackball {
|
|
|
|
|
off
|
|
|
|
|
natural-scroll
|
|
|
|
|
accel-speed 0.0
|
|
|
|
|
accel-profile "flat"
|
2024-10-17 01:43:47 -04:00
|
|
|
scroll-method "edge"
|
|
|
|
|
scroll-button 275
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll-button-lock
|
2024-10-16 16:51:56 +03:00
|
|
|
left-handed
|
|
|
|
|
middle-emulation
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-03 17:02:07 +04:00
|
|
|
tablet {
|
|
|
|
|
map-to-output "eDP-1"
|
2025-02-14 08:15:45 +03:00
|
|
|
calibration-matrix 1.0 2.0 3.0 \
|
|
|
|
|
4.0 5.0 6.0
|
2023-10-03 17:02:07 +04:00
|
|
|
}
|
2023-12-28 09:36:10 +04:00
|
|
|
|
2024-02-24 18:32:13 +01:00
|
|
|
touch {
|
|
|
|
|
map-to-output "eDP-1"
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-28 09:36:10 +04:00
|
|
|
disable-power-key-handling
|
2024-02-26 18:47:46 +01:00
|
|
|
|
|
|
|
|
warp-mouse-to-focus
|
2024-03-18 18:17:04 +04:00
|
|
|
focus-follows-mouse
|
2024-03-19 14:27:52 +00:00
|
|
|
workspace-auto-back-and-forth
|
2025-02-05 09:34:25 -05:00
|
|
|
|
|
|
|
|
mod-key "Mod5"
|
|
|
|
|
mod-key-nested "Super"
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
2023-09-21 13:48:32 +04:00
|
|
|
output "eDP-1" {
|
2025-03-29 10:13:59 +00:00
|
|
|
focus-at-startup
|
2024-06-18 11:01:18 +03:00
|
|
|
scale 2
|
2024-01-28 14:25:40 +01:00
|
|
|
transform "flipped-90"
|
2023-09-30 11:33:02 +04:00
|
|
|
position x=10 y=20
|
2023-10-03 08:35:24 +04:00
|
|
|
mode "1920x1080@144"
|
2024-08-22 18:58:07 +10:00
|
|
|
variable-refresh-rate on-demand=true
|
2024-07-25 23:41:33 +05:30
|
|
|
background-color "rgba(25, 25, 102, 1.0)"
|
2023-09-21 13:48:32 +04:00
|
|
|
}
|
|
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
layout {
|
|
|
|
|
focus-ring {
|
|
|
|
|
width 5
|
|
|
|
|
active-color 0 100 200 255
|
|
|
|
|
inactive-color 255 200 100 0
|
2024-02-21 21:27:44 +04:00
|
|
|
active-gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
|
2024-01-06 13:04:21 +04:00
|
|
|
}
|
2023-09-21 19:58:03 +04:00
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
border {
|
|
|
|
|
width 3
|
2024-02-26 09:02:47 +04:00
|
|
|
inactive-color "rgba(255, 200, 100, 0.0)"
|
2024-01-06 13:04:21 +04:00
|
|
|
}
|
2023-09-26 13:09:33 +04:00
|
|
|
|
2025-01-15 14:16:05 +03:00
|
|
|
shadow {
|
|
|
|
|
offset x=10 y=-20
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-02 08:41:42 +03:00
|
|
|
tab-indicator {
|
|
|
|
|
width 10
|
2025-02-05 10:36:46 +03:00
|
|
|
position "top"
|
2025-02-02 08:41:42 +03:00
|
|
|
}
|
|
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
preset-column-widths {
|
|
|
|
|
proportion 0.25
|
|
|
|
|
proportion 0.5
|
|
|
|
|
fixed 960
|
|
|
|
|
fixed 1280
|
|
|
|
|
}
|
2023-12-27 21:51:42 +04:00
|
|
|
|
2024-09-05 23:37:10 +02:00
|
|
|
preset-window-heights {
|
|
|
|
|
proportion 0.25
|
|
|
|
|
proportion 0.5
|
|
|
|
|
fixed 960
|
|
|
|
|
fixed 1280
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
default-column-width { proportion 0.25; }
|
2023-09-26 13:44:37 +04:00
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
gaps 8
|
2023-10-01 17:42:56 +04:00
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
struts {
|
|
|
|
|
left 1
|
|
|
|
|
right 2
|
|
|
|
|
top 3
|
|
|
|
|
}
|
2024-01-08 17:17:19 +04:00
|
|
|
|
|
|
|
|
center-focused-column "on-overflow"
|
2024-07-15 15:51:48 +02:00
|
|
|
|
2025-02-01 10:46:52 +03:00
|
|
|
default-column-display "tabbed"
|
|
|
|
|
|
2024-07-15 15:51:48 +02:00
|
|
|
insert-hint {
|
|
|
|
|
color "rgb(255, 200, 127)"
|
2024-11-02 09:33:44 +03:00
|
|
|
gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
|
2024-07-15 15:51:48 +02:00
|
|
|
}
|
2023-10-05 09:25:07 +04:00
|
|
|
}
|
|
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
spawn-at-startup "alacritty" "-e" "fish"
|
2023-11-02 21:07:29 +04:00
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
prefer-no-csd
|
2023-10-07 17:45:55 +04:00
|
|
|
|
2024-01-06 13:04:21 +04:00
|
|
|
cursor {
|
|
|
|
|
xcursor-theme "breeze_cursors"
|
|
|
|
|
xcursor-size 16
|
2024-10-29 12:46:58 +03:00
|
|
|
hide-when-typing
|
2024-10-02 08:22:50 +08:00
|
|
|
hide-after-inactive-ms 3000
|
2023-12-21 08:37:30 +04:00
|
|
|
}
|
|
|
|
|
|
2023-10-31 14:23:54 +04:00
|
|
|
screenshot-path "~/Screenshots/screenshot.png"
|
2023-10-31 08:57:44 +04:00
|
|
|
|
2025-01-22 00:00:35 -05:00
|
|
|
clipboard {
|
|
|
|
|
disable-primary
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-20 08:31:05 +04:00
|
|
|
hotkey-overlay {
|
|
|
|
|
skip-at-startup
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 17:05:15 +04:00
|
|
|
animations {
|
|
|
|
|
slowdown 2.0
|
|
|
|
|
|
2024-03-05 13:32:30 +04:00
|
|
|
workspace-switch {
|
|
|
|
|
spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001
|
|
|
|
|
}
|
2024-02-07 17:05:15 +04:00
|
|
|
|
|
|
|
|
horizontal-view-movement {
|
|
|
|
|
duration-ms 100
|
|
|
|
|
curve "ease-out-expo"
|
|
|
|
|
}
|
2024-03-05 13:32:30 +04:00
|
|
|
|
|
|
|
|
window-open { off; }
|
2024-02-07 17:05:15 +04:00
|
|
|
}
|
|
|
|
|
|
2025-02-16 08:46:38 +03:00
|
|
|
gestures {
|
|
|
|
|
dnd-edge-view-scroll {
|
|
|
|
|
trigger-width 10
|
|
|
|
|
max-speed 50
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-24 10:08:56 +04:00
|
|
|
environment {
|
|
|
|
|
QT_QPA_PLATFORM "wayland"
|
|
|
|
|
DISPLAY null
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-13 17:46:37 +04:00
|
|
|
window-rule {
|
|
|
|
|
match app-id=".*alacritty"
|
|
|
|
|
exclude title="~"
|
2024-03-23 16:16:52 +04:00
|
|
|
exclude is-active=true is-focused=false
|
2024-02-13 17:46:37 +04:00
|
|
|
|
|
|
|
|
open-on-output "eDP-1"
|
2024-02-23 14:24:39 +04:00
|
|
|
open-maximized true
|
2024-02-24 08:44:21 +04:00
|
|
|
open-fullscreen false
|
2024-11-29 21:11:02 +03:00
|
|
|
open-floating false
|
2024-12-27 09:31:32 +03:00
|
|
|
open-focused true
|
2024-12-27 09:58:22 +03:00
|
|
|
default-window-height { fixed 500; }
|
2025-02-06 09:09:07 +03:00
|
|
|
default-column-display "tabbed"
|
2024-12-30 13:22:02 +03:00
|
|
|
default-floating-position x=100 y=-200 relative-to="bottom-left"
|
2024-04-24 21:49:07 +04:00
|
|
|
|
2024-04-24 22:17:53 +04:00
|
|
|
focus-ring {
|
|
|
|
|
off
|
|
|
|
|
width 3
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-24 21:49:07 +04:00
|
|
|
border {
|
|
|
|
|
on
|
2024-06-17 09:16:28 +03:00
|
|
|
width 8.5
|
2024-04-24 21:49:07 +04:00
|
|
|
}
|
2025-02-02 08:41:42 +03:00
|
|
|
|
|
|
|
|
tab-indicator {
|
|
|
|
|
active-color "#f00"
|
|
|
|
|
}
|
2024-02-13 17:46:37 +04:00
|
|
|
}
|
|
|
|
|
|
2024-11-14 11:33:08 +03:00
|
|
|
layer-rule {
|
|
|
|
|
match namespace="^notifications$"
|
|
|
|
|
block-out-from "screencast"
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-05 12:58:51 +04:00
|
|
|
binds {
|
2025-02-13 08:45:23 +03:00
|
|
|
Mod+Escape hotkey-overlay-title="Inhibit" { toggle-keyboard-shortcuts-inhibit; }
|
2025-01-18 15:26:42 +01:00
|
|
|
Mod+Shift+Escape allow-inhibiting=true { toggle-keyboard-shortcuts-inhibit; }
|
2024-04-19 10:49:46 +04:00
|
|
|
Mod+T allow-when-locked=true { spawn "alacritty"; }
|
2025-02-13 08:45:23 +03:00
|
|
|
Mod+Q hotkey-overlay-title=null { close-window; }
|
2023-09-05 12:58:51 +04:00
|
|
|
Mod+Shift+H { focus-monitor-left; }
|
2025-02-20 20:25:43 +01:00
|
|
|
Mod+Shift+O { focus-monitor "eDP-1"; }
|
2023-09-05 12:58:51 +04:00
|
|
|
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
2025-02-20 23:09:59 +01:00
|
|
|
Mod+Ctrl+Alt+O { move-window-to-monitor "eDP-1"; }
|
|
|
|
|
Mod+Ctrl+Alt+P { move-column-to-monitor "DP-1"; }
|
2023-09-05 12:58:51 +04:00
|
|
|
Mod+Comma { consume-window-into-column; }
|
2024-02-12 07:48:31 +04:00
|
|
|
Mod+1 { focus-workspace 1; }
|
2024-05-11 22:40:30 +02:00
|
|
|
Mod+Shift+1 { focus-workspace "workspace-1"; }
|
2025-01-18 15:26:42 +01:00
|
|
|
Mod+Shift+E allow-inhibiting=false { quit skip-confirmation=true; }
|
2024-03-23 08:49:58 +04:00
|
|
|
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
2023-09-06 15:49:46 +04:00
|
|
|
|
2024-10-18 16:00:40 +02:00
|
|
|
switch-events {
|
|
|
|
|
tablet-mode-on { spawn "bash" "-c" "gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true"; }
|
|
|
|
|
tablet-mode-off { spawn "bash" "-c" "gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false"; }
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-06 15:49:46 +04:00
|
|
|
debug {
|
2024-01-06 09:14:48 +04:00
|
|
|
render-drm-device "/dev/dri/renderD129"
|
2023-09-06 15:49:46 +04:00
|
|
|
}
|
2024-05-11 22:40:30 +02:00
|
|
|
|
|
|
|
|
workspace "workspace-1" {
|
|
|
|
|
open-on-output "eDP-1"
|
|
|
|
|
}
|
|
|
|
|
workspace "workspace-2"
|
|
|
|
|
workspace "workspace-3"
|
2024-02-21 21:27:44 +04:00
|
|
|
"##,
|
2025-02-16 08:46:38 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_debug_snapshot!(parsed, @r#"
|
2025-02-16 08:46:38 +03:00
|
|
|
Config {
|
|
|
|
|
input: Input {
|
|
|
|
|
keyboard: Keyboard {
|
|
|
|
|
xkb: Xkb {
|
|
|
|
|
rules: "",
|
|
|
|
|
model: "",
|
|
|
|
|
layout: "us,ru",
|
|
|
|
|
variant: "",
|
|
|
|
|
options: Some(
|
|
|
|
|
"grp:win_space_toggle",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
file: None,
|
|
|
|
|
},
|
|
|
|
|
repeat_delay: 600,
|
|
|
|
|
repeat_rate: 25,
|
|
|
|
|
track_layout: Window,
|
2025-04-20 11:30:40 +02:00
|
|
|
numlock: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
touchpad: Touchpad {
|
|
|
|
|
off: false,
|
|
|
|
|
tap: true,
|
|
|
|
|
dwt: true,
|
|
|
|
|
dwtp: true,
|
2025-03-01 23:01:34 -08:00
|
|
|
drag: Some(
|
|
|
|
|
true,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
drag_lock: false,
|
|
|
|
|
natural_scroll: false,
|
|
|
|
|
click_method: Some(
|
|
|
|
|
Clickfinger,
|
|
|
|
|
),
|
2025-06-09 16:13:31 +03:00
|
|
|
accel_speed: FloatOrInt(
|
|
|
|
|
0.2,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
accel_profile: Some(
|
|
|
|
|
Flat,
|
|
|
|
|
),
|
|
|
|
|
scroll_method: Some(
|
|
|
|
|
TwoFinger,
|
|
|
|
|
),
|
|
|
|
|
scroll_button: Some(
|
|
|
|
|
272,
|
|
|
|
|
),
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll_button_lock: true,
|
2025-02-16 08:46:38 +03:00
|
|
|
tap_button_map: Some(
|
|
|
|
|
LeftMiddleRight,
|
|
|
|
|
),
|
|
|
|
|
left_handed: false,
|
|
|
|
|
disabled_on_external_mouse: true,
|
|
|
|
|
middle_emulation: false,
|
|
|
|
|
scroll_factor: Some(
|
|
|
|
|
FloatOrInt(
|
|
|
|
|
0.9,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
mouse: Mouse {
|
|
|
|
|
off: false,
|
|
|
|
|
natural_scroll: true,
|
2025-06-09 16:13:31 +03:00
|
|
|
accel_speed: FloatOrInt(
|
|
|
|
|
0.4,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
accel_profile: Some(
|
|
|
|
|
Flat,
|
|
|
|
|
),
|
|
|
|
|
scroll_method: Some(
|
|
|
|
|
NoScroll,
|
|
|
|
|
),
|
|
|
|
|
scroll_button: Some(
|
|
|
|
|
273,
|
|
|
|
|
),
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll_button_lock: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
left_handed: false,
|
|
|
|
|
middle_emulation: true,
|
|
|
|
|
scroll_factor: Some(
|
|
|
|
|
FloatOrInt(
|
|
|
|
|
0.2,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
trackpoint: Trackpoint {
|
|
|
|
|
off: true,
|
|
|
|
|
natural_scroll: true,
|
2025-06-09 16:13:31 +03:00
|
|
|
accel_speed: FloatOrInt(
|
|
|
|
|
0.0,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
accel_profile: Some(
|
|
|
|
|
Flat,
|
|
|
|
|
),
|
|
|
|
|
scroll_method: Some(
|
|
|
|
|
OnButtonDown,
|
|
|
|
|
),
|
|
|
|
|
scroll_button: Some(
|
|
|
|
|
274,
|
|
|
|
|
),
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll_button_lock: false,
|
2025-03-13 09:20:42 +01:00
|
|
|
left_handed: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
middle_emulation: false,
|
|
|
|
|
},
|
|
|
|
|
trackball: Trackball {
|
|
|
|
|
off: true,
|
|
|
|
|
natural_scroll: true,
|
2025-06-09 16:13:31 +03:00
|
|
|
accel_speed: FloatOrInt(
|
|
|
|
|
0.0,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
accel_profile: Some(
|
|
|
|
|
Flat,
|
|
|
|
|
),
|
|
|
|
|
scroll_method: Some(
|
|
|
|
|
Edge,
|
|
|
|
|
),
|
|
|
|
|
scroll_button: Some(
|
|
|
|
|
275,
|
|
|
|
|
),
|
2025-06-19 02:22:01 +03:00
|
|
|
scroll_button_lock: true,
|
2025-02-16 08:46:38 +03:00
|
|
|
left_handed: true,
|
|
|
|
|
middle_emulation: true,
|
|
|
|
|
},
|
|
|
|
|
tablet: Tablet {
|
|
|
|
|
off: false,
|
|
|
|
|
calibration_matrix: Some(
|
|
|
|
|
[
|
|
|
|
|
1.0,
|
|
|
|
|
2.0,
|
|
|
|
|
3.0,
|
|
|
|
|
4.0,
|
|
|
|
|
5.0,
|
|
|
|
|
6.0,
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
map_to_output: Some(
|
|
|
|
|
"eDP-1",
|
|
|
|
|
),
|
|
|
|
|
left_handed: false,
|
|
|
|
|
},
|
|
|
|
|
touch: Touch {
|
2025-02-21 13:35:10 -05:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
map_to_output: Some(
|
|
|
|
|
"eDP-1",
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
disable_power_key_handling: true,
|
2025-03-13 14:55:16 +01:00
|
|
|
warp_mouse_to_focus: Some(
|
|
|
|
|
WarpMouseToFocus {
|
|
|
|
|
mode: None,
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
focus_follows_mouse: Some(
|
|
|
|
|
FocusFollowsMouse {
|
|
|
|
|
max_scroll_amount: None,
|
2023-09-05 12:58:51 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
workspace_auto_back_and_forth: true,
|
2025-02-05 09:34:25 -05:00
|
|
|
mod_key: Some(
|
|
|
|
|
IsoLevel3Shift,
|
|
|
|
|
),
|
|
|
|
|
mod_key_nested: Some(
|
|
|
|
|
Super,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
outputs: Outputs(
|
|
|
|
|
[
|
|
|
|
|
Output {
|
2024-05-23 16:01:54 +09:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
name: "eDP-1",
|
|
|
|
|
scale: Some(
|
2025-02-16 08:46:38 +03:00
|
|
|
FloatOrInt(
|
2025-02-16 08:46:38 +03:00
|
|
|
2.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
transform: Flipped90,
|
|
|
|
|
position: Some(
|
|
|
|
|
Position {
|
|
|
|
|
x: 10,
|
|
|
|
|
y: 20,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
mode: Some(
|
|
|
|
|
ConfiguredMode {
|
|
|
|
|
width: 1920,
|
|
|
|
|
height: 1080,
|
|
|
|
|
refresh: Some(
|
|
|
|
|
144.0,
|
|
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
variable_refresh_rate: Some(
|
|
|
|
|
Vrr {
|
|
|
|
|
on_demand: true,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-03-29 10:13:59 +00:00
|
|
|
focus_at_startup: true,
|
2025-05-06 17:12:07 +03:00
|
|
|
background_color: Some(
|
|
|
|
|
Color {
|
|
|
|
|
r: 0.09803922,
|
|
|
|
|
g: 0.09803922,
|
|
|
|
|
b: 0.4,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-04-28 07:53:03 +03:00
|
|
|
backdrop_color: None,
|
2024-02-14 16:24:46 +01:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
spawn_at_startup: [
|
|
|
|
|
SpawnAtStartup {
|
|
|
|
|
command: [
|
|
|
|
|
"alacritty",
|
|
|
|
|
"-e",
|
|
|
|
|
"fish",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
layout: Layout {
|
|
|
|
|
focus_ring: FocusRing {
|
|
|
|
|
off: false,
|
|
|
|
|
width: FloatOrInt(
|
|
|
|
|
5.0,
|
|
|
|
|
),
|
|
|
|
|
active_color: Color {
|
|
|
|
|
r: 0.0,
|
|
|
|
|
g: 0.39215687,
|
|
|
|
|
b: 0.78431374,
|
|
|
|
|
a: 1.0,
|
2024-10-16 16:51:56 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
inactive_color: Color {
|
|
|
|
|
r: 1.0,
|
|
|
|
|
g: 0.78431374,
|
|
|
|
|
b: 0.39215687,
|
|
|
|
|
a: 0.0,
|
|
|
|
|
},
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: Color {
|
|
|
|
|
r: 0.60784316,
|
|
|
|
|
g: 0.0,
|
|
|
|
|
b: 0.0,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: Some(
|
|
|
|
|
Gradient {
|
|
|
|
|
from: Color {
|
|
|
|
|
r: 0.039215688,
|
|
|
|
|
g: 0.078431375,
|
|
|
|
|
b: 0.11764706,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
to: Color {
|
|
|
|
|
r: 0.0,
|
|
|
|
|
g: 0.5019608,
|
|
|
|
|
b: 1.0,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
angle: 180,
|
|
|
|
|
relative_to: WorkspaceView,
|
|
|
|
|
in_: GradientInterpolation {
|
|
|
|
|
color_space: Srgb,
|
|
|
|
|
hue_interpolation: Shorter,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
border: Border {
|
|
|
|
|
off: false,
|
|
|
|
|
width: FloatOrInt(
|
|
|
|
|
3.0,
|
|
|
|
|
),
|
|
|
|
|
active_color: Color {
|
|
|
|
|
r: 1.0,
|
|
|
|
|
g: 0.78431374,
|
|
|
|
|
b: 0.49803922,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
inactive_color: Color {
|
|
|
|
|
r: 1.0,
|
|
|
|
|
g: 0.78431374,
|
|
|
|
|
b: 0.39215687,
|
|
|
|
|
a: 0.0,
|
|
|
|
|
},
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: Color {
|
|
|
|
|
r: 0.60784316,
|
|
|
|
|
g: 0.0,
|
|
|
|
|
b: 0.0,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
shadow: Shadow {
|
|
|
|
|
on: false,
|
|
|
|
|
offset: ShadowOffset {
|
|
|
|
|
x: FloatOrInt(
|
|
|
|
|
10.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
y: FloatOrInt(
|
|
|
|
|
-20.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2023-10-03 17:02:07 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
softness: FloatOrInt(
|
|
|
|
|
30.0,
|
|
|
|
|
),
|
|
|
|
|
spread: FloatOrInt(
|
|
|
|
|
5.0,
|
|
|
|
|
),
|
|
|
|
|
draw_behind_window: false,
|
|
|
|
|
color: Color {
|
|
|
|
|
r: 0.0,
|
|
|
|
|
g: 0.0,
|
|
|
|
|
b: 0.0,
|
|
|
|
|
a: 0.4392157,
|
|
|
|
|
},
|
|
|
|
|
inactive_color: None,
|
|
|
|
|
},
|
|
|
|
|
tab_indicator: TabIndicator {
|
|
|
|
|
off: false,
|
|
|
|
|
hide_when_single_tab: false,
|
|
|
|
|
place_within_column: false,
|
|
|
|
|
gap: FloatOrInt(
|
|
|
|
|
5.0,
|
|
|
|
|
),
|
|
|
|
|
width: FloatOrInt(
|
|
|
|
|
10.0,
|
|
|
|
|
),
|
|
|
|
|
length: TabIndicatorLength {
|
|
|
|
|
total_proportion: Some(
|
|
|
|
|
0.5,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-02-24 18:32:13 +01:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
position: Top,
|
|
|
|
|
gaps_between_tabs: FloatOrInt(
|
|
|
|
|
0.0,
|
|
|
|
|
),
|
|
|
|
|
corner_radius: FloatOrInt(
|
|
|
|
|
0.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
active_color: None,
|
|
|
|
|
inactive_color: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_color: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_gradient: None,
|
2023-09-05 12:58:51 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
insert_hint: InsertHint {
|
|
|
|
|
off: false,
|
|
|
|
|
color: Color {
|
|
|
|
|
r: 1.0,
|
|
|
|
|
g: 0.78431374,
|
|
|
|
|
b: 0.49803922,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
gradient: Some(
|
|
|
|
|
Gradient {
|
|
|
|
|
from: Color {
|
|
|
|
|
r: 0.039215688,
|
|
|
|
|
g: 0.078431375,
|
|
|
|
|
b: 0.11764706,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
|
|
|
|
to: Color {
|
|
|
|
|
r: 0.0,
|
|
|
|
|
g: 0.5019608,
|
|
|
|
|
b: 1.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
angle: 180,
|
|
|
|
|
relative_to: WorkspaceView,
|
|
|
|
|
in_: GradientInterpolation {
|
|
|
|
|
color_space: Srgb,
|
|
|
|
|
hue_interpolation: Shorter,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
preset_column_widths: [
|
|
|
|
|
Proportion(
|
|
|
|
|
0.25,
|
|
|
|
|
),
|
|
|
|
|
Proportion(
|
|
|
|
|
0.5,
|
|
|
|
|
),
|
|
|
|
|
Fixed(
|
|
|
|
|
960,
|
|
|
|
|
),
|
|
|
|
|
Fixed(
|
|
|
|
|
1280,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
default_column_width: Some(
|
|
|
|
|
DefaultPresetSize(
|
|
|
|
|
Some(
|
|
|
|
|
Proportion(
|
|
|
|
|
0.25,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
preset_window_heights: [
|
|
|
|
|
Proportion(
|
|
|
|
|
0.25,
|
|
|
|
|
),
|
|
|
|
|
Proportion(
|
|
|
|
|
0.5,
|
|
|
|
|
),
|
|
|
|
|
Fixed(
|
|
|
|
|
960,
|
|
|
|
|
),
|
|
|
|
|
Fixed(
|
|
|
|
|
1280,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
],
|
2025-02-16 08:46:38 +03:00
|
|
|
center_focused_column: OnOverflow,
|
|
|
|
|
always_center_single_column: false,
|
|
|
|
|
empty_workspace_above_first: false,
|
|
|
|
|
default_column_display: Tabbed,
|
|
|
|
|
gaps: FloatOrInt(
|
|
|
|
|
8.0,
|
|
|
|
|
),
|
|
|
|
|
struts: Struts {
|
|
|
|
|
left: FloatOrInt(
|
|
|
|
|
1.0,
|
|
|
|
|
),
|
|
|
|
|
right: FloatOrInt(
|
|
|
|
|
2.0,
|
|
|
|
|
),
|
|
|
|
|
top: FloatOrInt(
|
|
|
|
|
3.0,
|
|
|
|
|
),
|
|
|
|
|
bottom: FloatOrInt(
|
|
|
|
|
0.0,
|
|
|
|
|
),
|
|
|
|
|
},
|
2025-05-06 17:12:07 +03:00
|
|
|
background_color: Color {
|
|
|
|
|
r: 0.25,
|
|
|
|
|
g: 0.25,
|
|
|
|
|
b: 0.25,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
prefer_no_csd: true,
|
|
|
|
|
cursor: Cursor {
|
|
|
|
|
xcursor_theme: "breeze_cursors",
|
|
|
|
|
xcursor_size: 16,
|
|
|
|
|
hide_when_typing: true,
|
|
|
|
|
hide_after_inactive_ms: Some(
|
|
|
|
|
3000,
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
screenshot_path: Some(
|
|
|
|
|
"~/Screenshots/screenshot.png",
|
|
|
|
|
),
|
|
|
|
|
clipboard: Clipboard {
|
|
|
|
|
disable_primary: true,
|
|
|
|
|
},
|
|
|
|
|
hotkey_overlay: HotkeyOverlay {
|
|
|
|
|
skip_at_startup: true,
|
2025-06-03 19:31:18 +02:00
|
|
|
hide_not_bound: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
animations: Animations {
|
|
|
|
|
off: false,
|
2025-06-09 16:13:31 +03:00
|
|
|
slowdown: FloatOrInt(
|
|
|
|
|
2.0,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
workspace_switch: WorkspaceSwitchAnim(
|
|
|
|
|
Animation {
|
2024-01-06 13:04:21 +04:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
kind: Spring(
|
|
|
|
|
SpringParams {
|
|
|
|
|
damping_ratio: 1.0,
|
|
|
|
|
stiffness: 1000,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
window_open: WindowOpenAnim {
|
|
|
|
|
anim: Animation {
|
|
|
|
|
off: true,
|
|
|
|
|
kind: Easing(
|
|
|
|
|
EasingParams {
|
|
|
|
|
duration_ms: 150,
|
|
|
|
|
curve: EaseOutExpo,
|
2024-07-16 10:22:03 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2023-09-26 13:09:33 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
custom_shader: None,
|
|
|
|
|
},
|
|
|
|
|
window_close: WindowCloseAnim {
|
|
|
|
|
anim: Animation {
|
2024-01-06 13:04:21 +04:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
kind: Easing(
|
|
|
|
|
EasingParams {
|
|
|
|
|
duration_ms: 150,
|
|
|
|
|
curve: EaseOutQuad,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2023-12-27 21:51:42 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
custom_shader: None,
|
|
|
|
|
},
|
|
|
|
|
horizontal_view_movement: HorizontalViewMovementAnim(
|
|
|
|
|
Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: Easing(
|
|
|
|
|
EasingParams {
|
|
|
|
|
duration_ms: 100,
|
|
|
|
|
curve: EaseOutExpo,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-01-15 14:16:05 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
window_movement: WindowMovementAnim(
|
|
|
|
|
Animation {
|
2025-02-16 08:46:38 +03:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
kind: Spring(
|
|
|
|
|
SpringParams {
|
|
|
|
|
damping_ratio: 1.0,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-02 08:41:42 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
window_resize: WindowResizeAnim {
|
|
|
|
|
anim: Animation {
|
2024-07-15 15:51:48 +02:00
|
|
|
off: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
kind: Spring(
|
|
|
|
|
SpringParams {
|
|
|
|
|
damping_ratio: 1.0,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
2024-11-02 09:33:44 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-07-15 15:51:48 +02:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
custom_shader: None,
|
|
|
|
|
},
|
|
|
|
|
config_notification_open_close: ConfigNotificationOpenCloseAnim(
|
|
|
|
|
Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: Spring(
|
|
|
|
|
SpringParams {
|
|
|
|
|
damping_ratio: 0.6,
|
|
|
|
|
stiffness: 1000,
|
|
|
|
|
epsilon: 0.001,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
screenshot_ui_open: ScreenshotUiOpenAnim(
|
|
|
|
|
Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: Easing(
|
|
|
|
|
EasingParams {
|
|
|
|
|
duration_ms: 200,
|
|
|
|
|
curve: EaseOutQuad,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
),
|
2025-04-25 09:36:50 +03:00
|
|
|
overview_open_close: OverviewOpenCloseAnim(
|
|
|
|
|
Animation {
|
|
|
|
|
off: false,
|
|
|
|
|
kind: Spring(
|
|
|
|
|
SpringParams {
|
|
|
|
|
damping_ratio: 1.0,
|
|
|
|
|
stiffness: 800,
|
|
|
|
|
epsilon: 0.0001,
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
gestures: Gestures {
|
|
|
|
|
dnd_edge_view_scroll: DndEdgeViewScroll {
|
|
|
|
|
trigger_width: FloatOrInt(
|
|
|
|
|
10.0,
|
|
|
|
|
),
|
2025-02-19 07:49:09 +03:00
|
|
|
delay_ms: 100,
|
2025-02-16 08:46:38 +03:00
|
|
|
max_speed: FloatOrInt(
|
|
|
|
|
50.0,
|
|
|
|
|
),
|
|
|
|
|
},
|
2025-04-25 10:02:31 +03:00
|
|
|
dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch {
|
|
|
|
|
trigger_height: FloatOrInt(
|
|
|
|
|
50.0,
|
|
|
|
|
),
|
|
|
|
|
delay_ms: 100,
|
|
|
|
|
max_speed: FloatOrInt(
|
|
|
|
|
1500.0,
|
|
|
|
|
),
|
|
|
|
|
},
|
2025-04-25 10:08:26 +03:00
|
|
|
hot_corners: HotCorners {
|
|
|
|
|
off: false,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
2025-04-25 09:36:50 +03:00
|
|
|
overview: Overview {
|
|
|
|
|
zoom: FloatOrInt(
|
|
|
|
|
0.5,
|
|
|
|
|
),
|
2025-04-28 07:53:03 +03:00
|
|
|
backdrop_color: Color {
|
|
|
|
|
r: 0.15,
|
|
|
|
|
g: 0.15,
|
|
|
|
|
b: 0.15,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-05-01 09:36:10 +03:00
|
|
|
workspace_shadow: WorkspaceShadow {
|
|
|
|
|
off: false,
|
|
|
|
|
offset: ShadowOffset {
|
|
|
|
|
x: FloatOrInt(
|
|
|
|
|
0.0,
|
|
|
|
|
),
|
|
|
|
|
y: FloatOrInt(
|
2025-05-01 10:30:50 +03:00
|
|
|
10.0,
|
2025-05-01 09:36:10 +03:00
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
softness: FloatOrInt(
|
2025-05-01 10:53:35 +03:00
|
|
|
40.0,
|
2025-05-01 09:36:10 +03:00
|
|
|
),
|
|
|
|
|
spread: FloatOrInt(
|
2025-05-01 10:30:50 +03:00
|
|
|
10.0,
|
2025-05-01 09:36:10 +03:00
|
|
|
),
|
|
|
|
|
color: Color {
|
|
|
|
|
r: 0.0,
|
|
|
|
|
g: 0.0,
|
|
|
|
|
b: 0.0,
|
2025-05-01 10:53:35 +03:00
|
|
|
a: 0.3137255,
|
2025-05-01 09:36:10 +03:00
|
|
|
},
|
|
|
|
|
},
|
2025-04-25 09:36:50 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
environment: Environment(
|
|
|
|
|
[
|
|
|
|
|
EnvironmentVariable {
|
|
|
|
|
name: "QT_QPA_PLATFORM",
|
|
|
|
|
value: Some(
|
|
|
|
|
"wayland",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
EnvironmentVariable {
|
|
|
|
|
name: "DISPLAY",
|
|
|
|
|
value: None,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
),
|
2025-06-04 08:26:51 +03:00
|
|
|
xwayland_satellite: XwaylandSatellite {
|
|
|
|
|
off: false,
|
|
|
|
|
path: "xwayland-satellite",
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
window_rules: [
|
|
|
|
|
WindowRule {
|
|
|
|
|
matches: [
|
|
|
|
|
Match {
|
|
|
|
|
app_id: Some(
|
|
|
|
|
RegexEq(
|
|
|
|
|
Regex(
|
|
|
|
|
".*alacritty",
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
title: None,
|
|
|
|
|
is_active: None,
|
|
|
|
|
is_focused: None,
|
|
|
|
|
is_active_in_column: None,
|
|
|
|
|
is_floating: None,
|
|
|
|
|
is_window_cast_target: None,
|
2025-05-10 22:48:11 +03:00
|
|
|
is_urgent: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
at_startup: None,
|
|
|
|
|
},
|
2024-01-06 13:04:21 +04:00
|
|
|
],
|
2025-02-16 08:46:38 +03:00
|
|
|
excludes: [
|
|
|
|
|
Match {
|
|
|
|
|
app_id: None,
|
|
|
|
|
title: Some(
|
|
|
|
|
RegexEq(
|
|
|
|
|
Regex(
|
|
|
|
|
"~",
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
is_active: None,
|
|
|
|
|
is_focused: None,
|
|
|
|
|
is_active_in_column: None,
|
|
|
|
|
is_floating: None,
|
|
|
|
|
is_window_cast_target: None,
|
2025-05-10 22:48:11 +03:00
|
|
|
is_urgent: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
at_startup: None,
|
|
|
|
|
},
|
|
|
|
|
Match {
|
|
|
|
|
app_id: None,
|
|
|
|
|
title: None,
|
|
|
|
|
is_active: Some(
|
|
|
|
|
true,
|
|
|
|
|
),
|
|
|
|
|
is_focused: Some(
|
|
|
|
|
false,
|
|
|
|
|
),
|
|
|
|
|
is_active_in_column: None,
|
|
|
|
|
is_floating: None,
|
|
|
|
|
is_window_cast_target: None,
|
2025-05-10 22:48:11 +03:00
|
|
|
is_urgent: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
at_startup: None,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
default_column_width: None,
|
|
|
|
|
default_window_height: Some(
|
2025-02-16 08:46:38 +03:00
|
|
|
DefaultPresetSize(
|
|
|
|
|
Some(
|
2025-02-16 08:46:38 +03:00
|
|
|
Fixed(
|
|
|
|
|
500,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
open_on_output: Some(
|
|
|
|
|
"eDP-1",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
open_on_workspace: None,
|
|
|
|
|
open_maximized: Some(
|
|
|
|
|
true,
|
|
|
|
|
),
|
|
|
|
|
open_fullscreen: Some(
|
|
|
|
|
false,
|
|
|
|
|
),
|
|
|
|
|
open_floating: Some(
|
|
|
|
|
false,
|
|
|
|
|
),
|
|
|
|
|
open_focused: Some(
|
|
|
|
|
true,
|
|
|
|
|
),
|
|
|
|
|
min_width: None,
|
|
|
|
|
min_height: None,
|
|
|
|
|
max_width: None,
|
|
|
|
|
max_height: None,
|
|
|
|
|
focus_ring: BorderRule {
|
|
|
|
|
off: true,
|
|
|
|
|
on: false,
|
|
|
|
|
width: Some(
|
|
|
|
|
FloatOrInt(
|
|
|
|
|
3.0,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
active_color: None,
|
|
|
|
|
inactive_color: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
border: BorderRule {
|
|
|
|
|
off: false,
|
|
|
|
|
on: true,
|
|
|
|
|
width: Some(
|
|
|
|
|
FloatOrInt(
|
|
|
|
|
8.5,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
active_color: None,
|
|
|
|
|
inactive_color: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
shadow: ShadowRule {
|
|
|
|
|
off: false,
|
|
|
|
|
on: false,
|
|
|
|
|
offset: None,
|
|
|
|
|
softness: None,
|
|
|
|
|
spread: None,
|
|
|
|
|
draw_behind_window: None,
|
|
|
|
|
color: None,
|
|
|
|
|
inactive_color: None,
|
|
|
|
|
},
|
|
|
|
|
tab_indicator: TabIndicatorRule {
|
|
|
|
|
active_color: Some(
|
|
|
|
|
Color {
|
|
|
|
|
r: 1.0,
|
|
|
|
|
g: 0.0,
|
|
|
|
|
b: 0.0,
|
|
|
|
|
a: 1.0,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
inactive_color: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_color: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-05-10 22:34:53 +03:00
|
|
|
urgent_gradient: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
draw_border_with_background: None,
|
|
|
|
|
opacity: None,
|
|
|
|
|
geometry_corner_radius: None,
|
|
|
|
|
clip_to_geometry: None,
|
|
|
|
|
baba_is_float: None,
|
|
|
|
|
block_out_from: None,
|
|
|
|
|
variable_refresh_rate: None,
|
|
|
|
|
default_column_display: Some(
|
|
|
|
|
Tabbed,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
default_floating_position: Some(
|
|
|
|
|
FloatingPosition {
|
|
|
|
|
x: FloatOrInt(
|
|
|
|
|
100.0,
|
|
|
|
|
),
|
|
|
|
|
y: FloatOrInt(
|
|
|
|
|
-200.0,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
relative_to: BottomLeft,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
scroll_factor: None,
|
2025-03-13 11:32:54 +03:00
|
|
|
tiled_state: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
layer_rules: [
|
|
|
|
|
LayerRule {
|
|
|
|
|
matches: [
|
|
|
|
|
Match {
|
|
|
|
|
namespace: Some(
|
|
|
|
|
RegexEq(
|
|
|
|
|
Regex(
|
|
|
|
|
"^notifications$",
|
|
|
|
|
),
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
at_startup: None,
|
2024-05-15 19:38:29 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
],
|
|
|
|
|
excludes: [],
|
|
|
|
|
opacity: None,
|
|
|
|
|
block_out_from: Some(
|
|
|
|
|
Screencast,
|
|
|
|
|
),
|
|
|
|
|
shadow: ShadowRule {
|
|
|
|
|
off: false,
|
|
|
|
|
on: false,
|
|
|
|
|
offset: None,
|
|
|
|
|
softness: None,
|
|
|
|
|
spread: None,
|
|
|
|
|
draw_behind_window: None,
|
|
|
|
|
color: None,
|
|
|
|
|
inactive_color: None,
|
2024-05-15 19:38:29 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
geometry_corner_radius: None,
|
2025-05-06 16:51:18 +03:00
|
|
|
place_within_backdrop: None,
|
2025-05-12 08:16:01 +03:00
|
|
|
baba_is_float: None,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
binds: Binds(
|
|
|
|
|
[
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_Escape,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: ToggleKeyboardShortcutsInhibit,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: false,
|
|
|
|
|
hotkey_overlay_title: Some(
|
|
|
|
|
Some(
|
|
|
|
|
"Inhibit",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_Escape,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
SHIFT | COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: ToggleKeyboardShortcutsInhibit,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: false,
|
|
|
|
|
hotkey_overlay_title: None,
|
2024-02-24 10:08:56 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_t,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: Spawn(
|
|
|
|
|
[
|
|
|
|
|
"alacritty",
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: true,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_q,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
action: CloseWindow,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: Some(
|
|
|
|
|
None,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_h,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
SHIFT | COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: FocusMonitorLeft,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
2025-02-20 20:25:43 +01:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_o,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
SHIFT | COMPOSITOR,
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
action: FocusMonitor(
|
|
|
|
|
"eDP-1",
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_l,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
CTRL | SHIFT | COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: MoveWindowToMonitorRight,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
2025-02-02 08:41:42 +03:00
|
|
|
},
|
2025-02-20 23:09:59 +01:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_o,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
CTRL | ALT | COMPOSITOR,
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
action: MoveWindowToMonitor(
|
|
|
|
|
"eDP-1",
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_p,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
CTRL | ALT | COMPOSITOR,
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
action: MoveColumnToMonitor(
|
|
|
|
|
"DP-1",
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_comma,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
|
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: ConsumeWindowIntoColumn,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
2024-05-11 22:40:30 +02:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_1,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2023-09-05 12:58:51 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: FocusWorkspace(
|
|
|
|
|
Index(
|
|
|
|
|
1,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_1,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
modifiers: Modifiers(
|
|
|
|
|
SHIFT | COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-05-11 22:40:30 +02:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: FocusWorkspace(
|
|
|
|
|
Name(
|
|
|
|
|
"workspace-1",
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: Keysym(
|
|
|
|
|
XK_e,
|
|
|
|
|
),
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
SHIFT | COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-02-12 07:53:06 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: Quit(
|
|
|
|
|
true,
|
|
|
|
|
),
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: None,
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: false,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
Bind {
|
|
|
|
|
key: Key {
|
|
|
|
|
trigger: WheelScrollDown,
|
|
|
|
|
modifiers: Modifiers(
|
|
|
|
|
COMPOSITOR,
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-03-22 10:36:19 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
action: FocusWorkspaceDown,
|
|
|
|
|
repeat: true,
|
|
|
|
|
cooldown: Some(
|
|
|
|
|
150ms,
|
|
|
|
|
),
|
|
|
|
|
allow_when_locked: false,
|
|
|
|
|
allow_inhibiting: true,
|
|
|
|
|
hotkey_overlay_title: None,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
switch_events: SwitchBinds {
|
|
|
|
|
lid_open: None,
|
|
|
|
|
lid_close: None,
|
|
|
|
|
tablet_mode_on: Some(
|
|
|
|
|
SwitchAction {
|
|
|
|
|
spawn: [
|
|
|
|
|
"bash",
|
|
|
|
|
"-c",
|
|
|
|
|
"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled true",
|
|
|
|
|
],
|
|
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
tablet_mode_off: Some(
|
|
|
|
|
SwitchAction {
|
|
|
|
|
spawn: [
|
|
|
|
|
"bash",
|
|
|
|
|
"-c",
|
|
|
|
|
"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false",
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
},
|
|
|
|
|
debug: DebugConfig {
|
|
|
|
|
preview_render: None,
|
|
|
|
|
dbus_interfaces_in_non_session_instances: false,
|
|
|
|
|
wait_for_frame_completion_before_queueing: false,
|
|
|
|
|
enable_overlay_planes: false,
|
|
|
|
|
disable_cursor_plane: false,
|
|
|
|
|
disable_direct_scanout: false,
|
2025-08-07 14:06:11 -04:00
|
|
|
keep_max_bpc_unchanged: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
restrict_primary_scanout_to_matching_format: false,
|
|
|
|
|
render_drm_device: Some(
|
|
|
|
|
"/dev/dri/renderD129",
|
|
|
|
|
),
|
|
|
|
|
force_pipewire_invalid_modifier: false,
|
|
|
|
|
emulate_zero_presentation_time: false,
|
|
|
|
|
disable_resize_throttling: false,
|
|
|
|
|
disable_transactions: false,
|
|
|
|
|
keep_laptop_panel_on_when_lid_is_closed: false,
|
|
|
|
|
disable_monitor_names: false,
|
|
|
|
|
strict_new_window_focus_policy: false,
|
2025-02-26 19:24:37 +03:00
|
|
|
honor_xdg_activation_with_invalid_serial: false,
|
2025-06-11 09:05:14 +03:00
|
|
|
deactivate_unfocused_windows: false,
|
2025-06-04 01:56:21 +10:00
|
|
|
skip_cursor_only_updates_during_vrr: false,
|
2025-02-16 08:46:38 +03:00
|
|
|
},
|
|
|
|
|
workspaces: [
|
|
|
|
|
Workspace {
|
|
|
|
|
name: WorkspaceName(
|
|
|
|
|
"workspace-1",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
open_on_output: Some(
|
|
|
|
|
"eDP-1",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2024-10-18 16:00:40 +02:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Workspace {
|
|
|
|
|
name: WorkspaceName(
|
|
|
|
|
"workspace-2",
|
2025-02-16 08:46:38 +03:00
|
|
|
),
|
2025-02-16 08:46:38 +03:00
|
|
|
open_on_output: None,
|
2023-09-06 15:49:46 +04:00
|
|
|
},
|
2025-02-16 08:46:38 +03:00
|
|
|
Workspace {
|
|
|
|
|
name: WorkspaceName(
|
|
|
|
|
"workspace-3",
|
|
|
|
|
),
|
|
|
|
|
open_on_output: None,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
2025-02-16 08:46:38 +03:00
|
|
|
"#);
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn can_create_default_config() {
|
|
|
|
|
let _ = Config::default();
|
|
|
|
|
}
|
2023-10-03 08:35:24 +04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_mode() {
|
|
|
|
|
assert_eq!(
|
2024-05-05 10:19:47 +04:00
|
|
|
"2560x1600@165.004".parse::<ConfiguredMode>().unwrap(),
|
|
|
|
|
ConfiguredMode {
|
2023-10-03 08:35:24 +04:00
|
|
|
width: 2560,
|
|
|
|
|
height: 1600,
|
|
|
|
|
refresh: Some(165.004),
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
2024-05-05 10:19:47 +04:00
|
|
|
"1920x1080".parse::<ConfiguredMode>().unwrap(),
|
|
|
|
|
ConfiguredMode {
|
2023-10-03 08:35:24 +04:00
|
|
|
width: 1920,
|
|
|
|
|
height: 1080,
|
|
|
|
|
refresh: None,
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
|
2024-05-05 10:19:47 +04:00
|
|
|
assert!("1920".parse::<ConfiguredMode>().is_err());
|
|
|
|
|
assert!("1920x".parse::<ConfiguredMode>().is_err());
|
|
|
|
|
assert!("1920x1080@".parse::<ConfiguredMode>().is_err());
|
|
|
|
|
assert!("1920x1080@60Hz".parse::<ConfiguredMode>().is_err());
|
2023-10-03 08:35:24 +04:00
|
|
|
}
|
2023-10-03 11:38:42 +04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn parse_size_change() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"10".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::SetFixed(10),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"+10".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::AdjustFixed(10),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"-10".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::AdjustFixed(-10),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"10%".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::SetProportion(10.),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"+10%".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::AdjustProportion(10.),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"-10%".parse::<SizeChange>().unwrap(),
|
|
|
|
|
SizeChange::AdjustProportion(-10.),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert!("-".parse::<SizeChange>().is_err());
|
|
|
|
|
assert!("10% ".parse::<SizeChange>().is_err());
|
|
|
|
|
}
|
2024-03-22 22:09:25 +01:00
|
|
|
|
2024-12-28 11:40:16 +03:00
|
|
|
#[test]
|
|
|
|
|
fn parse_position_change() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"10".parse::<PositionChange>().unwrap(),
|
|
|
|
|
PositionChange::SetFixed(10.),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"+10".parse::<PositionChange>().unwrap(),
|
|
|
|
|
PositionChange::AdjustFixed(10.),
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"-10".parse::<PositionChange>().unwrap(),
|
|
|
|
|
PositionChange::AdjustFixed(-10.),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert!("10%".parse::<PositionChange>().is_err());
|
|
|
|
|
assert!("+10%".parse::<PositionChange>().is_err());
|
|
|
|
|
assert!("-10%".parse::<PositionChange>().is_err());
|
|
|
|
|
assert!("-".parse::<PositionChange>().is_err());
|
|
|
|
|
assert!("10% ".parse::<PositionChange>().is_err());
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-16 10:22:03 +03:00
|
|
|
#[test]
|
|
|
|
|
fn parse_gradient_interpolation() {
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"srgb".parse::<GradientInterpolation>().unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Srgb,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"srgb-linear".parse::<GradientInterpolation>().unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::SrgbLinear,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklab".parse::<GradientInterpolation>().unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklab,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklch".parse::<GradientInterpolation>().unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklch,
|
|
|
|
|
..Default::default()
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklch shorter hue"
|
|
|
|
|
.parse::<GradientInterpolation>()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklch,
|
|
|
|
|
hue_interpolation: HueInterpolation::Shorter,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklch longer hue".parse::<GradientInterpolation>().unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklch,
|
|
|
|
|
hue_interpolation: HueInterpolation::Longer,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklch decreasing hue"
|
|
|
|
|
.parse::<GradientInterpolation>()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklch,
|
|
|
|
|
hue_interpolation: HueInterpolation::Decreasing,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"oklch increasing hue"
|
|
|
|
|
.parse::<GradientInterpolation>()
|
|
|
|
|
.unwrap(),
|
|
|
|
|
GradientInterpolation {
|
|
|
|
|
color_space: GradientColorSpace::Oklch,
|
|
|
|
|
hue_interpolation: HueInterpolation::Increasing,
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
assert!("".parse::<GradientInterpolation>().is_err());
|
|
|
|
|
assert!("srgb shorter hue".parse::<GradientInterpolation>().is_err());
|
|
|
|
|
assert!("oklch shorter".parse::<GradientInterpolation>().is_err());
|
|
|
|
|
assert!("oklch shorter h".parse::<GradientInterpolation>().is_err());
|
|
|
|
|
assert!("oklch a hue".parse::<GradientInterpolation>().is_err());
|
|
|
|
|
assert!("oklch shorter hue a"
|
|
|
|
|
.parse::<GradientInterpolation>()
|
|
|
|
|
.is_err());
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-22 22:09:25 +01:00
|
|
|
#[test]
|
2024-07-31 11:00:35 -04:00
|
|
|
fn parse_iso_level_shifts() {
|
2024-03-22 22:09:25 +01:00
|
|
|
assert_eq!(
|
|
|
|
|
"ISO_Level3_Shift+A".parse::<Key>().unwrap(),
|
|
|
|
|
Key {
|
|
|
|
|
trigger: Trigger::Keysym(Keysym::a),
|
|
|
|
|
modifiers: Modifiers::ISO_LEVEL3_SHIFT
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"Mod5+A".parse::<Key>().unwrap(),
|
|
|
|
|
Key {
|
|
|
|
|
trigger: Trigger::Keysym(Keysym::a),
|
|
|
|
|
modifiers: Modifiers::ISO_LEVEL3_SHIFT
|
|
|
|
|
},
|
|
|
|
|
);
|
2024-07-31 11:00:35 -04:00
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"ISO_Level5_Shift+A".parse::<Key>().unwrap(),
|
|
|
|
|
Key {
|
|
|
|
|
trigger: Trigger::Keysym(Keysym::a),
|
|
|
|
|
modifiers: Modifiers::ISO_LEVEL5_SHIFT
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
"Mod3+A".parse::<Key>().unwrap(),
|
|
|
|
|
Key {
|
|
|
|
|
trigger: Trigger::Keysym(Keysym::a),
|
|
|
|
|
modifiers: Modifiers::ISO_LEVEL5_SHIFT
|
|
|
|
|
},
|
|
|
|
|
);
|
2024-03-22 22:09:25 +01:00
|
|
|
}
|
2024-05-19 17:55:54 +04:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn default_repeat_params() {
|
|
|
|
|
let config = Config::parse("config.kdl", "").unwrap();
|
|
|
|
|
assert_eq!(config.input.keyboard.repeat_delay, 600);
|
|
|
|
|
assert_eq!(config.input.keyboard.repeat_rate, 25);
|
|
|
|
|
}
|
2024-09-03 12:13:04 +03:00
|
|
|
|
2024-09-05 20:10:01 +03:00
|
|
|
fn make_output_name(
|
|
|
|
|
connector: &str,
|
|
|
|
|
make: Option<&str>,
|
|
|
|
|
model: Option<&str>,
|
|
|
|
|
serial: Option<&str>,
|
|
|
|
|
) -> OutputName {
|
|
|
|
|
OutputName {
|
|
|
|
|
connector: connector.to_string(),
|
|
|
|
|
make: make.map(|x| x.to_string()),
|
|
|
|
|
model: model.map(|x| x.to_string()),
|
|
|
|
|
serial: serial.map(|x| x.to_string()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-03 12:13:04 +03:00
|
|
|
#[test]
|
|
|
|
|
fn test_output_name_match() {
|
|
|
|
|
fn check(
|
|
|
|
|
target: &str,
|
|
|
|
|
connector: &str,
|
|
|
|
|
make: Option<&str>,
|
|
|
|
|
model: Option<&str>,
|
|
|
|
|
serial: Option<&str>,
|
|
|
|
|
) -> bool {
|
2024-09-05 20:10:01 +03:00
|
|
|
let name = make_output_name(connector, make, model, serial);
|
2024-09-03 12:13:04 +03:00
|
|
|
name.matches(target)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert!(check("dp-2", "DP-2", None, None, None));
|
|
|
|
|
assert!(!check("dp-1", "DP-2", None, None, None));
|
|
|
|
|
assert!(check("dp-2", "DP-2", Some("a"), Some("b"), Some("c")));
|
|
|
|
|
assert!(check(
|
|
|
|
|
"some company some monitor 1234",
|
|
|
|
|
"DP-2",
|
|
|
|
|
Some("Some Company"),
|
|
|
|
|
Some("Some Monitor"),
|
|
|
|
|
Some("1234")
|
|
|
|
|
));
|
|
|
|
|
assert!(!check(
|
|
|
|
|
"some other company some monitor 1234",
|
|
|
|
|
"DP-2",
|
|
|
|
|
Some("Some Company"),
|
|
|
|
|
Some("Some Monitor"),
|
|
|
|
|
Some("1234")
|
|
|
|
|
));
|
|
|
|
|
assert!(!check(
|
|
|
|
|
"make model serial ",
|
|
|
|
|
"DP-2",
|
|
|
|
|
Some("make"),
|
|
|
|
|
Some("model"),
|
|
|
|
|
Some("serial")
|
|
|
|
|
));
|
|
|
|
|
assert!(check(
|
|
|
|
|
"make serial",
|
|
|
|
|
"DP-2",
|
|
|
|
|
Some("make"),
|
|
|
|
|
Some(""),
|
|
|
|
|
Some("serial")
|
|
|
|
|
));
|
|
|
|
|
assert!(check(
|
|
|
|
|
"make model unknown",
|
|
|
|
|
"DP-2",
|
|
|
|
|
Some("Make"),
|
|
|
|
|
Some("Model"),
|
|
|
|
|
None
|
|
|
|
|
));
|
|
|
|
|
assert!(check(
|
|
|
|
|
"unknown unknown serial",
|
|
|
|
|
"DP-2",
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
Some("Serial")
|
|
|
|
|
));
|
|
|
|
|
assert!(!check("unknown unknown unknown", "DP-2", None, None, None));
|
|
|
|
|
}
|
2024-09-05 20:10:01 +03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_output_name_sorting() {
|
|
|
|
|
let mut names = vec![
|
|
|
|
|
make_output_name("DP-2", None, None, None),
|
|
|
|
|
make_output_name("DP-1", None, None, None),
|
|
|
|
|
make_output_name("DP-3", Some("B"), Some("A"), Some("A")),
|
|
|
|
|
make_output_name("DP-3", Some("A"), Some("B"), Some("A")),
|
|
|
|
|
make_output_name("DP-3", Some("A"), Some("A"), Some("B")),
|
|
|
|
|
make_output_name("DP-3", None, Some("A"), Some("A")),
|
|
|
|
|
make_output_name("DP-3", Some("A"), None, Some("A")),
|
|
|
|
|
make_output_name("DP-3", Some("A"), Some("A"), None),
|
|
|
|
|
make_output_name("DP-5", Some("A"), Some("A"), Some("A")),
|
|
|
|
|
make_output_name("DP-4", Some("A"), Some("A"), Some("A")),
|
|
|
|
|
];
|
|
|
|
|
names.sort_by(|a, b| a.compare(b));
|
|
|
|
|
let names = names
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|name| {
|
|
|
|
|
format!(
|
|
|
|
|
"{} | {}",
|
|
|
|
|
name.format_make_model_serial_or_connector(),
|
|
|
|
|
name.connector,
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
2024-12-20 08:49:18 +03:00
|
|
|
assert_debug_snapshot!(
|
2024-09-05 20:10:01 +03:00
|
|
|
names,
|
2024-12-20 08:49:18 +03:00
|
|
|
@r#"
|
2025-01-09 10:21:53 +03:00
|
|
|
[
|
|
|
|
|
"Unknown A A | DP-3",
|
|
|
|
|
"A Unknown A | DP-3",
|
|
|
|
|
"A A Unknown | DP-3",
|
|
|
|
|
"A A A | DP-4",
|
|
|
|
|
"A A A | DP-5",
|
|
|
|
|
"A A B | DP-3",
|
|
|
|
|
"A B A | DP-3",
|
|
|
|
|
"B A A | DP-3",
|
|
|
|
|
"DP-1 | DP-1",
|
|
|
|
|
"DP-2 | DP-2",
|
|
|
|
|
]
|
|
|
|
|
"#
|
2024-09-05 20:10:01 +03:00
|
|
|
);
|
|
|
|
|
}
|
2024-12-27 15:39:37 +03:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_border_rule_on_off_merging() {
|
|
|
|
|
fn is_on(config: &str, rules: &[&str]) -> String {
|
|
|
|
|
let mut resolved = BorderRule {
|
|
|
|
|
off: false,
|
|
|
|
|
on: false,
|
|
|
|
|
width: None,
|
|
|
|
|
active_color: None,
|
|
|
|
|
inactive_color: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_color: None,
|
2024-12-27 15:39:37 +03:00
|
|
|
active_gradient: None,
|
|
|
|
|
inactive_gradient: None,
|
2025-03-22 19:04:24 +01:00
|
|
|
urgent_gradient: None,
|
2024-12-27 15:39:37 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for rule in rules.iter().copied() {
|
|
|
|
|
let rule = BorderRule {
|
|
|
|
|
off: rule == "off" || rule == "off,on",
|
|
|
|
|
on: rule == "on" || rule == "off,on",
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
resolved.merge_with(&rule);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let config = Border {
|
|
|
|
|
off: config == "off",
|
|
|
|
|
..Default::default()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if resolved.resolve_against(config).off {
|
|
|
|
|
"off"
|
|
|
|
|
} else {
|
|
|
|
|
"on"
|
|
|
|
|
}
|
|
|
|
|
.to_owned()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert_snapshot!(is_on("off", &[]), @"off");
|
|
|
|
|
assert_snapshot!(is_on("off", &["off"]), @"off");
|
|
|
|
|
assert_snapshot!(is_on("off", &["on"]), @"on");
|
|
|
|
|
assert_snapshot!(is_on("off", &["off,on"]), @"on");
|
|
|
|
|
|
|
|
|
|
assert_snapshot!(is_on("on", &[]), @"on");
|
|
|
|
|
assert_snapshot!(is_on("on", &["off"]), @"off");
|
|
|
|
|
assert_snapshot!(is_on("on", &["on"]), @"on");
|
|
|
|
|
assert_snapshot!(is_on("on", &["off,on"]), @"on");
|
|
|
|
|
|
|
|
|
|
assert_snapshot!(is_on("off", &["off", "off"]), @"off");
|
|
|
|
|
assert_snapshot!(is_on("off", &["off", "on"]), @"on");
|
2024-12-27 15:40:48 +03:00
|
|
|
assert_snapshot!(is_on("off", &["on", "off"]), @"off");
|
2024-12-27 15:39:37 +03:00
|
|
|
assert_snapshot!(is_on("off", &["on", "on"]), @"on");
|
|
|
|
|
|
|
|
|
|
assert_snapshot!(is_on("on", &["off", "off"]), @"off");
|
|
|
|
|
assert_snapshot!(is_on("on", &["off", "on"]), @"on");
|
2024-12-27 15:40:48 +03:00
|
|
|
assert_snapshot!(is_on("on", &["on", "off"]), @"off");
|
2024-12-27 15:39:37 +03:00
|
|
|
assert_snapshot!(is_on("on", &["on", "on"]), @"on");
|
|
|
|
|
}
|
2023-09-05 12:58:51 +04:00
|
|
|
}
|