From a03a7bd1a321ccb6eab91d1279abdb55ee1814f9 Mon Sep 17 00:00:00 2001 From: Ivan Molodetskikh Date: Wed, 27 Aug 2025 10:54:41 +0300 Subject: config: Extract binds --- niri-config/src/binds.rs | 975 +++++++++++++++++++++++++++++++++++++++++++++++ niri-config/src/input.rs | 3 +- niri-config/src/lib.rs | 967 +--------------------------------------------- 3 files changed, 979 insertions(+), 966 deletions(-) create mode 100644 niri-config/src/binds.rs diff --git a/niri-config/src/binds.rs b/niri-config/src/binds.rs new file mode 100644 index 00000000..2cdc96cd --- /dev/null +++ b/niri-config/src/binds.rs @@ -0,0 +1,975 @@ +use std::collections::HashSet; +use std::str::FromStr; +use std::time::Duration; + +use bitflags::bitflags; +use knuffel::errors::DecodeError; +use miette::miette; +use niri_ipc::{ + ColumnDisplay, LayoutSwitchTarget, PositionChange, SizeChange, WorkspaceReferenceArg, +}; +use smithay::input::keyboard::keysyms::KEY_NoSymbol; +use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE}; +use smithay::input::keyboard::Keysym; + +use crate::expect_only_children; + +#[derive(Debug, Default, PartialEq)] +pub struct Binds(pub Vec); + +#[derive(Debug, Clone, PartialEq)] +pub struct Bind { + pub key: Key, + pub action: Action, + pub repeat: bool, + pub cooldown: Option, + pub allow_when_locked: bool, + pub allow_inhibiting: bool, + pub hotkey_overlay_title: Option>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub struct Key { + pub trigger: Trigger, + pub modifiers: Modifiers, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum Trigger { + Keysym(Keysym), + MouseLeft, + MouseRight, + MouseMiddle, + MouseBack, + MouseForward, + WheelScrollDown, + WheelScrollUp, + WheelScrollLeft, + WheelScrollRight, + TouchpadScrollDown, + TouchpadScrollUp, + TouchpadScrollLeft, + TouchpadScrollRight, +} + +bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + pub struct Modifiers : u8 { + const CTRL = 1; + 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; + } +} + +#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] +pub struct SwitchBinds { + #[knuffel(child)] + pub lid_open: Option, + #[knuffel(child)] + pub lid_close: Option, + #[knuffel(child)] + pub tablet_mode_on: Option, + #[knuffel(child)] + pub tablet_mode_off: Option, +} + +#[derive(knuffel::Decode, Debug, Clone, PartialEq)] +pub struct SwitchAction { + #[knuffel(child, unwrap(arguments))] + pub spawn: Vec, +} + +// Remember to add new actions to the CLI enum too. +#[derive(knuffel::Decode, Debug, Clone, PartialEq)] +pub enum Action { + Quit(#[knuffel(property(name = "skip-confirmation"), default)] bool), + #[knuffel(skip)] + ChangeVt(i32), + Suspend, + PowerOffMonitors, + PowerOnMonitors, + ToggleDebugTint, + DebugToggleOpaqueRegions, + DebugToggleDamage, + Spawn(#[knuffel(arguments)] Vec), + SpawnSh(#[knuffel(argument)] String), + DoScreenTransition(#[knuffel(property(name = "delay-ms"))] Option), + #[knuffel(skip)] + ConfirmScreenshot { + write_to_disk: bool, + }, + #[knuffel(skip)] + CancelScreenshot, + #[knuffel(skip)] + ScreenshotTogglePointer, + 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, + ), + ScreenshotWindow(#[knuffel(property(name = "write-to-disk"), default = true)] bool), + #[knuffel(skip)] + ScreenshotWindowById { + id: u64, + write_to_disk: bool, + }, + ToggleKeyboardShortcutsInhibit, + CloseWindow, + #[knuffel(skip)] + CloseWindowById(u64), + FullscreenWindow, + #[knuffel(skip)] + FullscreenWindowById(u64), + ToggleWindowedFullscreen, + #[knuffel(skip)] + ToggleWindowedFullscreenById(u64), + #[knuffel(skip)] + FocusWindow(u64), + FocusWindowInColumn(#[knuffel(argument)] u8), + FocusWindowPrevious, + FocusColumnLeft, + #[knuffel(skip)] + FocusColumnLeftUnderMouse, + FocusColumnRight, + #[knuffel(skip)] + FocusColumnRightUnderMouse, + FocusColumnFirst, + FocusColumnLast, + FocusColumnRightOrFirst, + FocusColumnLeftOrLast, + FocusColumn(#[knuffel(argument)] usize), + FocusWindowOrMonitorUp, + FocusWindowOrMonitorDown, + FocusColumnOrMonitorLeft, + FocusColumnOrMonitorRight, + FocusWindowDown, + FocusWindowUp, + FocusWindowDownOrColumnLeft, + FocusWindowDownOrColumnRight, + FocusWindowUpOrColumnLeft, + FocusWindowUpOrColumnRight, + FocusWindowOrWorkspaceDown, + FocusWindowOrWorkspaceUp, + FocusWindowTop, + FocusWindowBottom, + FocusWindowDownOrTop, + FocusWindowUpOrBottom, + MoveColumnLeft, + MoveColumnRight, + MoveColumnToFirst, + MoveColumnToLast, + MoveColumnLeftOrToMonitorLeft, + MoveColumnRightOrToMonitorRight, + MoveColumnToIndex(#[knuffel(argument)] usize), + MoveWindowDown, + MoveWindowUp, + MoveWindowDownOrToWorkspaceDown, + MoveWindowUpOrToWorkspaceUp, + ConsumeOrExpelWindowLeft, + #[knuffel(skip)] + ConsumeOrExpelWindowLeftById(u64), + ConsumeOrExpelWindowRight, + #[knuffel(skip)] + ConsumeOrExpelWindowRightById(u64), + ConsumeWindowIntoColumn, + ExpelWindowFromColumn, + SwapWindowLeft, + SwapWindowRight, + ToggleColumnTabbedDisplay, + SetColumnDisplay(#[knuffel(argument, str)] ColumnDisplay), + CenterColumn, + CenterWindow, + #[knuffel(skip)] + CenterWindowById(u64), + CenterVisibleColumns, + FocusWorkspaceDown, + #[knuffel(skip)] + FocusWorkspaceDownUnderMouse, + FocusWorkspaceUp, + #[knuffel(skip)] + FocusWorkspaceUpUnderMouse, + FocusWorkspace(#[knuffel(argument)] WorkspaceReference), + FocusWorkspacePrevious, + MoveWindowToWorkspaceDown(#[knuffel(property(name = "focus"), default = true)] bool), + MoveWindowToWorkspaceUp(#[knuffel(property(name = "focus"), default = true)] bool), + MoveWindowToWorkspace( + #[knuffel(argument)] WorkspaceReference, + #[knuffel(property(name = "focus"), default = true)] bool, + ), + #[knuffel(skip)] + MoveWindowToWorkspaceById { + window_id: u64, + reference: WorkspaceReference, + focus: bool, + }, + 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, + ), + MoveWorkspaceDown, + MoveWorkspaceUp, + MoveWorkspaceToIndex(#[knuffel(argument)] usize), + #[knuffel(skip)] + MoveWorkspaceToIndexByRef { + new_idx: usize, + reference: WorkspaceReference, + }, + #[knuffel(skip)] + MoveWorkspaceToMonitorByRef { + output_name: String, + reference: WorkspaceReference, + }, + MoveWorkspaceToMonitor(#[knuffel(argument)] String), + SetWorkspaceName(#[knuffel(argument)] String), + #[knuffel(skip)] + SetWorkspaceNameByRef { + name: String, + reference: WorkspaceReference, + }, + UnsetWorkspaceName, + #[knuffel(skip)] + UnsetWorkSpaceNameByRef(#[knuffel(argument)] WorkspaceReference), + FocusMonitorLeft, + FocusMonitorRight, + FocusMonitorDown, + FocusMonitorUp, + FocusMonitorPrevious, + FocusMonitorNext, + FocusMonitor(#[knuffel(argument)] String), + MoveWindowToMonitorLeft, + MoveWindowToMonitorRight, + MoveWindowToMonitorDown, + MoveWindowToMonitorUp, + MoveWindowToMonitorPrevious, + MoveWindowToMonitorNext, + MoveWindowToMonitor(#[knuffel(argument)] String), + #[knuffel(skip)] + MoveWindowToMonitorById { + id: u64, + output: String, + }, + MoveColumnToMonitorLeft, + MoveColumnToMonitorRight, + MoveColumnToMonitorDown, + MoveColumnToMonitorUp, + MoveColumnToMonitorPrevious, + MoveColumnToMonitorNext, + MoveColumnToMonitor(#[knuffel(argument)] String), + SetWindowWidth(#[knuffel(argument, str)] SizeChange), + #[knuffel(skip)] + SetWindowWidthById { + id: u64, + change: SizeChange, + }, + SetWindowHeight(#[knuffel(argument, str)] SizeChange), + #[knuffel(skip)] + SetWindowHeightById { + id: u64, + change: SizeChange, + }, + ResetWindowHeight, + #[knuffel(skip)] + ResetWindowHeightById(u64), + SwitchPresetColumnWidth, + SwitchPresetWindowWidth, + #[knuffel(skip)] + SwitchPresetWindowWidthById(u64), + SwitchPresetWindowHeight, + #[knuffel(skip)] + SwitchPresetWindowHeightById(u64), + MaximizeColumn, + SetColumnWidth(#[knuffel(argument, str)] SizeChange), + ExpandColumnToAvailableWidth, + SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget), + ShowHotkeyOverlay, + MoveWorkspaceToMonitorLeft, + MoveWorkspaceToMonitorRight, + MoveWorkspaceToMonitorDown, + MoveWorkspaceToMonitorUp, + MoveWorkspaceToMonitorPrevious, + MoveWorkspaceToMonitorNext, + ToggleWindowFloating, + #[knuffel(skip)] + ToggleWindowFloatingById(u64), + MoveWindowToFloating, + #[knuffel(skip)] + MoveWindowToFloatingById(u64), + MoveWindowToTiling, + #[knuffel(skip)] + MoveWindowToTilingById(u64), + FocusFloating, + FocusTiling, + SwitchFocusBetweenFloatingAndTiling, + #[knuffel(skip)] + MoveFloatingWindowById { + id: Option, + x: PositionChange, + y: PositionChange, + }, + ToggleWindowRuleOpacity, + #[knuffel(skip)] + ToggleWindowRuleOpacityById(u64), + SetDynamicCastWindow, + #[knuffel(skip)] + SetDynamicCastWindowById(u64), + SetDynamicCastMonitor(#[knuffel(argument)] Option), + ClearDynamicCastTarget, + ToggleOverview, + OpenOverview, + CloseOverview, + #[knuffel(skip)] + ToggleWindowUrgent(u64), + #[knuffel(skip)] + SetWindowUrgent(u64), + #[knuffel(skip)] + UnsetWindowUrgent(u64), + #[knuffel(skip)] + LoadConfigFile, +} + +impl From for Action { + fn from(value: niri_ipc::Action) -> Self { + match value { + niri_ipc::Action::Quit { skip_confirmation } => Self::Quit(skip_confirmation), + niri_ipc::Action::PowerOffMonitors {} => Self::PowerOffMonitors, + niri_ipc::Action::PowerOnMonitors {} => Self::PowerOnMonitors, + niri_ipc::Action::Spawn { command } => Self::Spawn(command), + niri_ipc::Action::SpawnSh { command } => Self::SpawnSh(command), + niri_ipc::Action::DoScreenTransition { delay_ms } => Self::DoScreenTransition(delay_ms), + 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), + 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 }, + niri_ipc::Action::ToggleKeyboardShortcutsInhibit {} => { + Self::ToggleKeyboardShortcutsInhibit + } + 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), + niri_ipc::Action::ToggleWindowedFullscreen { id: None } => { + Self::ToggleWindowedFullscreen + } + niri_ipc::Action::ToggleWindowedFullscreen { id: Some(id) } => { + Self::ToggleWindowedFullscreenById(id) + } + niri_ipc::Action::FocusWindow { id } => Self::FocusWindow(id), + niri_ipc::Action::FocusWindowInColumn { index } => Self::FocusWindowInColumn(index), + niri_ipc::Action::FocusWindowPrevious {} => Self::FocusWindowPrevious, + 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, + niri_ipc::Action::FocusColumn { index } => Self::FocusColumn(index), + 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, + niri_ipc::Action::FocusWindowTop {} => Self::FocusWindowTop, + niri_ipc::Action::FocusWindowBottom {} => Self::FocusWindowBottom, + niri_ipc::Action::FocusWindowDownOrTop {} => Self::FocusWindowDownOrTop, + niri_ipc::Action::FocusWindowUpOrBottom {} => Self::FocusWindowUpOrBottom, + niri_ipc::Action::MoveColumnLeft {} => Self::MoveColumnLeft, + niri_ipc::Action::MoveColumnRight {} => Self::MoveColumnRight, + niri_ipc::Action::MoveColumnToFirst {} => Self::MoveColumnToFirst, + niri_ipc::Action::MoveColumnToLast {} => Self::MoveColumnToLast, + niri_ipc::Action::MoveColumnToIndex { index } => Self::MoveColumnToIndex(index), + niri_ipc::Action::MoveColumnLeftOrToMonitorLeft {} => { + Self::MoveColumnLeftOrToMonitorLeft + } + niri_ipc::Action::MoveColumnRightOrToMonitorRight {} => { + Self::MoveColumnRightOrToMonitorRight + } + niri_ipc::Action::MoveWindowDown {} => Self::MoveWindowDown, + niri_ipc::Action::MoveWindowUp {} => Self::MoveWindowUp, + niri_ipc::Action::MoveWindowDownOrToWorkspaceDown {} => { + Self::MoveWindowDownOrToWorkspaceDown + } + niri_ipc::Action::MoveWindowUpOrToWorkspaceUp {} => Self::MoveWindowUpOrToWorkspaceUp, + 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) + } + niri_ipc::Action::ConsumeWindowIntoColumn {} => Self::ConsumeWindowIntoColumn, + niri_ipc::Action::ExpelWindowFromColumn {} => Self::ExpelWindowFromColumn, + niri_ipc::Action::SwapWindowRight {} => Self::SwapWindowRight, + niri_ipc::Action::SwapWindowLeft {} => Self::SwapWindowLeft, + niri_ipc::Action::ToggleColumnTabbedDisplay {} => Self::ToggleColumnTabbedDisplay, + niri_ipc::Action::SetColumnDisplay { display } => Self::SetColumnDisplay(display), + niri_ipc::Action::CenterColumn {} => Self::CenterColumn, + niri_ipc::Action::CenterWindow { id: None } => Self::CenterWindow, + niri_ipc::Action::CenterWindow { id: Some(id) } => Self::CenterWindowById(id), + niri_ipc::Action::CenterVisibleColumns {} => Self::CenterVisibleColumns, + niri_ipc::Action::FocusWorkspaceDown {} => Self::FocusWorkspaceDown, + niri_ipc::Action::FocusWorkspaceUp {} => Self::FocusWorkspaceUp, + niri_ipc::Action::FocusWorkspace { reference } => { + Self::FocusWorkspace(WorkspaceReference::from(reference)) + } + niri_ipc::Action::FocusWorkspacePrevious {} => Self::FocusWorkspacePrevious, + niri_ipc::Action::MoveWindowToWorkspaceDown { focus } => { + Self::MoveWindowToWorkspaceDown(focus) + } + niri_ipc::Action::MoveWindowToWorkspaceUp { focus } => { + Self::MoveWindowToWorkspaceUp(focus) + } + niri_ipc::Action::MoveWindowToWorkspace { + window_id: None, + reference, + focus, + } => Self::MoveWindowToWorkspace(WorkspaceReference::from(reference), focus), + niri_ipc::Action::MoveWindowToWorkspace { + window_id: Some(window_id), + reference, + focus, + } => Self::MoveWindowToWorkspaceById { + window_id, + reference: WorkspaceReference::from(reference), + focus, + }, + 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) + } + niri_ipc::Action::MoveWorkspaceDown {} => Self::MoveWorkspaceDown, + niri_ipc::Action::MoveWorkspaceUp {} => Self::MoveWorkspaceUp, + 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)), + niri_ipc::Action::FocusMonitorLeft {} => Self::FocusMonitorLeft, + niri_ipc::Action::FocusMonitorRight {} => Self::FocusMonitorRight, + niri_ipc::Action::FocusMonitorDown {} => Self::FocusMonitorDown, + niri_ipc::Action::FocusMonitorUp {} => Self::FocusMonitorUp, + niri_ipc::Action::FocusMonitorPrevious {} => Self::FocusMonitorPrevious, + niri_ipc::Action::FocusMonitorNext {} => Self::FocusMonitorNext, + niri_ipc::Action::FocusMonitor { output } => Self::FocusMonitor(output), + niri_ipc::Action::MoveWindowToMonitorLeft {} => Self::MoveWindowToMonitorLeft, + niri_ipc::Action::MoveWindowToMonitorRight {} => Self::MoveWindowToMonitorRight, + niri_ipc::Action::MoveWindowToMonitorDown {} => Self::MoveWindowToMonitorDown, + niri_ipc::Action::MoveWindowToMonitorUp {} => Self::MoveWindowToMonitorUp, + niri_ipc::Action::MoveWindowToMonitorPrevious {} => Self::MoveWindowToMonitorPrevious, + niri_ipc::Action::MoveWindowToMonitorNext {} => Self::MoveWindowToMonitorNext, + niri_ipc::Action::MoveWindowToMonitor { id: None, output } => { + Self::MoveWindowToMonitor(output) + } + niri_ipc::Action::MoveWindowToMonitor { + id: Some(id), + output, + } => Self::MoveWindowToMonitorById { id, output }, + niri_ipc::Action::MoveColumnToMonitorLeft {} => Self::MoveColumnToMonitorLeft, + niri_ipc::Action::MoveColumnToMonitorRight {} => Self::MoveColumnToMonitorRight, + niri_ipc::Action::MoveColumnToMonitorDown {} => Self::MoveColumnToMonitorDown, + niri_ipc::Action::MoveColumnToMonitorUp {} => Self::MoveColumnToMonitorUp, + niri_ipc::Action::MoveColumnToMonitorPrevious {} => Self::MoveColumnToMonitorPrevious, + niri_ipc::Action::MoveColumnToMonitorNext {} => Self::MoveColumnToMonitorNext, + niri_ipc::Action::MoveColumnToMonitor { output } => Self::MoveColumnToMonitor(output), + niri_ipc::Action::SetWindowWidth { id: None, change } => Self::SetWindowWidth(change), + niri_ipc::Action::SetWindowWidth { + id: Some(id), + change, + } => Self::SetWindowWidthById { id, change }, + 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), + niri_ipc::Action::SwitchPresetColumnWidth {} => Self::SwitchPresetColumnWidth, + niri_ipc::Action::SwitchPresetWindowWidth { id: None } => Self::SwitchPresetWindowWidth, + niri_ipc::Action::SwitchPresetWindowWidth { id: Some(id) } => { + Self::SwitchPresetWindowWidthById(id) + } + niri_ipc::Action::SwitchPresetWindowHeight { id: None } => { + Self::SwitchPresetWindowHeight + } + niri_ipc::Action::SwitchPresetWindowHeight { id: Some(id) } => { + Self::SwitchPresetWindowHeightById(id) + } + niri_ipc::Action::MaximizeColumn {} => Self::MaximizeColumn, + niri_ipc::Action::SetColumnWidth { change } => Self::SetColumnWidth(change), + niri_ipc::Action::ExpandColumnToAvailableWidth {} => Self::ExpandColumnToAvailableWidth, + niri_ipc::Action::SwitchLayout { layout } => Self::SwitchLayout(layout), + 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, + niri_ipc::Action::MoveWorkspaceToMonitorPrevious {} => { + Self::MoveWorkspaceToMonitorPrevious + } + 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), + niri_ipc::Action::MoveWorkspaceToMonitorNext {} => Self::MoveWorkspaceToMonitorNext, + niri_ipc::Action::ToggleDebugTint {} => Self::ToggleDebugTint, + niri_ipc::Action::DebugToggleOpaqueRegions {} => Self::DebugToggleOpaqueRegions, + niri_ipc::Action::DebugToggleDamage {} => Self::DebugToggleDamage, + niri_ipc::Action::ToggleWindowFloating { id: None } => Self::ToggleWindowFloating, + niri_ipc::Action::ToggleWindowFloating { id: Some(id) } => { + Self::ToggleWindowFloatingById(id) + } + 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) + } + niri_ipc::Action::FocusFloating {} => Self::FocusFloating, + niri_ipc::Action::FocusTiling {} => Self::FocusTiling, + niri_ipc::Action::SwitchFocusBetweenFloatingAndTiling {} => { + Self::SwitchFocusBetweenFloatingAndTiling + } + niri_ipc::Action::MoveFloatingWindow { id, x, y } => { + Self::MoveFloatingWindowById { id, x, y } + } + niri_ipc::Action::ToggleWindowRuleOpacity { id: None } => Self::ToggleWindowRuleOpacity, + niri_ipc::Action::ToggleWindowRuleOpacity { id: Some(id) } => { + Self::ToggleWindowRuleOpacityById(id) + } + 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, + niri_ipc::Action::ToggleOverview {} => Self::ToggleOverview, + niri_ipc::Action::OpenOverview {} => Self::OpenOverview, + niri_ipc::Action::CloseOverview {} => Self::CloseOverview, + niri_ipc::Action::ToggleWindowUrgent { id } => Self::ToggleWindowUrgent(id), + niri_ipc::Action::SetWindowUrgent { id } => Self::SetWindowUrgent(id), + niri_ipc::Action::UnsetWindowUrgent { id } => Self::UnsetWindowUrgent(id), + niri_ipc::Action::LoadConfigFile {} => Self::LoadConfigFile, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum WorkspaceReference { + Id(u64), + Index(u8), + Name(String), +} + +impl From for WorkspaceReference { + fn from(reference: WorkspaceReferenceArg) -> WorkspaceReference { + match reference { + WorkspaceReferenceArg::Id(id) => Self::Id(id), + WorkspaceReferenceArg::Index(i) => Self::Index(i), + WorkspaceReferenceArg::Name(n) => Self::Name(n), + } + } +} + +impl knuffel::DecodeScalar for WorkspaceReference { + fn type_check( + type_name: &Option>, + ctx: &mut knuffel::decode::Context, + ) { + 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, + ctx: &mut knuffel::decode::Context, + ) -> Result> { + 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)) + } + } + } +} + +impl knuffel::Decode for Binds +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode, + ctx: &mut knuffel::decode::Context, + ) -> Result> { + 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 knuffel::Decode for Bind +where + S: knuffel::traits::ErrorSpan, +{ + fn decode_node( + node: &knuffel::ast::SpannedNode, + ctx: &mut knuffel::decode::Context, + ) -> Result> { + 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", + )); + } + + let key = node + .node_name + .parse::() + .map_err(|e| DecodeError::conversion(&node.node_name, e.wrap_err("invalid keybind")))?; + + let mut repeat = true; + let mut cooldown = None; + let mut allow_when_locked = false; + let mut allow_when_locked_node = None; + let mut allow_inhibiting = true; + let mut hotkey_overlay_title = None; + for (name, val) in &node.properties { + match &***name { + "repeat" => { + repeat = knuffel::traits::DecodeScalar::decode(val, ctx)?; + } + "cooldown-ms" => { + cooldown = Some(Duration::from_millis( + knuffel::traits::DecodeScalar::decode(val, ctx)?, + )); + } + "allow-when-locked" => { + allow_when_locked = knuffel::traits::DecodeScalar::decode(val, ctx)?; + allow_when_locked_node = Some(name); + } + "allow-inhibiting" => { + allow_inhibiting = knuffel::traits::DecodeScalar::decode(val, ctx)?; + } + "hotkey-overlay-title" => { + hotkey_overlay_title = Some(knuffel::traits::DecodeScalar::decode(val, ctx)?); + } + name_str => { + ctx.emit_error(DecodeError::unexpected( + name, + "property", + format!("unexpected property `{}`", name_str.escape_default()), + )); + } + } + } + + 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![]), + repeat: true, + cooldown: None, + allow_when_locked: false, + allow_inhibiting: true, + hotkey_overlay_title: None, + }; + + 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) { + Ok(action) => { + if !matches!(action, Action::Spawn(_) | Action::SpawnSh(_)) { + 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", + )); + } + } + + // The toggle-inhibit action must always be uninhibitable. + // Otherwise, it would be impossible to trigger it. + if matches!(action, Action::ToggleKeyboardShortcutsInhibit) { + allow_inhibiting = false; + } + + Ok(Self { + key, + action, + repeat, + cooldown, + allow_when_locked, + allow_inhibiting, + hotkey_overlay_title, + }) + } + Err(e) => { + ctx.emit_error(e); + Ok(dummy) + } + } + } else { + ctx.emit_error(DecodeError::missing( + node, + "expected an action for this keybind", + )); + Ok(dummy) + } + } +} + +impl FromStr for Key { + type Err = miette::Error; + + fn from_str(s: &str) -> Result { + 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; + } else if part.eq_ignore_ascii_case("iso_level3_shift") + || part.eq_ignore_ascii_case("mod5") + { + modifiers |= Modifiers::ISO_LEVEL3_SHIFT; + } else if part.eq_ignore_ascii_case("iso_level5_shift") + || part.eq_ignore_ascii_case("mod3") + { + modifiers |= Modifiers::ISO_LEVEL5_SHIFT; + } else { + return Err(miette!("invalid modifier: {part}")); + } + } + + 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") { + 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 + } 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 + } else { + let keysym = keysym_from_name(key, KEYSYM_CASE_INSENSITIVE); + if keysym.raw() == KEY_NoSymbol { + return Err(miette!("invalid key: {key}")); + } + Trigger::Keysym(keysym) + }; + + Ok(Key { trigger, modifiers }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_iso_level_shifts() { + assert_eq!( + "ISO_Level3_Shift+A".parse::().unwrap(), + Key { + trigger: Trigger::Keysym(Keysym::a), + modifiers: Modifiers::ISO_LEVEL3_SHIFT + }, + ); + assert_eq!( + "Mod5+A".parse::().unwrap(), + Key { + trigger: Trigger::Keysym(Keysym::a), + modifiers: Modifiers::ISO_LEVEL3_SHIFT + }, + ); + + assert_eq!( + "ISO_Level5_Shift+A".parse::().unwrap(), + Key { + trigger: Trigger::Keysym(Keysym::a), + modifiers: Modifiers::ISO_LEVEL5_SHIFT + }, + ); + assert_eq!( + "Mod3+A".parse::().unwrap(), + Key { + trigger: Trigger::Keysym(Keysym::a), + modifiers: Modifiers::ISO_LEVEL5_SHIFT + }, + ); + } +} diff --git a/niri-config/src/input.rs b/niri-config/src/input.rs index a5b546df..c5d4a038 100644 --- a/niri-config/src/input.rs +++ b/niri-config/src/input.rs @@ -4,7 +4,8 @@ use miette::miette; use smithay::input::keyboard::XkbConfig; use smithay::reexports::input; -use crate::{FloatOrInt, Modifiers, Percent}; +use crate::binds::Modifiers; +use crate::{FloatOrInt, Percent}; #[derive(knuffel::Decode, Debug, Default, PartialEq)] pub struct Input { diff --git a/niri-config/src/lib.rs b/niri-config/src/lib.rs index acc57cd6..1f44b041 100644 --- a/niri-config/src/lib.rs +++ b/niri-config/src/lib.rs @@ -1,26 +1,18 @@ #[macro_use] extern crate tracing; -use std::collections::HashSet; use std::ffi::OsStr; use std::fs::{self, File}; use std::io::Write; use std::path::{Path, PathBuf}; use std::str::FromStr; -use std::time::Duration; -use bitflags::bitflags; use knuffel::errors::DecodeError; use miette::{miette, Context, IntoDiagnostic}; -use niri_ipc::{ - ColumnDisplay, LayoutSwitchTarget, PositionChange, SizeChange, WorkspaceReferenceArg, -}; -use smithay::input::keyboard::keysyms::KEY_NoSymbol; -use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE}; -use smithay::input::keyboard::Keysym; pub mod animations; pub mod appearance; +pub mod binds; pub mod debug; pub mod gestures; pub mod input; @@ -34,6 +26,7 @@ pub use crate::animations::{ Animation, AnimationCurve, AnimationKind, Animations, EasingParams, SpringParams, }; pub use crate::appearance::*; +pub use crate::binds::*; pub use crate::debug::DebugConfig; pub use crate::gestures::Gestures; pub use crate::input::{Input, ModKey, ScrollMethod, TrackLayout, WarpMouseToFocusMode, Xkb}; @@ -217,663 +210,6 @@ pub struct Workspace { #[derive(Debug, Clone, PartialEq, Eq)] pub struct WorkspaceName(pub String); -#[derive(Debug, Default, PartialEq)] -pub struct Binds(pub Vec); - -#[derive(Debug, Clone, PartialEq)] -pub struct Bind { - pub key: Key, - pub action: Action, - pub repeat: bool, - pub cooldown: Option, - pub allow_when_locked: bool, - pub allow_inhibiting: bool, - pub hotkey_overlay_title: Option>, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct Key { - pub trigger: Trigger, - pub modifiers: Modifiers, -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub enum Trigger { - Keysym(Keysym), - MouseLeft, - MouseRight, - MouseMiddle, - MouseBack, - MouseForward, - WheelScrollDown, - WheelScrollUp, - WheelScrollLeft, - WheelScrollRight, - TouchpadScrollDown, - TouchpadScrollUp, - TouchpadScrollLeft, - TouchpadScrollRight, -} - -bitflags! { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] - pub struct Modifiers : u8 { - const CTRL = 1; - 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; - } -} - -#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq)] -pub struct SwitchBinds { - #[knuffel(child)] - pub lid_open: Option, - #[knuffel(child)] - pub lid_close: Option, - #[knuffel(child)] - pub tablet_mode_on: Option, - #[knuffel(child)] - pub tablet_mode_off: Option, -} - -#[derive(knuffel::Decode, Debug, Clone, PartialEq)] -pub struct SwitchAction { - #[knuffel(child, unwrap(arguments))] - pub spawn: Vec, -} - -// Remember to add new actions to the CLI enum too. -#[derive(knuffel::Decode, Debug, Clone, PartialEq)] -pub enum Action { - Quit(#[knuffel(property(name = "skip-confirmation"), default)] bool), - #[knuffel(skip)] - ChangeVt(i32), - Suspend, - PowerOffMonitors, - PowerOnMonitors, - ToggleDebugTint, - DebugToggleOpaqueRegions, - DebugToggleDamage, - Spawn(#[knuffel(arguments)] Vec), - SpawnSh(#[knuffel(argument)] String), - DoScreenTransition(#[knuffel(property(name = "delay-ms"))] Option), - #[knuffel(skip)] - ConfirmScreenshot { - write_to_disk: bool, - }, - #[knuffel(skip)] - CancelScreenshot, - #[knuffel(skip)] - ScreenshotTogglePointer, - 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, - ), - ScreenshotWindow(#[knuffel(property(name = "write-to-disk"), default = true)] bool), - #[knuffel(skip)] - ScreenshotWindowById { - id: u64, - write_to_disk: bool, - }, - ToggleKeyboardShortcutsInhibit, - CloseWindow, - #[knuffel(skip)] - CloseWindowById(u64), - FullscreenWindow, - #[knuffel(skip)] - FullscreenWindowById(u64), - ToggleWindowedFullscreen, - #[knuffel(skip)] - ToggleWindowedFullscreenById(u64), - #[knuffel(skip)] - FocusWindow(u64), - FocusWindowInColumn(#[knuffel(argument)] u8), - FocusWindowPrevious, - FocusColumnLeft, - #[knuffel(skip)] - FocusColumnLeftUnderMouse, - FocusColumnRight, - #[knuffel(skip)] - FocusColumnRightUnderMouse, - FocusColumnFirst, - FocusColumnLast, - FocusColumnRightOrFirst, - FocusColumnLeftOrLast, - FocusColumn(#[knuffel(argument)] usize), - FocusWindowOrMonitorUp, - FocusWindowOrMonitorDown, - FocusColumnOrMonitorLeft, - FocusColumnOrMonitorRight, - FocusWindowDown, - FocusWindowUp, - FocusWindowDownOrColumnLeft, - FocusWindowDownOrColumnRight, - FocusWindowUpOrColumnLeft, - FocusWindowUpOrColumnRight, - FocusWindowOrWorkspaceDown, - FocusWindowOrWorkspaceUp, - FocusWindowTop, - FocusWindowBottom, - FocusWindowDownOrTop, - FocusWindowUpOrBottom, - MoveColumnLeft, - MoveColumnRight, - MoveColumnToFirst, - MoveColumnToLast, - MoveColumnLeftOrToMonitorLeft, - MoveColumnRightOrToMonitorRight, - MoveColumnToIndex(#[knuffel(argument)] usize), - MoveWindowDown, - MoveWindowUp, - MoveWindowDownOrToWorkspaceDown, - MoveWindowUpOrToWorkspaceUp, - ConsumeOrExpelWindowLeft, - #[knuffel(skip)] - ConsumeOrExpelWindowLeftById(u64), - ConsumeOrExpelWindowRight, - #[knuffel(skip)] - ConsumeOrExpelWindowRightById(u64), - ConsumeWindowIntoColumn, - ExpelWindowFromColumn, - SwapWindowLeft, - SwapWindowRight, - ToggleColumnTabbedDisplay, - SetColumnDisplay(#[knuffel(argument, str)] ColumnDisplay), - CenterColumn, - CenterWindow, - #[knuffel(skip)] - CenterWindowById(u64), - CenterVisibleColumns, - FocusWorkspaceDown, - #[knuffel(skip)] - FocusWorkspaceDownUnderMouse, - FocusWorkspaceUp, - #[knuffel(skip)] - FocusWorkspaceUpUnderMouse, - FocusWorkspace(#[knuffel(argument)] WorkspaceReference), - FocusWorkspacePrevious, - MoveWindowToWorkspaceDown(#[knuffel(property(name = "focus"), default = true)] bool), - MoveWindowToWorkspaceUp(#[knuffel(property(name = "focus"), default = true)] bool), - MoveWindowToWorkspace( - #[knuffel(argument)] WorkspaceReference, - #[knuffel(property(name = "focus"), default = true)] bool, - ), - #[knuffel(skip)] - MoveWindowToWorkspaceById { - window_id: u64, - reference: WorkspaceReference, - focus: bool, - }, - 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, - ), - MoveWorkspaceDown, - MoveWorkspaceUp, - MoveWorkspaceToIndex(#[knuffel(argument)] usize), - #[knuffel(skip)] - MoveWorkspaceToIndexByRef { - new_idx: usize, - reference: WorkspaceReference, - }, - #[knuffel(skip)] - MoveWorkspaceToMonitorByRef { - output_name: String, - reference: WorkspaceReference, - }, - MoveWorkspaceToMonitor(#[knuffel(argument)] String), - SetWorkspaceName(#[knuffel(argument)] String), - #[knuffel(skip)] - SetWorkspaceNameByRef { - name: String, - reference: WorkspaceReference, - }, - UnsetWorkspaceName, - #[knuffel(skip)] - UnsetWorkSpaceNameByRef(#[knuffel(argument)] WorkspaceReference), - FocusMonitorLeft, - FocusMonitorRight, - FocusMonitorDown, - FocusMonitorUp, - FocusMonitorPrevious, - FocusMonitorNext, - FocusMonitor(#[knuffel(argument)] String), - MoveWindowToMonitorLeft, - MoveWindowToMonitorRight, - MoveWindowToMonitorDown, - MoveWindowToMonitorUp, - MoveWindowToMonitorPrevious, - MoveWindowToMonitorNext, - MoveWindowToMonitor(#[knuffel(argument)] String), - #[knuffel(skip)] - MoveWindowToMonitorById { - id: u64, - output: String, - }, - MoveColumnToMonitorLeft, - MoveColumnToMonitorRight, - MoveColumnToMonitorDown, - MoveColumnToMonitorUp, - MoveColumnToMonitorPrevious, - MoveColumnToMonitorNext, - MoveColumnToMonitor(#[knuffel(argument)] String), - SetWindowWidth(#[knuffel(argument, str)] SizeChange), - #[knuffel(skip)] - SetWindowWidthById { - id: u64, - change: SizeChange, - }, - SetWindowHeight(#[knuffel(argument, str)] SizeChange), - #[knuffel(skip)] - SetWindowHeightById { - id: u64, - change: SizeChange, - }, - ResetWindowHeight, - #[knuffel(skip)] - ResetWindowHeightById(u64), - SwitchPresetColumnWidth, - SwitchPresetWindowWidth, - #[knuffel(skip)] - SwitchPresetWindowWidthById(u64), - SwitchPresetWindowHeight, - #[knuffel(skip)] - SwitchPresetWindowHeightById(u64), - MaximizeColumn, - SetColumnWidth(#[knuffel(argument, str)] SizeChange), - ExpandColumnToAvailableWidth, - SwitchLayout(#[knuffel(argument, str)] LayoutSwitchTarget), - ShowHotkeyOverlay, - MoveWorkspaceToMonitorLeft, - MoveWorkspaceToMonitorRight, - MoveWorkspaceToMonitorDown, - MoveWorkspaceToMonitorUp, - MoveWorkspaceToMonitorPrevious, - MoveWorkspaceToMonitorNext, - ToggleWindowFloating, - #[knuffel(skip)] - ToggleWindowFloatingById(u64), - MoveWindowToFloating, - #[knuffel(skip)] - MoveWindowToFloatingById(u64), - MoveWindowToTiling, - #[knuffel(skip)] - MoveWindowToTilingById(u64), - FocusFloating, - FocusTiling, - SwitchFocusBetweenFloatingAndTiling, - #[knuffel(skip)] - MoveFloatingWindowById { - id: Option, - x: PositionChange, - y: PositionChange, - }, - ToggleWindowRuleOpacity, - #[knuffel(skip)] - ToggleWindowRuleOpacityById(u64), - SetDynamicCastWindow, - #[knuffel(skip)] - SetDynamicCastWindowById(u64), - SetDynamicCastMonitor(#[knuffel(argument)] Option), - ClearDynamicCastTarget, - ToggleOverview, - OpenOverview, - CloseOverview, - #[knuffel(skip)] - ToggleWindowUrgent(u64), - #[knuffel(skip)] - SetWindowUrgent(u64), - #[knuffel(skip)] - UnsetWindowUrgent(u64), - #[knuffel(skip)] - LoadConfigFile, -} - -impl From for Action { - fn from(value: niri_ipc::Action) -> Self { - match value { - niri_ipc::Action::Quit { skip_confirmation } => Self::Quit(skip_confirmation), - niri_ipc::Action::PowerOffMonitors {} => Self::PowerOffMonitors, - niri_ipc::Action::PowerOnMonitors {} => Self::PowerOnMonitors, - niri_ipc::Action::Spawn { command } => Self::Spawn(command), - niri_ipc::Action::SpawnSh { command } => Self::SpawnSh(command), - niri_ipc::Action::DoScreenTransition { delay_ms } => Self::DoScreenTransition(delay_ms), - 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), - 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 }, - niri_ipc::Action::ToggleKeyboardShortcutsInhibit {} => { - Self::ToggleKeyboardShortcutsInhibit - } - 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), - niri_ipc::Action::ToggleWindowedFullscreen { id: None } => { - Self::ToggleWindowedFullscreen - } - niri_ipc::Action::ToggleWindowedFullscreen { id: Some(id) } => { - Self::ToggleWindowedFullscreenById(id) - } - niri_ipc::Action::FocusWindow { id } => Self::FocusWindow(id), - niri_ipc::Action::FocusWindowInColumn { index } => Self::FocusWindowInColumn(index), - niri_ipc::Action::FocusWindowPrevious {} => Self::FocusWindowPrevious, - 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, - niri_ipc::Action::FocusColumn { index } => Self::FocusColumn(index), - 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, - niri_ipc::Action::FocusWindowTop {} => Self::FocusWindowTop, - niri_ipc::Action::FocusWindowBottom {} => Self::FocusWindowBottom, - niri_ipc::Action::FocusWindowDownOrTop {} => Self::FocusWindowDownOrTop, - niri_ipc::Action::FocusWindowUpOrBottom {} => Self::FocusWindowUpOrBottom, - niri_ipc::Action::MoveColumnLeft {} => Self::MoveColumnLeft, - niri_ipc::Action::MoveColumnRight {} => Self::MoveColumnRight, - niri_ipc::Action::MoveColumnToFirst {} => Self::MoveColumnToFirst, - niri_ipc::Action::MoveColumnToLast {} => Self::MoveColumnToLast, - niri_ipc::Action::MoveColumnToIndex { index } => Self::MoveColumnToIndex(index), - niri_ipc::Action::MoveColumnLeftOrToMonitorLeft {} => { - Self::MoveColumnLeftOrToMonitorLeft - } - niri_ipc::Action::MoveColumnRightOrToMonitorRight {} => { - Self::MoveColumnRightOrToMonitorRight - } - niri_ipc::Action::MoveWindowDown {} => Self::MoveWindowDown, - niri_ipc::Action::MoveWindowUp {} => Self::MoveWindowUp, - niri_ipc::Action::MoveWindowDownOrToWorkspaceDown {} => { - Self::MoveWindowDownOrToWorkspaceDown - } - niri_ipc::Action::MoveWindowUpOrToWorkspaceUp {} => Self::MoveWindowUpOrToWorkspaceUp, - 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) - } - niri_ipc::Action::ConsumeWindowIntoColumn {} => Self::ConsumeWindowIntoColumn, - niri_ipc::Action::ExpelWindowFromColumn {} => Self::ExpelWindowFromColumn, - niri_ipc::Action::SwapWindowRight {} => Self::SwapWindowRight, - niri_ipc::Action::SwapWindowLeft {} => Self::SwapWindowLeft, - niri_ipc::Action::ToggleColumnTabbedDisplay {} => Self::ToggleColumnTabbedDisplay, - niri_ipc::Action::SetColumnDisplay { display } => Self::SetColumnDisplay(display), - niri_ipc::Action::CenterColumn {} => Self::CenterColumn, - niri_ipc::Action::CenterWindow { id: None } => Self::CenterWindow, - niri_ipc::Action::CenterWindow { id: Some(id) } => Self::CenterWindowById(id), - niri_ipc::Action::CenterVisibleColumns {} => Self::CenterVisibleColumns, - niri_ipc::Action::FocusWorkspaceDown {} => Self::FocusWorkspaceDown, - niri_ipc::Action::FocusWorkspaceUp {} => Self::FocusWorkspaceUp, - niri_ipc::Action::FocusWorkspace { reference } => { - Self::FocusWorkspace(WorkspaceReference::from(reference)) - } - niri_ipc::Action::FocusWorkspacePrevious {} => Self::FocusWorkspacePrevious, - niri_ipc::Action::MoveWindowToWorkspaceDown { focus } => { - Self::MoveWindowToWorkspaceDown(focus) - } - niri_ipc::Action::MoveWindowToWorkspaceUp { focus } => { - Self::MoveWindowToWorkspaceUp(focus) - } - niri_ipc::Action::MoveWindowToWorkspace { - window_id: None, - reference, - focus, - } => Self::MoveWindowToWorkspace(WorkspaceReference::from(reference), focus), - niri_ipc::Action::MoveWindowToWorkspace { - window_id: Some(window_id), - reference, - focus, - } => Self::MoveWindowToWorkspaceById { - window_id, - reference: WorkspaceReference::from(reference), - focus, - }, - 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) - } - niri_ipc::Action::MoveWorkspaceDown {} => Self::MoveWorkspaceDown, - niri_ipc::Action::MoveWorkspaceUp {} => Self::MoveWorkspaceUp, - 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)), - niri_ipc::Action::FocusMonitorLeft {} => Self::FocusMonitorLeft, - niri_ipc::Action::FocusMonitorRight {} => Self::FocusMonitorRight, - niri_ipc::Action::FocusMonitorDown {} => Self::FocusMonitorDown, - niri_ipc::Action::FocusMonitorUp {} => Self::FocusMonitorUp, - niri_ipc::Action::FocusMonitorPrevious {} => Self::FocusMonitorPrevious, - niri_ipc::Action::FocusMonitorNext {} => Self::FocusMonitorNext, - niri_ipc::Action::FocusMonitor { output } => Self::FocusMonitor(output), - niri_ipc::Action::MoveWindowToMonitorLeft {} => Self::MoveWindowToMonitorLeft, - niri_ipc::Action::MoveWindowToMonitorRight {} => Self::MoveWindowToMonitorRight, - niri_ipc::Action::MoveWindowToMonitorDown {} => Self::MoveWindowToMonitorDown, - niri_ipc::Action::MoveWindowToMonitorUp {} => Self::MoveWindowToMonitorUp, - niri_ipc::Action::MoveWindowToMonitorPrevious {} => Self::MoveWindowToMonitorPrevious, - niri_ipc::Action::MoveWindowToMonitorNext {} => Self::MoveWindowToMonitorNext, - niri_ipc::Action::MoveWindowToMonitor { id: None, output } => { - Self::MoveWindowToMonitor(output) - } - niri_ipc::Action::MoveWindowToMonitor { - id: Some(id), - output, - } => Self::MoveWindowToMonitorById { id, output }, - niri_ipc::Action::MoveColumnToMonitorLeft {} => Self::MoveColumnToMonitorLeft, - niri_ipc::Action::MoveColumnToMonitorRight {} => Self::MoveColumnToMonitorRight, - niri_ipc::Action::MoveColumnToMonitorDown {} => Self::MoveColumnToMonitorDown, - niri_ipc::Action::MoveColumnToMonitorUp {} => Self::MoveColumnToMonitorUp, - niri_ipc::Action::MoveColumnToMonitorPrevious {} => Self::MoveColumnToMonitorPrevious, - niri_ipc::Action::MoveColumnToMonitorNext {} => Self::MoveColumnToMonitorNext, - niri_ipc::Action::MoveColumnToMonitor { output } => Self::MoveColumnToMonitor(output), - niri_ipc::Action::SetWindowWidth { id: None, change } => Self::SetWindowWidth(change), - niri_ipc::Action::SetWindowWidth { - id: Some(id), - change, - } => Self::SetWindowWidthById { id, change }, - 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::ResetWindowHei