diff --git a/README.md b/README.md index 5301fdf8..5d9dccb3 100644 --- a/README.md +++ b/README.md @@ -465,21 +465,21 @@ First, your application must create a named pipe. Once the named pipe has been c komorebic.exe subscribe ``` -Note that you do not have to incldue the full path of the named pipe, just the name. +Note that you do not have to include the full path of the named pipe, just the name. If the named pipe exists, `komorebi` will start pushing JSON data of successfully handled events and messages: ```json lines -{"type":"AddSubscriber","content":"test-pipe"} -{"type":"FocusWindow","content":"Up"} -{"type":"FocusChange","content":["SystemForeground",{"hwnd":1443930,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]} -{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":2624200,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]} -{"type":"FocusWindow","content":"Left"} -{"type":"FocusChange","content":["SystemForeground",{"hwnd":2558668,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":13,"top":60,"right":1520,"bottom":1655}}]} -{"type":"FocusWindow","content":"Right"} -{"type":"FocusChange","content":["SystemForeground",{"hwnd":1443930,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]} -{"type":"FocusWindow","content":"Down"} -{"type":"FocusChange","content":["SystemForeground",{"hwnd":67344,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":894,"right":757,"bottom":821}}]} +{"event":{"type":"AddSubscriber","content":"yasb"},"state":{...}} +{"event":{"type":"FocusWindow","content":"Left"},"state":{...}} +{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":131444,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":13,"top":60,"right":1520,"bottom":1655}}]},"state":{...}} +{"event":{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":5572450,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]},"state":{...}} +{"event":{"type":"FocusWindow","content":"Right"},"state":{...}} +{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{}...} +{"event":{"type":"FocusWindow","content":"Down"},"state":{...}} +{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":329264,"title":"den — Mozilla Firefox","exe":"firefox.exe","class":"MozillaWindowClass","rect":{"left":1539,"top":894,"right":1520,"bottom":821}}]},"state":{...}} +{"event":{"type":"FocusWindow","content":"Up"},"state":{...}} +{"event":{"type":"FocusChange","content":["SystemForeground",{"hwnd":132968,"title":"Windows PowerShell","exe":"WindowsTerminal.exe","class":"CASCADIA_HOSTING_WINDOW_CLASS","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]},"state":{...}} ``` You may then filter on the `type` key to listen to the events that you are interested in. For a full list of possible diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index b0d95a06..f3dceb7a 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -22,15 +22,19 @@ use lazy_static::lazy_static; #[cfg(feature = "deadlock_detection")] use parking_lot::deadlock; use parking_lot::Mutex; +use serde::Serialize; use sysinfo::SystemExt; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::EnvFilter; use which::which; +use komorebi_core::SocketMessage; + use crate::process_command::listen_for_commands; use crate::process_event::listen_for_events; use crate::process_movement::listen_for_movements; +use crate::window_manager::State; use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; @@ -186,6 +190,19 @@ pub fn load_configuration() -> Result<()> { Ok(()) } +#[derive(Debug, Serialize)] +#[serde(untagged)] +pub enum NotificationEvent { + WindowManager(WindowManagerEvent), + Socket(SocketMessage), +} + +#[derive(Debug, Serialize)] +pub struct Notification { + pub event: NotificationEvent, + pub state: State, +} + pub fn notify_subscribers(notification: &str) -> Result<()> { let mut stale_subscriptions = vec![]; let mut subscriptions = SUBSCRIPTION_PIPES.lock(); diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index b455a595..4f281e39 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -24,6 +24,8 @@ use crate::notify_subscribers; use crate::window_manager; use crate::window_manager::WindowManager; use crate::windows_api::WindowsApi; +use crate::Notification; +use crate::NotificationEvent; use crate::BORDER_OVERFLOW_IDENTIFIERS; use crate::CUSTOM_FFM; use crate::FLOAT_IDENTIFIERS; @@ -221,7 +223,7 @@ impl WindowManager { self.set_workspace_name(monitor_idx, workspace_idx, name)?; } SocketMessage::State => { - let state = serde_json::to_string_pretty(&window_manager::State::from(self))?; + let state = serde_json::to_string_pretty(&window_manager::State::from(&*self))?; let mut socket = dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?; socket.push("komorebic.sock"); @@ -476,7 +478,10 @@ impl WindowManager { } self.process_command(message.clone())?; - notify_subscribers(&serde_json::to_string(&message)?)?; + notify_subscribers(&serde_json::to_string(&Notification { + event: NotificationEvent::Socket(message.clone()), + state: (&*self).into(), + })?)?; } Ok(()) diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 25357186..3073e429 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -15,6 +15,8 @@ use crate::notify_subscribers; use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; +use crate::Notification; +use crate::NotificationEvent; use crate::HIDDEN_HWNDS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; @@ -317,7 +319,10 @@ impl WindowManager { .open(hwnd_json)?; serde_json::to_writer_pretty(&file, &known_hwnds)?; - notify_subscribers(&serde_json::to_string(&event)?)?; + notify_subscribers(&serde_json::to_string(&Notification { + event: NotificationEvent::WindowManager(*event), + state: (&*self).into(), + })?)?; tracing::info!("processed: {}", event.window().to_string()); Ok(()) diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 7b43500d..5a816444 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -70,9 +70,8 @@ pub struct State { pub border_overflow_identifiers: Vec, } -#[allow(clippy::fallible_impl_from)] -impl From<&mut WindowManager> for State { - fn from(wm: &mut WindowManager) -> Self { +impl From<&WindowManager> for State { + fn from(wm: &WindowManager) -> Self { Self { monitors: wm.monitors.clone(), is_paused: wm.is_paused, @@ -567,8 +566,12 @@ impl WindowManager { // Calling this directly instead of the window.focus() wrapper because trying to // attach to the thread of the desktop window always seems to result in "Access is // denied (os error 5)" - WindowsApi::set_foreground_window(desktop_window.hwnd()) - .map_err(|error| anyhow!("{} {}:{}", error, file!(), line!()))?; + match WindowsApi::set_foreground_window(desktop_window.hwnd()) { + Ok(_) => {} + Err(error) => { + tracing::warn!("{} {}:{}", error, file!(), line!()); + } + } } }