mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
1910 lines
65 KiB
Rust
1910 lines
65 KiB
Rust
#[macro_use]
|
|
extern crate tracing;
|
|
|
|
use std::ffi::OsStr;
|
|
use std::fs::{self, File};
|
|
use std::io::Write as _;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
use miette::{Context as _, IntoDiagnostic as _};
|
|
|
|
#[macro_use]
|
|
pub mod macros;
|
|
|
|
pub mod animations;
|
|
pub mod appearance;
|
|
pub mod binds;
|
|
pub mod debug;
|
|
pub mod gestures;
|
|
pub mod input;
|
|
pub mod layer_rule;
|
|
pub mod layout;
|
|
pub mod misc;
|
|
pub mod output;
|
|
pub mod utils;
|
|
pub mod window_rule;
|
|
pub mod workspace;
|
|
|
|
pub use crate::animations::{Animation, Animations};
|
|
pub use crate::appearance::*;
|
|
pub use crate::binds::*;
|
|
pub use crate::debug::Debug;
|
|
pub use crate::gestures::Gestures;
|
|
pub use crate::input::{Input, ModKey, ScrollMethod, TrackLayout, WarpMouseToFocusMode, Xkb};
|
|
pub use crate::layer_rule::LayerRule;
|
|
pub use crate::layout::*;
|
|
pub use crate::misc::*;
|
|
pub use crate::output::{Output, OutputName, Outputs, Position, Vrr};
|
|
pub use crate::utils::FloatOrInt;
|
|
use crate::utils::MergeWith as _;
|
|
pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule};
|
|
pub use crate::workspace::{Workspace, WorkspaceLayoutPart};
|
|
|
|
#[derive(knuffel::Decode, Debug, PartialEq)]
|
|
pub struct Config {
|
|
#[knuffel(child, default)]
|
|
pub input: Input,
|
|
#[knuffel(children(name = "output"))]
|
|
pub outputs: Outputs,
|
|
#[knuffel(children(name = "spawn-at-startup"))]
|
|
pub spawn_at_startup: Vec<SpawnAtStartup>,
|
|
#[knuffel(children(name = "spawn-sh-at-startup"))]
|
|
pub spawn_sh_at_startup: Vec<SpawnShAtStartup>,
|
|
#[knuffel(child, default)]
|
|
pub layout: LayoutPart,
|
|
#[knuffel(child, default)]
|
|
pub prefer_no_csd: bool,
|
|
#[knuffel(child, default)]
|
|
pub cursor: Cursor,
|
|
#[knuffel(
|
|
child,
|
|
unwrap(argument),
|
|
default = Some(String::from(
|
|
"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
|
|
)))
|
|
]
|
|
pub screenshot_path: Option<String>,
|
|
#[knuffel(child, default)]
|
|
pub clipboard: Clipboard,
|
|
#[knuffel(child, default)]
|
|
pub hotkey_overlay: HotkeyOverlay,
|
|
#[knuffel(child, default)]
|
|
pub config_notification: ConfigNotification,
|
|
#[knuffel(child, default)]
|
|
pub animations: Animations,
|
|
#[knuffel(child, default)]
|
|
pub gestures: Gestures,
|
|
#[knuffel(child, default)]
|
|
pub overview: Overview,
|
|
#[knuffel(child, default)]
|
|
pub environment: Environment,
|
|
#[knuffel(child, default)]
|
|
pub xwayland_satellite: XwaylandSatellite,
|
|
#[knuffel(children(name = "window-rule"))]
|
|
pub window_rules: Vec<WindowRule>,
|
|
#[knuffel(children(name = "layer-rule"))]
|
|
pub layer_rules: Vec<LayerRule>,
|
|
#[knuffel(child, default)]
|
|
pub binds: Binds,
|
|
#[knuffel(child, default)]
|
|
pub switch_events: SwitchBinds,
|
|
#[knuffel(child, default)]
|
|
pub debug: Debug,
|
|
#[knuffel(children(name = "workspace"))]
|
|
pub workspaces: Vec<Workspace>,
|
|
}
|
|
|
|
#[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 Config {
|
|
pub fn load(path: &Path) -> miette::Result<Self> {
|
|
let contents = fs::read_to_string(path)
|
|
.into_diagnostic()
|
|
.with_context(|| format!("error reading {path:?}"))?;
|
|
|
|
let config = Self::parse(
|
|
path.file_name()
|
|
.and_then(OsStr::to_str)
|
|
.unwrap_or("config.kdl"),
|
|
&contents,
|
|
)
|
|
.context("error parsing")?;
|
|
debug!("loaded config from {path:?}");
|
|
Ok(config)
|
|
}
|
|
|
|
pub fn parse(filename: &str, text: &str) -> Result<Self, knuffel::Error> {
|
|
let _span = tracy_client::span!("Config::parse");
|
|
knuffel::parse(filename, text)
|
|
}
|
|
|
|
pub fn resolve_layout(&self) -> Layout {
|
|
let mut rv = Layout::from_part(&self.layout);
|
|
|
|
// Preserve the behavior we'd always had for the border section:
|
|
// - `layout {}` gives border = off
|
|
// - `layout { border {} }` gives border = on
|
|
// - `layout { border { off } }` gives border = off
|
|
//
|
|
// This behavior is inconsistent with the rest of the config where adding an empty section
|
|
// generally doesn't change the outcome. Particularly, shadows are also disabled by default
|
|
// (like borders), and they always had an `on` instead of an `off` for this reason, so that
|
|
// writing `layout { shadow {} }` still results in shadow = off, as it should.
|
|
//
|
|
// Unfortunately, the default config has always had wording that heavily implies that
|
|
// `layout { border {} }` enables the borders. This wording is sure to be present in a lot
|
|
// of users' configs by now, which we can't change.
|
|
//
|
|
// Another way to make things consistent would be to default borders to on. However, that
|
|
// is annoying because it would mean changing many tests that rely on borders being off by
|
|
// default. This would also contradict the intended default borders value (off).
|
|
//
|
|
// So, let's just work around the problem here, preserving the original behavior.
|
|
if self.layout.border.is_some_and(|x| !x.on && !x.off) {
|
|
rv.border.off = false;
|
|
}
|
|
|
|
rv
|
|
}
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
Config::parse(
|
|
"default-config.kdl",
|
|
include_str!("../../resources/default-config.kdl"),
|
|
)
|
|
.unwrap()
|
|
}
|
|
}
|
|
|
|
impl ConfigPath {
|
|
/// Loads the config, returns 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")
|
|
}
|
|
|
|
/// Loads the config, or creates 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)
|
|
}
|
|
|
|
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(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use insta::{assert_debug_snapshot, assert_snapshot};
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn can_create_default_config() {
|
|
let _ = Config::default();
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[track_caller]
|
|
fn do_parse(text: &str) -> Config {
|
|
Config::parse("test.kdl", text)
|
|
.map_err(miette::Report::new)
|
|
.unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn parse() {
|
|
let parsed = do_parse(
|
|
r##"
|
|
input {
|
|
keyboard {
|
|
repeat-delay 600
|
|
repeat-rate 25
|
|
track-layout "window"
|
|
xkb {
|
|
layout "us,ru"
|
|
options "grp:win_space_toggle"
|
|
}
|
|
}
|
|
|
|
touchpad {
|
|
tap
|
|
dwt
|
|
dwtp
|
|
drag true
|
|
click-method "clickfinger"
|
|
accel-speed 0.2
|
|
accel-profile "flat"
|
|
scroll-method "two-finger"
|
|
scroll-button 272
|
|
scroll-button-lock
|
|
tap-button-map "left-middle-right"
|
|
disabled-on-external-mouse
|
|
scroll-factor 0.9
|
|
}
|
|
|
|
mouse {
|
|
natural-scroll
|
|
accel-speed 0.4
|
|
accel-profile "flat"
|
|
scroll-method "no-scroll"
|
|
scroll-button 273
|
|
middle-emulation
|
|
scroll-factor 0.2
|
|
}
|
|
|
|
trackpoint {
|
|
off
|
|
natural-scroll
|
|
accel-speed 0.0
|
|
accel-profile "flat"
|
|
scroll-method "on-button-down"
|
|
scroll-button 274
|
|
}
|
|
|
|
trackball {
|
|
off
|
|
natural-scroll
|
|
accel-speed 0.0
|
|
accel-profile "flat"
|
|
scroll-method "edge"
|
|
scroll-button 275
|
|
scroll-button-lock
|
|
left-handed
|
|
middle-emulation
|
|
}
|
|
|
|
tablet {
|
|
map-to-output "eDP-1"
|
|
calibration-matrix 1.0 2.0 3.0 \
|
|
4.0 5.0 6.0
|
|
}
|
|
|
|
touch {
|
|
map-to-output "eDP-1"
|
|
}
|
|
|
|
disable-power-key-handling
|
|
|
|
warp-mouse-to-focus
|
|
focus-follows-mouse
|
|
workspace-auto-back-and-forth
|
|
|
|
mod-key "Mod5"
|
|
mod-key-nested "Super"
|
|
}
|
|
|
|
output "eDP-1" {
|
|
focus-at-startup
|
|
scale 2
|
|
transform "flipped-90"
|
|
position x=10 y=20
|
|
mode "1920x1080@144"
|
|
variable-refresh-rate on-demand=true
|
|
background-color "rgba(25, 25, 102, 1.0)"
|
|
hot-corners {
|
|
off
|
|
top-left
|
|
top-right
|
|
bottom-left
|
|
bottom-right
|
|
}
|
|
}
|
|
|
|
layout {
|
|
focus-ring {
|
|
width 5
|
|
active-color 0 100 200 255
|
|
inactive-color 255 200 100 0
|
|
active-gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
|
|
}
|
|
|
|
border {
|
|
width 3
|
|
inactive-color "rgba(255, 200, 100, 0.0)"
|
|
}
|
|
|
|
shadow {
|
|
offset x=10 y=-20
|
|
}
|
|
|
|
tab-indicator {
|
|
width 10
|
|
position "top"
|
|
}
|
|
|
|
preset-column-widths {
|
|
proportion 0.25
|
|
proportion 0.5
|
|
fixed 960
|
|
fixed 1280
|
|
}
|
|
|
|
preset-window-heights {
|
|
proportion 0.25
|
|
proportion 0.5
|
|
fixed 960
|
|
fixed 1280
|
|
}
|
|
|
|
default-column-width { proportion 0.25; }
|
|
|
|
gaps 8
|
|
|
|
struts {
|
|
left 1
|
|
right 2
|
|
top 3
|
|
}
|
|
|
|
center-focused-column "on-overflow"
|
|
|
|
default-column-display "tabbed"
|
|
|
|
insert-hint {
|
|
color "rgb(255, 200, 127)"
|
|
gradient from="rgba(10, 20, 30, 1.0)" to="#0080ffff" relative-to="workspace-view"
|
|
}
|
|
}
|
|
|
|
spawn-at-startup "alacritty" "-e" "fish"
|
|
spawn-sh-at-startup "qs -c ~/source/qs/MyAwesomeShell"
|
|
|
|
prefer-no-csd
|
|
|
|
cursor {
|
|
xcursor-theme "breeze_cursors"
|
|
xcursor-size 16
|
|
hide-when-typing
|
|
hide-after-inactive-ms 3000
|
|
}
|
|
|
|
screenshot-path "~/Screenshots/screenshot.png"
|
|
|
|
clipboard {
|
|
disable-primary
|
|
}
|
|
|
|
hotkey-overlay {
|
|
skip-at-startup
|
|
}
|
|
|
|
animations {
|
|
slowdown 2.0
|
|
|
|
workspace-switch {
|
|
spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001
|
|
}
|
|
|
|
horizontal-view-movement {
|
|
duration-ms 100
|
|
curve "ease-out-expo"
|
|
}
|
|
|
|
window-open { off; }
|
|
|
|
window-close {
|
|
curve "cubic-bezier" 0.05 0.7 0.1 1
|
|
}
|
|
}
|
|
|
|
gestures {
|
|
dnd-edge-view-scroll {
|
|
trigger-width 10
|
|
max-speed 50
|
|
}
|
|
}
|
|
|
|
environment {
|
|
QT_QPA_PLATFORM "wayland"
|
|
DISPLAY null
|
|
}
|
|
|
|
window-rule {
|
|
match app-id=".*alacritty"
|
|
exclude title="~"
|
|
exclude is-active=true is-focused=false
|
|
|
|
open-on-output "eDP-1"
|
|
open-maximized true
|
|
open-fullscreen false
|
|
open-floating false
|
|
open-focused true
|
|
default-window-height { fixed 500; }
|
|
default-column-display "tabbed"
|
|
default-floating-position x=100 y=-200 relative-to="bottom-left"
|
|
|
|
focus-ring {
|
|
off
|
|
width 3
|
|
}
|
|
|
|
border {
|
|
on
|
|
width 8.5
|
|
}
|
|
|
|
tab-indicator {
|
|
active-color "#f00"
|
|
}
|
|
}
|
|
|
|
layer-rule {
|
|
match namespace="^notifications$"
|
|
block-out-from "screencast"
|
|
}
|
|
|
|
binds {
|
|
Mod+Escape hotkey-overlay-title="Inhibit" { toggle-keyboard-shortcuts-inhibit; }
|
|
Mod+Shift+Escape allow-inhibiting=true { toggle-keyboard-shortcuts-inhibit; }
|
|
Mod+T allow-when-locked=true { spawn "alacritty"; }
|
|
Mod+Q hotkey-overlay-title=null { close-window; }
|
|
Mod+Shift+H { focus-monitor-left; }
|
|
Mod+Shift+O { focus-monitor "eDP-1"; }
|
|
Mod+Ctrl+Shift+L { move-window-to-monitor-right; }
|
|
Mod+Ctrl+Alt+O { move-window-to-monitor "eDP-1"; }
|
|
Mod+Ctrl+Alt+P { move-column-to-monitor "DP-1"; }
|
|
Mod+Comma { consume-window-into-column; }
|
|
Mod+1 { focus-workspace 1; }
|
|
Mod+Shift+1 { focus-workspace "workspace-1"; }
|
|
Mod+Shift+E allow-inhibiting=false { quit skip-confirmation=true; }
|
|
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
|
|
Super+Alt+S allow-when-locked=true { spawn-sh "pkill orca || exec orca"; }
|
|
}
|
|
|
|
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"; }
|
|
}
|
|
|
|
debug {
|
|
render-drm-device "/dev/dri/renderD129"
|
|
}
|
|
|
|
workspace "workspace-1" {
|
|
open-on-output "eDP-1"
|
|
}
|
|
workspace "workspace-2"
|
|
workspace "workspace-3"
|
|
"##,
|
|
);
|
|
|
|
assert_debug_snapshot!(parsed, @r#"
|
|
Config {
|
|
input: Input {
|
|
keyboard: Keyboard {
|
|
xkb: Xkb {
|
|
rules: "",
|
|
model: "",
|
|
layout: "us,ru",
|
|
variant: "",
|
|
options: Some(
|
|
"grp:win_space_toggle",
|
|
),
|
|
file: None,
|
|
},
|
|
repeat_delay: 600,
|
|
repeat_rate: 25,
|
|
track_layout: Window,
|
|
numlock: false,
|
|
},
|
|
touchpad: Touchpad {
|
|
off: false,
|
|
tap: true,
|
|
dwt: true,
|
|
dwtp: true,
|
|
drag: Some(
|
|
true,
|
|
),
|
|
drag_lock: false,
|
|
natural_scroll: false,
|
|
click_method: Some(
|
|
Clickfinger,
|
|
),
|
|
accel_speed: FloatOrInt(
|
|
0.2,
|
|
),
|
|
accel_profile: Some(
|
|
Flat,
|
|
),
|
|
scroll_method: Some(
|
|
TwoFinger,
|
|
),
|
|
scroll_button: Some(
|
|
272,
|
|
),
|
|
scroll_button_lock: true,
|
|
tap_button_map: Some(
|
|
LeftMiddleRight,
|
|
),
|
|
left_handed: false,
|
|
disabled_on_external_mouse: true,
|
|
middle_emulation: false,
|
|
scroll_factor: Some(
|
|
ScrollFactor {
|
|
base: Some(
|
|
FloatOrInt(
|
|
0.9,
|
|
),
|
|
),
|
|
horizontal: None,
|
|
vertical: None,
|
|
},
|
|
),
|
|
},
|
|
mouse: Mouse {
|
|
off: false,
|
|
natural_scroll: true,
|
|
accel_speed: FloatOrInt(
|
|
0.4,
|
|
),
|
|
accel_profile: Some(
|
|
Flat,
|
|
),
|
|
scroll_method: Some(
|
|
NoScroll,
|
|
),
|
|
scroll_button: Some(
|
|
273,
|
|
),
|
|
scroll_button_lock: false,
|
|
left_handed: false,
|
|
middle_emulation: true,
|
|
scroll_factor: Some(
|
|
ScrollFactor {
|
|
base: Some(
|
|
FloatOrInt(
|
|
0.2,
|
|
),
|
|
),
|
|
horizontal: None,
|
|
vertical: None,
|
|
},
|
|
),
|
|
},
|
|
trackpoint: Trackpoint {
|
|
off: true,
|
|
natural_scroll: true,
|
|
accel_speed: FloatOrInt(
|
|
0.0,
|
|
),
|
|
accel_profile: Some(
|
|
Flat,
|
|
),
|
|
scroll_method: Some(
|
|
OnButtonDown,
|
|
),
|
|
scroll_button: Some(
|
|
274,
|
|
),
|
|
scroll_button_lock: false,
|
|
left_handed: false,
|
|
middle_emulation: false,
|
|
},
|
|
trackball: Trackball {
|
|
off: true,
|
|
natural_scroll: true,
|
|
accel_speed: FloatOrInt(
|
|
0.0,
|
|
),
|
|
accel_profile: Some(
|
|
Flat,
|
|
),
|
|
scroll_method: Some(
|
|
Edge,
|
|
),
|
|
scroll_button: Some(
|
|
275,
|
|
),
|
|
scroll_button_lock: true,
|
|
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 {
|
|
off: false,
|
|
map_to_output: Some(
|
|
"eDP-1",
|
|
),
|
|
},
|
|
disable_power_key_handling: true,
|
|
warp_mouse_to_focus: Some(
|
|
WarpMouseToFocus {
|
|
mode: None,
|
|
},
|
|
),
|
|
focus_follows_mouse: Some(
|
|
FocusFollowsMouse {
|
|
max_scroll_amount: None,
|
|
},
|
|
),
|
|
workspace_auto_back_and_forth: true,
|
|
mod_key: Some(
|
|
IsoLevel3Shift,
|
|
),
|
|
mod_key_nested: Some(
|
|
Super,
|
|
),
|
|
},
|
|
outputs: Outputs(
|
|
[
|
|
Output {
|
|
off: false,
|
|
name: "eDP-1",
|
|
scale: Some(
|
|
FloatOrInt(
|
|
2.0,
|
|
),
|
|
),
|
|
transform: Flipped90,
|
|
position: Some(
|
|
Position {
|
|
x: 10,
|
|
y: 20,
|
|
},
|
|
),
|
|
mode: Some(
|
|
ConfiguredMode {
|
|
width: 1920,
|
|
height: 1080,
|
|
refresh: Some(
|
|
144.0,
|
|
),
|
|
},
|
|
),
|
|
variable_refresh_rate: Some(
|
|
Vrr {
|
|
on_demand: true,
|
|
},
|
|
),
|
|
focus_at_startup: true,
|
|
background_color: Some(
|
|
Color {
|
|
r: 0.09803922,
|
|
g: 0.09803922,
|
|
b: 0.4,
|
|
a: 1.0,
|
|
},
|
|
),
|
|
backdrop_color: None,
|
|
hot_corners: Some(
|
|
HotCorners {
|
|
off: true,
|
|
top_left: true,
|
|
top_right: true,
|
|
bottom_left: true,
|
|
bottom_right: true,
|
|
},
|
|
),
|
|
layout: None,
|
|
},
|
|
],
|
|
),
|
|
spawn_at_startup: [
|
|
SpawnAtStartup {
|
|
command: [
|
|
"alacritty",
|
|
"-e",
|
|
"fish",
|
|
],
|
|
},
|
|
],
|
|
spawn_sh_at_startup: [
|
|
SpawnShAtStartup {
|
|
command: "qs -c ~/source/qs/MyAwesomeShell",
|
|
},
|
|
],
|
|
layout: LayoutPart {
|
|
focus_ring: Some(
|
|
BorderRule {
|
|
off: false,
|
|
on: false,
|
|
width: Some(
|
|
FloatOrInt(
|
|
5.0,
|
|
),
|
|
),
|
|
active_color: Some(
|
|
Color {
|
|
r: 0.0,
|
|
g: 0.39215687,
|
|
b: 0.78431374,
|
|
a: 1.0,
|
|
},
|
|
),
|
|
inactive_color: Some(
|
|
Color {
|
|
r: 1.0,
|
|
g: 0.78431374,
|
|
b: 0.39215687,
|
|
a: 0.0,
|
|
},
|
|
),
|
|
urgent_color: None,
|
|
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,
|
|
urgent_gradient: None,
|
|
},
|
|
),
|
|
border: Some(
|
|
BorderRule {
|
|
off: false,
|
|
on: false,
|
|
width: Some(
|
|
FloatOrInt(
|
|
3.0,
|
|
),
|
|
),
|
|
active_color: None,
|
|
inactive_color: Some(
|
|
Color {
|
|
r: 1.0,
|
|
g: 0.78431374,
|
|
b: 0.39215687,
|
|
a: 0.0,
|
|
},
|
|
),
|
|
urgent_color: None,
|
|
active_gradient: None,
|
|
inactive_gradient: None,
|
|
urgent_gradient: None,
|
|
},
|
|
),
|
|
shadow: Some(
|
|
ShadowRule {
|
|
off: false,
|
|
on: false,
|
|
offset: Some(
|
|
ShadowOffset {
|
|
x: FloatOrInt(
|
|
10.0,
|
|
),
|
|
y: FloatOrInt(
|
|
-20.0,
|
|
),
|
|
},
|
|
),
|
|
softness: None,
|
|
spread: None,
|
|
draw_behind_window: None,
|
|
color: None,
|
|
inactive_color: None,
|
|
},
|
|
),
|
|
tab_indicator: Some(
|
|
TabIndicatorPart {
|
|
off: false,
|
|
on: false,
|
|
hide_when_single_tab: None,
|
|
place_within_column: None,
|
|
gap: None,
|
|
width: Some(
|
|
FloatOrInt(
|
|
10.0,
|
|
),
|
|
),
|
|
length: None,
|
|
position: Some(
|
|
Top,
|
|
),
|
|
gaps_between_tabs: None,
|
|
corner_radius: None,
|
|
active_color: None,
|
|
inactive_color: None,
|
|
urgent_color: None,
|
|
active_gradient: None,
|
|
inactive_gradient: None,
|
|
urgent_gradient: None,
|
|
},
|
|
),
|
|
insert_hint: Some(
|
|
InsertHintPart {
|
|
off: false,
|
|
on: false,
|
|
color: Some(
|
|
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,
|
|
a: 1.0,
|
|
},
|
|
angle: 180,
|
|
relative_to: WorkspaceView,
|
|
in_: GradientInterpolation {
|
|
color_space: Srgb,
|
|
hue_interpolation: Shorter,
|
|
},
|
|
},
|
|
),
|
|
},
|
|
),
|
|
preset_column_widths: Some(
|
|
[
|
|
Proportion(
|
|
0.25,
|
|
),
|
|
Proportion(
|
|
0.5,
|
|
),
|
|
Fixed(
|
|
960,
|
|
),
|
|
Fixed(
|
|
1280,
|
|
),
|
|
],
|
|
),
|
|
default_column_width: Some(
|
|
DefaultPresetSize(
|
|
Some(
|
|
Proportion(
|
|
0.25,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
preset_window_heights: Some(
|
|
[
|
|
Proportion(
|
|
0.25,
|
|
),
|
|
Proportion(
|
|
0.5,
|
|
),
|
|
Fixed(
|
|
960,
|
|
),
|
|
Fixed(
|
|
1280,
|
|
),
|
|
],
|
|
),
|
|
center_focused_column: Some(
|
|
OnOverflow,
|
|
),
|
|
always_center_single_column: None,
|
|
empty_workspace_above_first: None,
|
|
default_column_display: Some(
|
|
Tabbed,
|
|
),
|
|
gaps: Some(
|
|
FloatOrInt(
|
|
8.0,
|
|
),
|
|
),
|
|
struts: Some(
|
|
Struts {
|
|
left: FloatOrInt(
|
|
1.0,
|
|
),
|
|
right: FloatOrInt(
|
|
2.0,
|
|
),
|
|
top: FloatOrInt(
|
|
3.0,
|
|
),
|
|
bottom: FloatOrInt(
|
|
0.0,
|
|
),
|
|
},
|
|
),
|
|
background_color: None,
|
|
},
|
|
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,
|
|
hide_not_bound: false,
|
|
},
|
|
config_notification: ConfigNotification {
|
|
disable_failed: false,
|
|
},
|
|
animations: Animations {
|
|
off: false,
|
|
slowdown: FloatOrInt(
|
|
2.0,
|
|
),
|
|
workspace_switch: WorkspaceSwitchAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 1.0,
|
|
stiffness: 1000,
|
|
epsilon: 0.0001,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
window_open: WindowOpenAnim {
|
|
anim: Animation {
|
|
off: true,
|
|
kind: Easing(
|
|
EasingParams {
|
|
duration_ms: 150,
|
|
curve: EaseOutExpo,
|
|
},
|
|
),
|
|
},
|
|
custom_shader: None,
|
|
},
|
|
window_close: WindowCloseAnim {
|
|
anim: Animation {
|
|
off: false,
|
|
kind: Easing(
|
|
EasingParams {
|
|
duration_ms: 150,
|
|
curve: CubicBezier(
|
|
0.05,
|
|
0.7,
|
|
0.1,
|
|
1.0,
|
|
),
|
|
},
|
|
),
|
|
},
|
|
custom_shader: None,
|
|
},
|
|
horizontal_view_movement: HorizontalViewMovementAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Easing(
|
|
EasingParams {
|
|
duration_ms: 100,
|
|
curve: EaseOutExpo,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
window_movement: WindowMovementAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 1.0,
|
|
stiffness: 800,
|
|
epsilon: 0.0001,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
window_resize: WindowResizeAnim {
|
|
anim: Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 1.0,
|
|
stiffness: 800,
|
|
epsilon: 0.0001,
|
|
},
|
|
),
|
|
},
|
|
custom_shader: None,
|
|
},
|
|
config_notification_open_close: ConfigNotificationOpenCloseAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 0.6,
|
|
stiffness: 1000,
|
|
epsilon: 0.001,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
exit_confirmation_open_close: ExitConfirmationOpenCloseAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 0.6,
|
|
stiffness: 500,
|
|
epsilon: 0.01,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
screenshot_ui_open: ScreenshotUiOpenAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Easing(
|
|
EasingParams {
|
|
duration_ms: 200,
|
|
curve: EaseOutQuad,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
overview_open_close: OverviewOpenCloseAnim(
|
|
Animation {
|
|
off: false,
|
|
kind: Spring(
|
|
SpringParams {
|
|
damping_ratio: 1.0,
|
|
stiffness: 800,
|
|
epsilon: 0.0001,
|
|
},
|
|
),
|
|
},
|
|
),
|
|
},
|
|
gestures: Gestures {
|
|
dnd_edge_view_scroll: DndEdgeViewScroll {
|
|
trigger_width: FloatOrInt(
|
|
10.0,
|
|
),
|
|
delay_ms: 100,
|
|
max_speed: FloatOrInt(
|
|
50.0,
|
|
),
|
|
},
|
|
dnd_edge_workspace_switch: DndEdgeWorkspaceSwitch {
|
|
trigger_height: FloatOrInt(
|
|
50.0,
|
|
),
|
|
delay_ms: 100,
|
|
max_speed: FloatOrInt(
|
|
1500.0,
|
|
),
|
|
},
|
|
hot_corners: HotCorners {
|
|
off: false,
|
|
top_left: false,
|
|
top_right: false,
|
|
bottom_left: false,
|
|
bottom_right: false,
|
|
},
|
|
},
|
|
overview: Overview {
|
|
zoom: FloatOrInt(
|
|
0.5,
|
|
),
|
|
backdrop_color: Color {
|
|
r: 0.15,
|
|
g: 0.15,
|
|
b: 0.15,
|
|
a: 1.0,
|
|
},
|
|
workspace_shadow: WorkspaceShadow {
|
|
off: false,
|
|
offset: ShadowOffset {
|
|
x: FloatOrInt(
|
|
0.0,
|
|
),
|
|
y: FloatOrInt(
|
|
10.0,
|
|
),
|
|
},
|
|
softness: FloatOrInt(
|
|
40.0,
|
|
),
|
|
spread: FloatOrInt(
|
|
10.0,
|
|
),
|
|
color: Color {
|
|
r: 0.0,
|
|
g: 0.0,
|
|
b: 0.0,
|
|
a: 0.3137255,
|
|
},
|
|
},
|
|
},
|
|
environment: Environment(
|
|
[
|
|
EnvironmentVariable {
|
|
name: "QT_QPA_PLATFORM",
|
|
value: Some(
|
|
"wayland",
|
|
),
|
|
},
|
|
EnvironmentVariable {
|
|
name: "DISPLAY",
|
|
value: None,
|
|
},
|
|
],
|
|
),
|
|
xwayland_satellite: XwaylandSatellite {
|
|
off: false,
|
|
path: "xwayland-satellite",
|
|
},
|
|
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,
|
|
is_urgent: None,
|
|
at_startup: None,
|
|
},
|
|
],
|
|
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,
|
|
is_urgent: None,
|
|
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,
|
|
is_urgent: None,
|
|
at_startup: None,
|
|
},
|
|
],
|
|
default_column_width: None,
|
|
default_window_height: Some(
|
|
DefaultPresetSize(
|
|
Some(
|
|
Fixed(
|
|
500,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
open_on_output: Some(
|
|
"eDP-1",
|
|
),
|
|
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,
|
|
),
|
|
),
|
|
active_color: None,
|
|
inactive_color: None,
|
|
urgent_color: None,
|
|
active_gradient: None,
|
|
inactive_gradient: None,
|
|
urgent_gradient: None,
|
|
},
|
|
border: BorderRule {
|
|
off: false,
|
|
on: true,
|
|
width: Some(
|
|
FloatOrInt(
|
|
8.5,
|
|
),
|
|
),
|
|
active_color: None,
|
|
inactive_color: None,
|
|
urgent_color: None,
|
|
active_gradient: None,
|
|
inactive_gradient: None,
|
|
urgent_gradient: None,
|
|
},
|
|
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,
|
|
},
|
|
),
|
|
inactive_color: None,
|
|
urgent_color: None,
|
|
active_gradient: None,
|
|
inactive_gradient: None,
|
|
urgent_gradient: None,
|
|
},
|
|
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,
|
|
),
|
|
default_floating_position: Some(
|
|
FloatingPosition {
|
|
x: FloatOrInt(
|
|
100.0,
|
|
),
|
|
y: FloatOrInt(
|
|
-200.0,
|
|
),
|
|
relative_to: BottomLeft,
|
|
},
|
|
),
|
|
scroll_factor: None,
|
|
tiled_state: None,
|
|
},
|
|
],
|
|
layer_rules: [
|
|
LayerRule {
|
|
matches: [
|
|
Match {
|
|
namespace: Some(
|
|
RegexEq(
|
|
Regex(
|
|
"^notifications$",
|
|
),
|
|
),
|
|
),
|
|
at_startup: None,
|
|
},
|
|
],
|
|
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,
|
|
},
|
|
geometry_corner_radius: None,
|
|
place_within_backdrop: None,
|
|
baba_is_float: None,
|
|
},
|
|
],
|
|
binds: Binds(
|
|
[
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_Escape,
|
|
),
|
|
modifiers: Modifiers(
|
|
COMPOSITOR,
|
|
),
|
|
},
|
|
action: ToggleKeyboardShortcutsInhibit,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: false,
|
|
hotkey_overlay_title: Some(
|
|
Some(
|
|
"Inhibit",
|
|
),
|
|
),
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_Escape,
|
|
),
|
|
modifiers: Modifiers(
|
|
SHIFT | COMPOSITOR,
|
|
),
|
|
},
|
|
action: ToggleKeyboardShortcutsInhibit,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: false,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_t,
|
|
),
|
|
modifiers: Modifiers(
|
|
COMPOSITOR,
|
|
),
|
|
},
|
|
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,
|
|
),
|
|
modifiers: Modifiers(
|
|
COMPOSITOR,
|
|
),
|
|
},
|
|
action: CloseWindow,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: Some(
|
|
None,
|
|
),
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_h,
|
|
),
|
|
modifiers: Modifiers(
|
|
SHIFT | COMPOSITOR,
|
|
),
|
|
},
|
|
action: FocusMonitorLeft,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
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,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_l,
|
|
),
|
|
modifiers: Modifiers(
|
|
CTRL | SHIFT | COMPOSITOR,
|
|
),
|
|
},
|
|
action: MoveWindowToMonitorRight,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
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,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_comma,
|
|
),
|
|
modifiers: Modifiers(
|
|
COMPOSITOR,
|
|
),
|
|
},
|
|
action: ConsumeWindowIntoColumn,
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_1,
|
|
),
|
|
modifiers: Modifiers(
|
|
COMPOSITOR,
|
|
),
|
|
},
|
|
action: FocusWorkspace(
|
|
Index(
|
|
1,
|
|
),
|
|
),
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_1,
|
|
),
|
|
modifiers: Modifiers(
|
|
SHIFT | COMPOSITOR,
|
|
),
|
|
},
|
|
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,
|
|
),
|
|
},
|
|
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,
|
|
),
|
|
},
|
|
action: FocusWorkspaceDown,
|
|
repeat: true,
|
|
cooldown: Some(
|
|
150ms,
|
|
),
|
|
allow_when_locked: false,
|
|
allow_inhibiting: true,
|
|
hotkey_overlay_title: None,
|
|
},
|
|
Bind {
|
|
key: Key {
|
|
trigger: Keysym(
|
|
XK_s,
|
|
),
|
|
modifiers: Modifiers(
|
|
ALT | SUPER,
|
|
),
|
|
},
|
|
action: SpawnSh(
|
|
"pkill orca || exec orca",
|
|
),
|
|
repeat: true,
|
|
cooldown: None,
|
|
allow_when_locked: true,
|
|
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",
|
|
],
|
|
},
|
|
),
|
|
tablet_mode_off: Some(
|
|
SwitchAction {
|
|
spawn: [
|
|
"bash",
|
|
"-c",
|
|
"gsettings set org.gnome.desktop.a11y.applications screen-keyboard-enabled false",
|
|
],
|
|
},
|
|
),
|
|
},
|
|
debug: Debug {
|
|
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,
|
|
keep_max_bpc_unchanged: false,
|
|
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,
|
|
honor_xdg_activation_with_invalid_serial: false,
|
|
deactivate_unfocused_windows: false,
|
|
skip_cursor_only_updates_during_vrr: false,
|
|
},
|
|
workspaces: [
|
|
Workspace {
|
|
name: WorkspaceName(
|
|
"workspace-1",
|
|
),
|
|
open_on_output: Some(
|
|
"eDP-1",
|
|
),
|
|
layout: None,
|
|
},
|
|
Workspace {
|
|
name: WorkspaceName(
|
|
"workspace-2",
|
|
),
|
|
open_on_output: None,
|
|
layout: None,
|
|
},
|
|
Workspace {
|
|
name: WorkspaceName(
|
|
"workspace-3",
|
|
),
|
|
open_on_output: None,
|
|
layout: None,
|
|
},
|
|
],
|
|
}
|
|
"#);
|
|
}
|
|
|
|
fn diff_lines(expected: &str, actual: &str) -> String {
|
|
let mut output = String::new();
|
|
let mut in_change = false;
|
|
|
|
for change in diff::lines(expected, actual) {
|
|
match change {
|
|
diff::Result::Both(_, _) => {
|
|
in_change = false;
|
|
}
|
|
diff::Result::Left(line) => {
|
|
if !output.is_empty() && !in_change {
|
|
output.push('\n');
|
|
}
|
|
output.push('-');
|
|
output.push_str(line);
|
|
output.push('\n');
|
|
in_change = true;
|
|
}
|
|
diff::Result::Right(line) => {
|
|
if !output.is_empty() && !in_change {
|
|
output.push('\n');
|
|
}
|
|
output.push('+');
|
|
output.push_str(line);
|
|
output.push('\n');
|
|
in_change = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
output
|
|
}
|
|
|
|
#[test]
|
|
fn diff_empty_to_default() {
|
|
// We try to write the config defaults in such a way that empty sections (and an empty
|
|
// config) give the same outcome as the default config bundled with niri. This test
|
|
// verifies the actual differences between the two.
|
|
let mut default_config = Config::default();
|
|
let empty_config = Config::parse("empty.kdl", "").unwrap();
|
|
|
|
// Some notable omissions: the default config has some window rules, and an empty config
|
|
// will not have any binds. Clear them out so they don't spam the diff.
|
|
default_config.window_rules.clear();
|
|
default_config.binds.0.clear();
|
|
|
|
let default_layout = default_config.resolve_layout();
|
|
let empty_layout = empty_config.resolve_layout();
|
|
default_config.layout = Default::default();
|
|
assert_snapshot!(
|
|
diff_lines(
|
|
&format!("{empty_layout:#?}"),
|
|
&format!("{default_layout:#?}")
|
|
),
|
|
@r"
|
|
- 0.3333333333333333,
|
|
+ 0.33333,
|
|
|
|
- 0.6666666666666666,
|
|
+ 0.66667,
|
|
",
|
|
);
|
|
|
|
assert_snapshot!(
|
|
diff_lines(
|
|
&format!("{empty_config:#?}"),
|
|
&format!("{default_config:#?}")
|
|
),
|
|
@r#"
|
|
- numlock: false,
|
|
+ numlock: true,
|
|
|
|
- tap: false,
|
|
+ tap: true,
|
|
|
|
- natural_scroll: false,
|
|
+ natural_scroll: true,
|
|
|
|
- spawn_at_startup: [],
|
|
+ spawn_at_startup: [
|
|
+ SpawnAtStartup {
|
|
+ command: [
|
|
+ "waybar",
|
|
+ ],
|
|
+ },
|
|
+ ],
|
|
"#,
|
|
);
|
|
}
|
|
}
|