From 9c5554560012aa95239843a57a68c75d422fe144 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Mon, 16 Aug 2021 10:25:01 -0700 Subject: [PATCH] refactor(komorebic): update clap, add cli docs The latest clap beta introduced a lot of breaking changes for komorebic, so I decided it was a good time to refactor a little and add documentation to all of the cli commands. The primary change for komorebic is that subcommands now only take structs as arguments, so every enum must be wrapped in a struct. Some macros have been introduced to ease this. Using on|off alongside enable|disable for BooleanState arguments has been deprecated, going forward only enable|disable will be supported. The commands to introduce float rules have been refactored to make use of ApplicationTarget, and a single command 'float-rule' has been introduced in the cli. Finally I took some time to standardise the sample AHK config a little, primarily making sure that command prompt windows are never shown for any of the configuration commands. BREAKING CHANGE: float-exe, float-class, and float-title have been deprecated in favour of float-rule in komorebic. workspace-tiling now only accepts enable|disable as valid inputs to the final arg, deprecating the previously also valid on|off. re #8 --- Cargo.lock | 22 +- README.md | 10 +- bindings/build.rs | 3 +- komorebi-core/Cargo.toml | 2 +- komorebi-core/src/cycle_direction.rs | 5 +- komorebi-core/src/layout.rs | 8 +- komorebi-core/src/lib.rs | 8 +- komorebi-core/src/operation_direction.rs | 5 +- komorebi.sample.ahk | 103 +++--- komorebi/src/process_command.rs | 1 + komorebic/Cargo.toml | 3 +- komorebic/src/main.rs | 426 ++++++++++++++--------- 12 files changed, 342 insertions(+), 254 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3820d61d..d4430d43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da1976d75adbe5fbc88130ecd119529cf1cc6a93ae1546d8696ee66f0d21af1" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.0.0-beta.2" +version = "3.0.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" +checksum = "fcd70aa5597dbc42f7217a543f9ef2768b2ef823ba29036072d30e1d88e98406" dependencies = [ "atty", "bitflags", @@ -117,15 +117,14 @@ dependencies = [ "strsim", "termcolor", "textwrap", - "unicode-width", "vec_map", ] [[package]] name = "clap_derive" -version = "3.0.0-beta.2" +version = "3.0.0-beta.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" +checksum = "0b5bb0d655624a0b8770d1c178fb8ffcb1f91cc722cb08f451e3dc72465421ac" dependencies = [ "heck", "proc-macro-error", @@ -490,6 +489,7 @@ dependencies = [ "color-eyre", "dirs", "komorebi-core", + "paste", "powershell_script", "serde", "serde_json", @@ -706,9 +706,9 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "os_str_bytes" -version = "2.4.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb2e1c3ee07430c2cf76151675e583e0f19985fa6efae47d6848a3e2c824f85" +checksum = "6acbef58a60fe69ab50510a55bc8cdd4d6cf2283d27ad338f54cb52747a9cf2d" [[package]] name = "owo-colors" @@ -1089,9 +1089,9 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.12.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" +checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" dependencies = [ "unicode-width", ] diff --git a/README.md b/README.md index 7884076f..14bd7a88 100644 --- a/README.md +++ b/README.md @@ -88,9 +88,9 @@ If you have AutoHotKey installed and a `komorebi.ahk` file in your home director PowerShell prompt to find your home directory), `komorebi` will automatically try to load it when starting. If you are experiencing behaviour where -[closing a window leaves a blank tile, but minimizing the same window does not](https://github.com/LGUG2Z/komorebi/issues/6), -you have probably enabled a 'close/minimize to tray' option for that application. You can tell _komorebi_ to handle this -application appropriately by identifying it via the executable name or the window class: +[closing a window leaves a blank tile, but minimizing the same window does not](https://github.com/LGUG2Z/komorebi/issues/6) +, you have probably enabled a 'close/minimize to tray' option for that application. You can tell _komorebi_ to handle +this application appropriately by identifying it via the executable name or the window class: ```powershell komorebic.exe identify-tray-application exe Discord.exe @@ -103,6 +103,10 @@ As previously mentioned, this project does not handle anything related to keybin personally use AutoHotKey to manage my window management shortcuts, and have provided a sample [komorebi.ahk](komorebi.sample.ahk) AHK script that you can use as a starting point for your own. +You can run `komorebic.exe` to get a full list of the commands that you can use to customise `komorebi` and create +keybindings with. You can run `komorebic.exe --help` to get a full explanation of the arguments required for +each command. + ## Features - [x] Multi-monitor diff --git a/bindings/build.rs b/bindings/build.rs index 80b65a0b..3c98669a 100644 --- a/bindings/build.rs +++ b/bindings/build.rs @@ -18,7 +18,8 @@ fn main() { Windows::Win32::System::Threading::AttachThreadInput, Windows::Win32::System::Threading::GetCurrentProcessId, Windows::Win32::UI::KeyboardAndMouseInput::SetFocus, - Windows::Win32::UI::Accessibility::{SetWinEventHook, HWINEVENTHOOK}, + Windows::Win32::UI::Accessibility::SetWinEventHook, + Windows::Win32::UI::Accessibility::HWINEVENTHOOK, // error: `Windows.Win32.UI.WindowsAndMessaging.GWL_EXSTYLE` not found in metadata Windows::Win32::UI::WindowsAndMessaging::*, ); diff --git a/komorebi-core/Cargo.toml b/komorebi-core/Cargo.toml index 83210cc4..d95c40a0 100644 --- a/komorebi-core/Cargo.toml +++ b/komorebi-core/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] bindings = { package = "bindings", path = "../bindings" } -clap = "3.0.0-beta.2" +clap = "3.0.0-beta.4" color-eyre = "0.5" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/komorebi-core/src/cycle_direction.rs b/komorebi-core/src/cycle_direction.rs index fd11664b..521c804d 100644 --- a/komorebi-core/src/cycle_direction.rs +++ b/komorebi-core/src/cycle_direction.rs @@ -1,12 +1,11 @@ -use clap::Clap; +use clap::ArgEnum; use serde::Deserialize; use serde::Serialize; use strum::Display; use strum::EnumString; -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] -#[derive(Clap)] pub enum CycleDirection { Previous, Next, diff --git a/komorebi-core/src/layout.rs b/komorebi-core/src/layout.rs index 7dc401ab..a5960fca 100644 --- a/komorebi-core/src/layout.rs +++ b/komorebi-core/src/layout.rs @@ -1,6 +1,6 @@ use std::num::NonZeroUsize; -use clap::Clap; +use clap::ArgEnum; use serde::Deserialize; use serde::Serialize; use strum::Display; @@ -10,18 +10,16 @@ use crate::OperationDirection; use crate::Rect; use crate::Sizing; -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] -#[derive(Clap)] pub enum Layout { BSP, Columns, Rows, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] -#[derive(Clap)] pub enum LayoutFlip { Horizontal, Vertical, diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 27621c6e..631b7407 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use clap::Clap; +use clap::ArgEnum; use color_eyre::Result; use serde::Deserialize; use serde::Serialize; @@ -80,16 +80,16 @@ impl FromStr for SocketMessage { } } -#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] pub enum ApplicationIdentifier { Exe, Class, + Title, } -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] -#[derive(Clap)] pub enum Sizing { Increase, Decrease, diff --git a/komorebi-core/src/operation_direction.rs b/komorebi-core/src/operation_direction.rs index 4913e24f..4e19281f 100644 --- a/komorebi-core/src/operation_direction.rs +++ b/komorebi-core/src/operation_direction.rs @@ -1,4 +1,4 @@ -use clap::Clap; +use clap::ArgEnum; use serde::Deserialize; use serde::Serialize; use strum::Display; @@ -7,9 +7,8 @@ use strum::EnumString; use crate::Layout; use crate::LayoutFlip; -#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString)] +#[derive(Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] -#[derive(Clap)] pub enum OperationDirection { Left, Right, diff --git a/komorebi.sample.ahk b/komorebi.sample.ahk index 9f4918b7..4dea28f6 100644 --- a/komorebi.sample.ahk +++ b/komorebi.sample.ahk @@ -1,50 +1,49 @@ #SingleInstance Force ; Enable hot reloading of changes to this file -Run, komorebic.exe watch-configuration enable +Run, komorebic.exe watch-configuration enable, , Hide ; Enable focus follows mouse -Run, komorebic.exe focus-follows-mouse enable +Run, komorebic.exe focus-follows-mouse enable, , Hide ; Ensure there are 3 workspaces created on monitor 0 -Run, komorebic.exe ensure-workspaces 0 5 +Run, komorebic.exe ensure-workspaces 0 5, , Hide ; Give the workspaces some optional names -Run, komorebic.exe workspace-name 0 0 bsp -Run, komorebic.exe workspace-name 0 1 columns -Run, komorebic.exe workspace-name 0 2 thicc -Run, komorebic.exe workspace-name 0 3 matrix -Run, komorebic.exe workspace-name 0 4 floaty +Run, komorebic.exe workspace-name 0 0 bsp, , Hide +Run, komorebic.exe workspace-name 0 1 columns, , Hide +Run, komorebic.exe workspace-name 0 2 thicc, , Hide +Run, komorebic.exe workspace-name 0 3 matrix, , Hide +Run, komorebic.exe workspace-name 0 4 floaty, , Hide ; Set the padding of the different workspaces -Run, komorebic.exe workspace-padding 0 1 30 -Run, komorebic.exe container-padding 0 1 30 -Run, komorebic.exe workspace-padding 0 2 200 -Run, komorebic.exe workspace-padding 0 3 0 -Run, komorebic.exe container-padding 0 3 0 +Run, komorebic.exe workspace-padding 0 1 30, , Hide +Run, komorebic.exe container-padding 0 1 30, , Hide +Run, komorebic.exe workspace-padding 0 2 200, , Hide +Run, komorebic.exe workspace-padding 0 3 0, , Hide +Run, komorebic.exe container-padding 0 3 0, , Hide ; Set the layouts of different workspaces -Run, komorebic.exe workspace-layout 0 1 columns +Run, komorebic.exe workspace-layout 0 1 columns, , Hide ; Set the floaty layout to not tile any windows -Run, komorebic.exe workspace-tiling 0 4 off +Run, komorebic.exe workspace-tiling 0 4 disable, , Hide ; Always float IntelliJ popups, matching on class -Run, komorebic.exe float-class SunAwtDialog, , Hide +Run, komorebic.exe float-rule class SunAwtDialog, , Hide ; Always float Control Panel, matching on title -Run, komorebic.exe float-title "Control Panel", , Hide +Run, komorebic.exe float-rule title "Control Panel", , Hide ; Always float Task Manager, matching on class -Run, komorebic.exe float-class TaskManagerWindow, , Hide +Run, komorebic.exe float-rule class TaskManagerWindow, , Hide ; Always float Wally, matching on executable name -Run, komorebic.exe float-exe Wally.exe, , Hide -Run, komorebic.exe float-exe wincompose.exe, , Hide +Run, komorebic.exe float-rule exe Wally.exe, , Hide +Run, komorebic.exe float-rule exe wincompose.exe, , Hide ; Always float Calculator app, matching on window title -Run, komorebic.exe float-title Calculator, , Hide -Run, komorebic.exe float-exe 1Password.exe, , Hide +Run, komorebic.exe float-rule title Calculator, , Hide +Run, komorebic.exe float-rule exe 1Password.exe, , Hide ; Identify applications that close to the tray Run, komorebic.exe identify-tray-application exe Discord.exe, , Hide -Run, komorebic.exe identify-tray-application exe Telegram.exe, , Hide ; Change the focused window, Alt + Vim direction keys !h:: @@ -65,36 +64,36 @@ return ; Move the focused window in a given direction, Alt + Shift + Vim direction keys !+h:: -Run, komorebic.exe move left, Hide +Run, komorebic.exe move left, , Hide return !+j:: -Run, komorebic.exe move down, Hide +Run, komorebic.exe move down, , Hide return !+k:: -Run, komorebic.exe move up, Hide +Run, komorebic.exe move up, , Hide return !+l:: -Run, komorebic.exe move right, Hide +Run, komorebic.exe move right, , Hide return ; Stack the focused window in a given direction, Alt + Shift + direction keys !+Left:: -Run, komorebic.exe stack left, Hide +Run, komorebic.exe stack left, , Hide return !+Down:: -Run, komorebic.exe stack down, Hide +Run, komorebic.exe stack down, , Hide return !+Up:: -Run, komorebic.exe stack up, Hide +Run, komorebic.exe stack up, , Hide return !+Right:: -Run, komorebic.exe stack right, Hide +Run, komorebic.exe stack right, , Hide return !]:: @@ -107,102 +106,102 @@ return ; Unstack the focused window, Alt + Shift + D !+d:: -Run, komorebic.exe unstack, Hide +Run, komorebic.exe unstack, , Hide return ; Promote the focused window to the top of the tree, Alt + Shift + Enter !+Enter:: -Run, komorebic.exe promote, Hide +Run, komorebic.exe promote, , Hide return ; Switch to an equal-width, max-height column layout on the main workspace, Alt + Shift + C !+c:: -Run, komorebic.exe workspace-layout 0 0 columns, Hide +Run, komorebic.exe workspace-layout 0 0 columns, , Hide return ; Switch to the default bsp tiling layout on the main workspace, Alt + Shift + T !+t:: -Run, komorebic.exe workspace-layout 0 0 bsp, Hide +Run, komorebic.exe workspace-layout 0 0 bsp, , Hide return ; Toggle the Monocle layout for the focused window, Alt + Shift + F !+f:: -Run, komorebic.exe toggle-monocle, Hide +Run, komorebic.exe toggle-monocle, , Hide return ; Flip horizontally, Alt + X !x:: -Run, komorebic.exe flip-layout horizontal, Hide +Run, komorebic.exe flip-layout horizontal, , Hide return ; Flip vertically, Alt + Y !y:: -Run, komorebic.exe flip-layout vertical, Hide +Run, komorebic.exe flip-layout vertical, , Hide return ; Force a retile if things get janky, Alt + Shift + R !+r:: -Run, komorebic.exe retile, Hide +Run, komorebic.exe retile, , Hide return ; Float the focused window, Alt + T !t:: -Run, komorebic.exe toggle-float, Hide +Run, komorebic.exe toggle-float, , Hide return ; Reload ~/komorebi.ahk, Alt + O !o:: -Run, komorebic.exe reload-configuration, Hide +Run, komorebic.exe reload-configuration, , Hide return ; Pause responding to any window events or komorebic commands, Alt + P !p:: -Run, komorebic.exe toggle-pause, Hide +Run, komorebic.exe toggle-pause, , Hide return ; Switch to workspace !1:: Send ! -Run, komorebic.exe focus-workspace 0, Hide +Run, komorebic.exe focus-workspace 0, , Hide return !2:: Send ! -Run, komorebic.exe focus-workspace 1, Hide +Run, komorebic.exe focus-workspace 1, , Hide return !3:: Send ! -Run, komorebic.exe focus-workspace 2, Hide +Run, komorebic.exe focus-workspace 2, , Hide return !4:: Send ! -Run, komorebic.exe focus-workspace 3, Hide +Run, komorebic.exe focus-workspace 3, , Hide return !5:: Send ! -Run, komorebic.exe focus-workspace 4, Hide +Run, komorebic.exe focus-workspace 4, , Hide return ; Move window to workspace !+1:: -Run, komorebic.exe move-to-workspace 0, Hide +Run, komorebic.exe move-to-workspace 0, , Hide return !+2:: -Run, komorebic.exe move-to-workspace 1, Hide +Run, komorebic.exe move-to-workspace 1, , Hide return !+3:: -Run, komorebic.exe move-to-workspace 2, Hide +Run, komorebic.exe move-to-workspace 2, , Hide return !+4:: -Run, komorebic.exe move-to-workspace 3, Hide +Run, komorebic.exe move-to-workspace 3, , Hide return !+5:: -Run, komorebic.exe move-to-workspace 4, Hide +Run, komorebic.exe move-to-workspace 4, , Hide return diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 4ce3d65f..9fa9fda7 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -193,6 +193,7 @@ impl WindowManager { classes.push(id); } } + ApplicationIdentifier::Title => {} }, } diff --git a/komorebic/Cargo.toml b/komorebic/Cargo.toml index b460c1a4..5f505687 100644 --- a/komorebic/Cargo.toml +++ b/komorebic/Cargo.toml @@ -9,9 +9,10 @@ edition = "2018" bindings = { package = "bindings", path = "../bindings" } komorebi-core = { path = "../komorebi-core" } -clap = "3.0.0-beta.2" +clap = "3.0.0-beta.4" color-eyre = "0.5" dirs = "3" +paste = "1" powershell_script = "0.2" serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 65ce1c9e..19f0f099 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -4,6 +4,8 @@ use std::io::BufReader; use std::io::ErrorKind; use std::io::Write; +use clap::AppSettings; +use clap::ArgEnum; use clap::Clap; use color_eyre::eyre::ContextCompat; use color_eyre::Result; @@ -22,8 +24,129 @@ use komorebi_core::OperationDirection; use komorebi_core::Sizing; use komorebi_core::SocketMessage; +#[derive(ArgEnum)] +enum BooleanState { + Enable, + Disable, +} + +impl From for bool { + fn from(b: BooleanState) -> Self { + match b { + BooleanState::Enable => true, + BooleanState::Disable => false, + } + } +} + +macro_rules! gen_enum_subcommand { + ($name:ident, $element:ty) => { + paste::paste! { + #[derive(clap::Clap)] + pub struct $name { + #[clap(arg_enum)] + [<$element:snake>]: $element + } + } + }; +} + +gen_enum_subcommand!(Focus, OperationDirection); +gen_enum_subcommand!(Move, OperationDirection); +gen_enum_subcommand!(Stack, OperationDirection); +gen_enum_subcommand!(CycleStack, CycleDirection); +gen_enum_subcommand!(WatchConfiguration, BooleanState); +gen_enum_subcommand!(FlipLayout, LayoutFlip); +gen_enum_subcommand!(FocusFollowsMouse, BooleanState); + +macro_rules! gen_target_subcommands { + ( $( $name:ident ),+ ) => { + $( + #[derive(clap::Clap)] + pub struct $name { + target: usize, + } + )+ + }; +} + +gen_target_subcommands!(MoveToMonitor, MoveToWorkspace, FocusMonitor, FocusWorkspace); + #[derive(Clap)] -#[clap(version = "1.0", author = "Jade Iqbal ")] +struct Resize { + #[clap(arg_enum)] + edge: OperationDirection, + #[clap(arg_enum)] + sizing: Sizing, +} + +#[derive(Clap)] +struct EnsureWorkspaces { + /// Monitor index (zero-indexed, ie. the primary/first monitor is 0) + monitor: usize, + /// Number of desired workspaces + workspace_count: usize, +} + +#[derive(Clap)] +struct SizeForMonitorWorkspace { + /// Monitor index (zero-indexed, ie. the primary/first monitor is 0) + monitor: usize, + /// Workspace index on the specified monitor (zero-indexed, ie. the first workspace is 0) + workspace: usize, + /// Pixels to pad with as an integer + size: i32, +} + +#[derive(Clap)] +struct WorkspaceName { + /// Monitor index (zero-indexed, ie. the primary/first monitor is 0) + monitor: usize, + /// Workspace index on the specified monitor (zero-indexed, ie. the first workspace is 0) + workspace: usize, + /// Name of the workspace as a string + value: String, +} + +#[derive(Clap)] +struct WorkspaceLayout { + /// Monitor index (zero-indexed, ie. the primary/first monitor is 0) + monitor: usize, + /// Workspace index on the specified monitor (zero-indexed, ie. the first workspace is 0) + workspace: usize, + #[clap(arg_enum)] + layout: Layout, +} + +#[derive(Clap)] +struct WorkspaceTiling { + /// Monitor index (zero-indexed, ie. the primary/first monitor is 0) + monitor: usize, + /// Workspace index on the specified monitor (zero-indexed, ie. the first workspace is 0) + workspace: usize, + #[clap(arg_enum)] + tile: BooleanState, +} + +#[derive(Clap)] +struct ApplicationTarget { + #[clap(arg_enum)] + identifier: ApplicationIdentifier, + /// Identifier as a string + id: String, +} + +#[derive(Clap)] +struct PaddingAdjustment { + #[clap(arg_enum)] + sizing: Sizing, + /// Pixels to adjust by as an integer + adjustment: i32, +} + +#[derive(Clap)] +#[clap(version = "0.1.0", author = "Jade Iqbal ")] +#[clap(setting = AppSettings::DeriveDisplayOrder)] struct Opts { #[clap(subcommand)] subcmd: SubCommand, @@ -31,123 +154,96 @@ struct Opts { #[derive(Clap)] enum SubCommand { - Focus(OperationDirection), - Move(OperationDirection), - Stack(OperationDirection), - Resize(Resize), - Unstack, - CycleStack(CycleDirection), - MoveToMonitor(Target), - MoveToWorkspace(Target), - FocusMonitor(Target), - FocusWorkspace(Target), - NewWorkspace, - Promote, - EnsureWorkspaces(WorkspaceCountForMonitor), - Retile, - ContainerPadding(SizeForMonitorWorkspace), - WorkspacePadding(SizeForMonitorWorkspace), - WorkspaceLayout(LayoutForMonitorWorkspace), - WorkspaceTiling(TilingForMonitorWorkspace), - WorkspaceName(NameForMonitorWorkspace), - ToggleTiling, - ToggleFloat, - TogglePause, - ToggleMonocle, - RestoreWindows, - ReloadConfiguration, - WatchConfiguration(BooleanState), - State, + /// Start komorebi.exe as a background process Start, + /// Stop the komorebi.exe process and restore all hidden windows Stop, - FloatClass(FloatTarget), - FloatExe(FloatTarget), - FloatTitle(FloatTarget), + /// Show a JSON representation of the current window manager state + State, + /// Change focus to the window in the specified direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + Focus(Focus), + /// Move the focused window in the specified direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + Move(Move), + /// Stack the focused window in the specified direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + Stack(Stack), + /// Resize the focused window in the specified direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + Resize(Resize), + /// Unstack the focused window + Unstack, + /// Cycle the focused stack in the specified cycle direction + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + CycleStack(CycleStack), + /// Move the focused window to the specified monitor + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + MoveToMonitor(MoveToMonitor), + /// Move the focused window to the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + MoveToWorkspace(MoveToWorkspace), + /// Focus the specified monitor + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + FocusMonitor(FocusMonitor), + /// Focus the specified workspace on the focused monitor + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + FocusWorkspace(FocusWorkspace), + /// Create and append a new workspace on the focused monitor + NewWorkspace, + /// Adjust container padding on the focused workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + AdjustContainerPadding(PaddingAdjustment), + /// Adjust workspace padding on the focused workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + AdjustWorkspacePadding(PaddingAdjustment), + /// Flip the layout on the focused workspace (BSP only) + FlipLayout(FlipLayout), + /// Promote the focused window to the top of the tree + Promote, + /// Force the retiling of all managed windows + Retile, + /// Create at least this many workspaces for the specified monitor + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + EnsureWorkspaces(EnsureWorkspaces), + /// Set the container padding for the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + ContainerPadding(SizeForMonitorWorkspace), + /// Set the workspace padding for the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WorkspacePadding(SizeForMonitorWorkspace), + /// Set the layout for the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WorkspaceLayout(WorkspaceLayout), + /// Enable or disable window tiling for the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WorkspaceTiling(WorkspaceTiling), + /// Set the workspace name for the specified workspace + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WorkspaceName(WorkspaceName), + /// Toggle the window manager on and off across all monitors + TogglePause, + /// Toggle window tiling on the focused workspace + ToggleTiling, + /// Toggle floating mode for the focused window + ToggleFloat, + /// Toggle monocle mode for the focused container + ToggleMonocle, + /// Restore all hidden windows (debugging command) + RestoreWindows, + /// Reload ~/komorebi.ahk (if it exists) + ReloadConfiguration, + /// Toggle the automatic reloading of ~/komorebi.ahk (if it exists) + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + WatchConfiguration(WatchConfiguration), + /// Add a rule to always float the specified application + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + FloatRule(ApplicationTarget), + /// Identify an application that closes to the system tray + #[clap(setting = AppSettings::ArgRequiredElseHelp)] IdentifyTrayApplication(ApplicationTarget), - AdjustContainerPadding(SizingAdjustment), - AdjustWorkspacePadding(SizingAdjustment), - FlipLayout(LayoutFlip), - FocusFollowsMouse(BooleanState), -} - -#[derive(Clap)] -struct WorkspaceCountForMonitor { - monitor: usize, - workspace_count: usize, -} - -#[derive(Clap)] -struct SizeForMonitorWorkspace { - monitor: usize, - workspace: usize, - size: i32, -} - -#[derive(Clap)] -struct NameForMonitorWorkspace { - monitor: usize, - workspace: usize, - value: String, -} - -#[derive(Clap)] -struct LayoutForMonitorWorkspace { - monitor: usize, - workspace: usize, - layout: Layout, -} - -fn on_or_off(s: &str) -> Result { - match s { - "enable" => Ok(true), - "disable" => Ok(false), - // in order to not break backwards compat for mouse follows focus - "on" => Ok(true), - "off" => Ok(false), - _ => Err("expected `enable` or `disable`"), - } -} - -#[derive(Clap)] -struct TilingForMonitorWorkspace { - monitor: usize, - workspace: usize, - #[clap(parse(try_from_str = on_or_off))] - tile: bool, -} - -#[derive(Clap)] -struct Target { - number: usize, -} - -#[derive(Clap)] -struct SizingAdjustment { - sizing: Sizing, - adjustment: i32, -} - -#[derive(Clap)] -struct FloatTarget { - id: String, -} - -#[derive(Clap)] -struct ApplicationTarget { - identifier: ApplicationIdentifier, - id: String, -} - -#[derive(Clap)] -struct Resize { - edge: OperationDirection, - sizing: Sizing, -} - -#[derive(Clap)] -enum BooleanState { - Enable, - Disable, + /// Enable or disable focus follows mouse for the operating system + FocusFollowsMouse(FocusFollowsMouse), } pub fn send_message(bytes: &[u8]) -> Result<()> { @@ -163,8 +259,8 @@ fn main() -> Result<()> { let opts: Opts = Opts::parse(); match opts.subcmd { - SubCommand::Focus(direction) => { - send_message(&*SocketMessage::FocusWindow(direction).as_bytes()?)?; + SubCommand::Focus(arg) => { + send_message(&*SocketMessage::FocusWindow(arg.operation_direction).as_bytes()?)?; } SubCommand::Promote => { send_message(&*SocketMessage::Promote.as_bytes()?)?; @@ -175,47 +271,35 @@ fn main() -> Result<()> { SubCommand::Retile => { send_message(&*SocketMessage::Retile.as_bytes()?)?; } - SubCommand::Move(direction) => { - send_message(&*SocketMessage::MoveWindow(direction).as_bytes()?)?; + SubCommand::Move(arg) => { + send_message(&*SocketMessage::MoveWindow(arg.operation_direction).as_bytes()?)?; } - SubCommand::MoveToMonitor(display) => { - send_message( - &*SocketMessage::MoveContainerToMonitorNumber(display.number).as_bytes()?, - )?; + SubCommand::MoveToMonitor(arg) => { + send_message(&*SocketMessage::MoveContainerToMonitorNumber(arg.target).as_bytes()?)?; } - SubCommand::MoveToWorkspace(workspace) => { - send_message( - &*SocketMessage::MoveContainerToWorkspaceNumber(workspace.number).as_bytes()?, - )?; + SubCommand::MoveToWorkspace(arg) => { + send_message(&*SocketMessage::MoveContainerToWorkspaceNumber(arg.target).as_bytes()?)?; } - SubCommand::ContainerPadding(gap) => { + SubCommand::ContainerPadding(arg) => { send_message( - &*SocketMessage::ContainerPadding(gap.monitor, gap.workspace, gap.size) + &*SocketMessage::ContainerPadding(arg.monitor, arg.workspace, arg.size) .as_bytes()?, )?; } - SubCommand::WorkspacePadding(gap) => { + SubCommand::WorkspacePadding(arg) => { send_message( - &*SocketMessage::WorkspacePadding(gap.monitor, gap.workspace, gap.size) + &*SocketMessage::WorkspacePadding(arg.monitor, arg.workspace, arg.size) .as_bytes()?, )?; } - SubCommand::AdjustWorkspacePadding(sizing_adjustment) => { + SubCommand::AdjustWorkspacePadding(arg) => { send_message( - &*SocketMessage::AdjustWorkspacePadding( - sizing_adjustment.sizing, - sizing_adjustment.adjustment, - ) - .as_bytes()?, + &*SocketMessage::AdjustWorkspacePadding(arg.sizing, arg.adjustment).as_bytes()?, )?; } - SubCommand::AdjustContainerPadding(sizing_adjustment) => { + SubCommand::AdjustContainerPadding(arg) => { send_message( - &*SocketMessage::AdjustContainerPadding( - sizing_adjustment.sizing, - sizing_adjustment.adjustment, - ) - .as_bytes()?, + &*SocketMessage::AdjustContainerPadding(arg.sizing, arg.adjustment).as_bytes()?, )?; } SubCommand::ToggleTiling => { @@ -227,15 +311,15 @@ fn main() -> Result<()> { SubCommand::ToggleMonocle => { send_message(&*SocketMessage::ToggleMonocle.as_bytes()?)?; } - SubCommand::WorkspaceLayout(layout) => { + SubCommand::WorkspaceLayout(arg) => { send_message( - &*SocketMessage::WorkspaceLayout(layout.monitor, layout.workspace, layout.layout) + &*SocketMessage::WorkspaceLayout(arg.monitor, arg.workspace, arg.layout) .as_bytes()?, )?; } - SubCommand::WorkspaceTiling(layout) => { + SubCommand::WorkspaceTiling(arg) => { send_message( - &*SocketMessage::WorkspaceTiling(layout.monitor, layout.workspace, layout.tile) + &*SocketMessage::WorkspaceTiling(arg.monitor, arg.workspace, arg.tile.into()) .as_bytes()?, )?; } @@ -253,32 +337,34 @@ fn main() -> Result<()> { SubCommand::Stop => { send_message(&*SocketMessage::Stop.as_bytes()?)?; } - SubCommand::FloatClass(target) => { - send_message(&*SocketMessage::FloatClass(target.id).as_bytes()?)?; - } - SubCommand::FloatExe(target) => { - send_message(&*SocketMessage::FloatExe(target.id).as_bytes()?)?; - } - SubCommand::FloatTitle(target) => { - send_message(&*SocketMessage::FloatTitle(target.id).as_bytes()?)?; - } - SubCommand::Stack(direction) => { - send_message(&*SocketMessage::StackWindow(direction).as_bytes()?)?; + SubCommand::FloatRule(arg) => match arg.identifier { + ApplicationIdentifier::Exe => { + send_message(&*SocketMessage::FloatExe(arg.id).as_bytes()?)?; + } + ApplicationIdentifier::Class => { + send_message(&*SocketMessage::FloatClass(arg.id).as_bytes()?)?; + } + ApplicationIdentifier::Title => { + send_message(&*SocketMessage::FloatTitle(arg.id).as_bytes()?)?; + } + }, + SubCommand::Stack(arg) => { + send_message(&*SocketMessage::StackWindow(arg.operation_direction).as_bytes()?)?; } SubCommand::Unstack => { send_message(&*SocketMessage::UnstackWindow.as_bytes()?)?; } - SubCommand::CycleStack(direction) => { - send_message(&*SocketMessage::CycleStack(direction).as_bytes()?)?; + SubCommand::CycleStack(arg) => { + send_message(&*SocketMessage::CycleStack(arg.cycle_direction).as_bytes()?)?; } - SubCommand::FlipLayout(flip) => { - send_message(&*SocketMessage::FlipLayout(flip).as_bytes()?)?; + SubCommand::FlipLayout(arg) => { + send_message(&*SocketMessage::FlipLayout(arg.layout_flip).as_bytes()?)?; } - SubCommand::FocusMonitor(target) => { - send_message(&*SocketMessage::FocusMonitorNumber(target.number).as_bytes()?)?; + SubCommand::FocusMonitor(arg) => { + send_message(&*SocketMessage::FocusMonitorNumber(arg.target).as_bytes()?)?; } - SubCommand::FocusWorkspace(target) => { - send_message(&*SocketMessage::FocusWorkspaceNumber(target.number).as_bytes()?)?; + SubCommand::FocusWorkspace(arg) => { + send_message(&*SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?; } SubCommand::NewWorkspace => { send_message(&*SocketMessage::NewWorkspace.as_bytes()?)?; @@ -344,8 +430,8 @@ fn main() -> Result<()> { SubCommand::Resize(resize) => { send_message(&*SocketMessage::ResizeWindow(resize.edge, resize.sizing).as_bytes()?)?; } - SubCommand::FocusFollowsMouse(enable) => { - let enable = match enable { + SubCommand::FocusFollowsMouse(arg) => { + let enable = match arg.boolean_state { BooleanState::Enable => true, BooleanState::Disable => false, }; @@ -355,8 +441,8 @@ fn main() -> Result<()> { SubCommand::ReloadConfiguration => { send_message(&*SocketMessage::ReloadConfiguration.as_bytes()?)?; } - SubCommand::WatchConfiguration(enable) => { - let enable = match enable { + SubCommand::WatchConfiguration(arg) => { + let enable = match arg.boolean_state { BooleanState::Enable => true, BooleanState::Disable => false, };