diff --git a/README.md b/README.md index 766e5867..95d1e54e 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ each command. start Start komorebi.exe as a background process stop Stop the komorebi.exe process and restore all hidden windows state Show a JSON representation of the current window manager state +query Query the current window manager state log Tail komorebi.exe's process logs (cancel with Ctrl-C) focus Change focus to the window in the specified direction move Move the focused window in the specified direction @@ -270,6 +271,7 @@ used [is available here](komorebi.sample.with.lib.ahk). - [x] Watch configuration for changes - [x] Helper library for AutoHotKey - [x] View window manager state +- [x] Query window manager state ## Development diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index b13c453e..eb04c497 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -67,6 +67,7 @@ pub enum SocketMessage { ManageRule(ApplicationIdentifier, String), IdentifyTrayApplication(ApplicationIdentifier, String), State, + Query(StateQuery), FocusFollowsMouse(bool), ToggleFocusFollowsMouse, } @@ -89,6 +90,15 @@ impl FromStr for SocketMessage { } } +#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] +#[strum(serialize_all = "snake_case")] +pub enum StateQuery { + FocusedMonitorIndex, + FocusedWorkspaceIndex, + FocusedContainerIndex, + FocusedWindowIndex, +} + #[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum)] #[strum(serialize_all = "snake_case")] pub enum ApplicationIdentifier { diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index ab1b9876..f6f3c66d 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -12,6 +12,7 @@ use uds_windows::UnixStream; use komorebi_core::ApplicationIdentifier; use komorebi_core::SocketMessage; +use komorebi_core::StateQuery; use crate::window_manager; use crate::window_manager::WindowManager; @@ -175,6 +176,30 @@ impl WindowManager { let mut stream = UnixStream::connect(&socket)?; stream.write_all(state.as_bytes())?; } + SocketMessage::Query(query) => { + let response = match query { + StateQuery::FocusedMonitorIndex => self.focused_monitor_idx(), + StateQuery::FocusedWorkspaceIndex => self + .focused_monitor() + .ok_or_else(|| anyhow!("there is no monitor"))? + .focused_workspace_idx(), + StateQuery::FocusedContainerIndex => { + self.focused_workspace()?.focused_container_idx() + } + StateQuery::FocusedWindowIndex => { + self.focused_container()?.focused_window_idx() + } + } + .to_string(); + + let mut socket = + dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?; + socket.push("komorebic.sock"); + let socket = socket.as_path(); + + let mut stream = UnixStream::connect(&socket)?; + stream.write_all(response.as_bytes())?; + } SocketMessage::ResizeWindow(direction, sizing) => { self.resize_window(direction, sizing, Option::from(50))?; } diff --git a/komorebic.lib.sample.ahk b/komorebic.lib.sample.ahk index b5cd58d1..c11c6b52 100644 --- a/komorebic.lib.sample.ahk +++ b/komorebic.lib.sample.ahk @@ -12,6 +12,10 @@ State() { Run, komorebic.exe state, , Hide } +Query(state_query) { + Run, komorebic.exe query %state_query%, , Hide +} + Log() { Run, komorebic.exe log, , Hide } diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 94958ad8..9bb78194 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -34,6 +34,7 @@ use komorebi_core::Layout; use komorebi_core::OperationDirection; use komorebi_core::Sizing; use komorebi_core::SocketMessage; +use komorebi_core::StateQuery; trait AhkLibrary { fn generate_ahk_library() -> String; @@ -82,6 +83,7 @@ gen_enum_subcommand_args! { ChangeLayout: Layout, WatchConfiguration: BooleanState, FocusFollowsMouse: BooleanState, + Query: StateQuery, } macro_rules! gen_target_subcommand_args { @@ -246,6 +248,9 @@ enum SubCommand { Stop, /// Show a JSON representation of the current window manager state State, + /// Query the current window manager state + #[clap(setting = AppSettings::ArgRequiredElseHelp)] + Query(Query), /// Tail komorebi.exe's process logs (cancel with Ctrl-C) Log, /// Change focus to the window in the specified direction @@ -607,6 +612,40 @@ fn main() -> Result<()> { } } } + SubCommand::Query(arg) => { + let home = dirs::home_dir().context("there is no home directory")?; + let mut socket = home; + socket.push("komorebic.sock"); + let socket = socket.as_path(); + + match std::fs::remove_file(&socket) { + Ok(_) => {} + Err(error) => match error.kind() { + // Doing this because ::exists() doesn't work reliably on Windows via IntelliJ + ErrorKind::NotFound => {} + _ => { + return Err(error.into()); + } + }, + }; + + send_message(&*SocketMessage::Query(arg.state_query).as_bytes()?)?; + + let listener = UnixListener::bind(&socket)?; + match listener.accept() { + Ok(incoming) => { + let stream = BufReader::new(incoming.0); + for line in stream.lines() { + println!("{}", line?); + } + + return Ok(()); + } + Err(error) => { + panic!("{}", error); + } + } + } SubCommand::RestoreWindows => { let mut hwnd_json = dirs::home_dir().context("there is no home directory")?; hwnd_json.push("komorebi.hwnd.json");