mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-05-07 03:13:31 +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:
22
README.md
22
README.md
@@ -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>
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -70,9 +70,8 @@ pub struct State {
|
||||
pub border_overflow_identifiers: Vec<String>,
|
||||
}
|
||||
|
||||
#[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!());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user