mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-07-05 04:31:37 +02:00
feat(subscriptions): embed latest state
This commit embeds the latest window manager state (as returned from 'komorebic.exe state') as part of the event notifications sent to subscribers. Separately, WindowManager.update_focused_workspace has been refactored to allow a failure to set the foreground window to the default desktop window on an empty workspace to log a warning instead of returning an error, allowing messages previously impacted by this to run to conclusion and be surfaced in the event notifications stream. resolve #56
This commit is contained in:
@@ -465,21 +465,21 @@ First, your application must create a named pipe. Once the named pipe has been c
|
|||||||
komorebic.exe subscribe <your pipe name>
|
komorebic.exe subscribe <your pipe name>
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
If the named pipe exists, `komorebi` will start pushing JSON data of successfully handled events and messages:
|
||||||
|
|
||||||
```json lines
|
```json lines
|
||||||
{"type":"AddSubscriber","content":"test-pipe"}
|
{"event":{"type":"AddSubscriber","content":"yasb"},"state":{...}}
|
||||||
{"type":"FocusWindow","content":"Up"}
|
{"event":{"type":"FocusWindow","content":"Left"},"state":{...}}
|
||||||
{"type":"FocusChange","content":["SystemForeground",{"hwnd":1443930,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]}
|
{"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":{...}}
|
||||||
{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":2624200,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]}
|
{"event":{"type":"MonitorPoll","content":["ObjectCreate",{"hwnd":5572450,"title":"OLEChannelWnd","exe":"explorer.exe","class":"OleMainThreadWndClass","rect":{"left":0,"top":0,"right":0,"bottom":0}}]},"state":{...}}
|
||||||
{"type":"FocusWindow","content":"Left"}
|
{"event":{"type":"FocusWindow","content":"Right"},"state":{...}}
|
||||||
{"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}}]}
|
{"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":{}...}
|
||||||
{"type":"FocusWindow","content":"Right"}
|
{"event":{"type":"FocusWindow","content":"Down"},"state":{...}}
|
||||||
{"type":"FocusChange","content":["SystemForeground",{"hwnd":1443930,"title":"komorebi – README.md","exe":"idea64.exe","class":"SunAwtFrame","rect":{"left":1539,"top":60,"right":1520,"bottom":821}}]}
|
{"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":{...}}
|
||||||
{"type":"FocusWindow","content":"Down"}
|
{"event":{"type":"FocusWindow","content":"Up"},"state":{...}}
|
||||||
{"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":"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
|
You may then filter on the `type` key to listen to the events that you are interested in. For a full list of possible
|
||||||
|
|||||||
@@ -22,15 +22,19 @@ use lazy_static::lazy_static;
|
|||||||
#[cfg(feature = "deadlock_detection")]
|
#[cfg(feature = "deadlock_detection")]
|
||||||
use parking_lot::deadlock;
|
use parking_lot::deadlock;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use serde::Serialize;
|
||||||
use sysinfo::SystemExt;
|
use sysinfo::SystemExt;
|
||||||
use tracing_appender::non_blocking::WorkerGuard;
|
use tracing_appender::non_blocking::WorkerGuard;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
use which::which;
|
use which::which;
|
||||||
|
|
||||||
|
use komorebi_core::SocketMessage;
|
||||||
|
|
||||||
use crate::process_command::listen_for_commands;
|
use crate::process_command::listen_for_commands;
|
||||||
use crate::process_event::listen_for_events;
|
use crate::process_event::listen_for_events;
|
||||||
use crate::process_movement::listen_for_movements;
|
use crate::process_movement::listen_for_movements;
|
||||||
|
use crate::window_manager::State;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
@@ -186,6 +190,19 @@ pub fn load_configuration() -> Result<()> {
|
|||||||
Ok(())
|
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<()> {
|
pub fn notify_subscribers(notification: &str) -> Result<()> {
|
||||||
let mut stale_subscriptions = vec![];
|
let mut stale_subscriptions = vec![];
|
||||||
let mut subscriptions = SUBSCRIPTION_PIPES.lock();
|
let mut subscriptions = SUBSCRIPTION_PIPES.lock();
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ use crate::notify_subscribers;
|
|||||||
use crate::window_manager;
|
use crate::window_manager;
|
||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
|
use crate::Notification;
|
||||||
|
use crate::NotificationEvent;
|
||||||
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
use crate::BORDER_OVERFLOW_IDENTIFIERS;
|
||||||
use crate::CUSTOM_FFM;
|
use crate::CUSTOM_FFM;
|
||||||
use crate::FLOAT_IDENTIFIERS;
|
use crate::FLOAT_IDENTIFIERS;
|
||||||
@@ -221,7 +223,7 @@ impl WindowManager {
|
|||||||
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
|
self.set_workspace_name(monitor_idx, workspace_idx, name)?;
|
||||||
}
|
}
|
||||||
SocketMessage::State => {
|
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 =
|
let mut socket =
|
||||||
dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?;
|
dirs::home_dir().ok_or_else(|| anyhow!("there is no home directory"))?;
|
||||||
socket.push("komorebic.sock");
|
socket.push("komorebic.sock");
|
||||||
@@ -476,7 +478,10 @@ impl WindowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.process_command(message.clone())?;
|
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(())
|
Ok(())
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use crate::notify_subscribers;
|
|||||||
use crate::window_manager::WindowManager;
|
use crate::window_manager::WindowManager;
|
||||||
use crate::window_manager_event::WindowManagerEvent;
|
use crate::window_manager_event::WindowManagerEvent;
|
||||||
use crate::windows_api::WindowsApi;
|
use crate::windows_api::WindowsApi;
|
||||||
|
use crate::Notification;
|
||||||
|
use crate::NotificationEvent;
|
||||||
use crate::HIDDEN_HWNDS;
|
use crate::HIDDEN_HWNDS;
|
||||||
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||||
|
|
||||||
@@ -317,7 +319,10 @@ impl WindowManager {
|
|||||||
.open(hwnd_json)?;
|
.open(hwnd_json)?;
|
||||||
|
|
||||||
serde_json::to_writer_pretty(&file, &known_hwnds)?;
|
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());
|
tracing::info!("processed: {}", event.window().to_string());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -70,9 +70,8 @@ pub struct State {
|
|||||||
pub border_overflow_identifiers: Vec<String>,
|
pub border_overflow_identifiers: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::fallible_impl_from)]
|
impl From<&WindowManager> for State {
|
||||||
impl From<&mut WindowManager> for State {
|
fn from(wm: &WindowManager) -> Self {
|
||||||
fn from(wm: &mut WindowManager) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
monitors: wm.monitors.clone(),
|
monitors: wm.monitors.clone(),
|
||||||
is_paused: wm.is_paused,
|
is_paused: wm.is_paused,
|
||||||
@@ -567,8 +566,12 @@ impl WindowManager {
|
|||||||
// Calling this directly instead of the window.focus() wrapper because trying to
|
// 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
|
// attach to the thread of the desktop window always seems to result in "Access is
|
||||||
// denied (os error 5)"
|
// denied (os error 5)"
|
||||||
WindowsApi::set_foreground_window(desktop_window.hwnd())
|
match WindowsApi::set_foreground_window(desktop_window.hwnd()) {
|
||||||
.map_err(|error| anyhow!("{} {}:{}", error, file!(), line!()))?;
|
Ok(_) => {}
|
||||||
|
Err(error) => {
|
||||||
|
tracing::warn!("{} {}:{}", error, file!(), line!());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user