Skip to content

Commit

Permalink
feat(subscriptions): embed latest state
Browse files Browse the repository at this point in the history
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
  • Loading branch information
LGUG2Z committed Oct 27, 2021
1 parent 5d0806a commit 29a6c39
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 19 deletions.
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 17 additions & 0 deletions komorebi/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
9 changes: 7 additions & 2 deletions komorebi/src/process_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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(())
Expand Down
7 changes: 6 additions & 1 deletion komorebi/src/process_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(())
Expand Down
13 changes: 8 additions & 5 deletions komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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!());
}
}
}
}

Expand Down

0 comments on commit 29a6c39

Please sign in to comment.