mirror of
https://github.com/YaLTeR/niri.git
synced 2025-10-06 00:23:14 +02:00
config: Extract binds
This commit is contained in:
975
niri-config/src/binds.rs
Normal file
975
niri-config/src/binds.rs
Normal file
@@ -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<Bind>);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Bind {
|
||||
pub key: Key,
|
||||
pub action: Action,
|
||||
pub repeat: bool,
|
||||
pub cooldown: Option<Duration>,
|
||||
pub allow_when_locked: bool,
|
||||
pub allow_inhibiting: bool,
|
||||
pub hotkey_overlay_title: Option<Option<String>>,
|
||||
}
|
||||
|
||||
#[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<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>,
|
||||
}
|
||||
|
||||
// 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<String>),
|
||||
SpawnSh(#[knuffel(argument)] String),
|
||||
DoScreenTransition(#[knuffel(property(name = "delay-ms"))] Option<u16>),
|
||||
#[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<u64>,
|
||||
x: PositionChange,
|
||||
y: PositionChange,
|
||||
},
|
||||
ToggleWindowRuleOpacity,
|
||||
#[knuffel(skip)]
|
||||
ToggleWindowRuleOpacityById(u64),
|
||||
SetDynamicCastWindow,
|
||||
#[knuffel(skip)]
|
||||
SetDynamicCastWindowById(u64),
|
||||
SetDynamicCastMonitor(#[knuffel(argument)] Option<String>),
|
||||
ClearDynamicCastTarget,
|
||||
ToggleOverview,
|
||||
OpenOverview,
|
||||
CloseOverview,
|
||||
#[knuffel(skip)]
|
||||
ToggleWindowUrgent(u64),
|
||||
#[knuffel(skip)]
|
||||
SetWindowUrgent(u64),
|
||||
#[knuffel(skip)]
|
||||
UnsetWindowUrgent(u64),
|
||||
#[knuffel(skip)]
|
||||
LoadConfigFile,
|
||||
}
|
||||
|
||||
impl From<niri_ipc::Action> 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<WorkspaceReferenceArg> 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<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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>> {
|
||||
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::<Key>()
|
||||
.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<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;
|
||||
} 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::<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
|
||||
},
|
||||
);
|
||||
|
||||
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
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user