Skip to content

Commit

Permalink
feat(client): add subscribe_with_options
Browse files Browse the repository at this point in the history
This commit adds a new method, subscribe_with_options to
komorebi-client.

The first option introduced is to tell komorebi to only send
notifications when the window manager state has been changed during the
processing of an event.

This new subscription option is now used with komorebi-bar to improve
rendering and update performance.
  • Loading branch information
LGUG2Z committed Oct 13, 2024
1 parent d21ffb2 commit 5da72e1
Show file tree
Hide file tree
Showing 8 changed files with 176 additions and 29 deletions.
5 changes: 4 additions & 1 deletion komorebi-bar/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use font_loader::system_fonts;
use hotwatch::EventKind;
use hotwatch::Hotwatch;
use komorebi_client::SocketMessage;
use komorebi_client::SubscribeOptions;
use schemars::gen::SchemaSettings;
use std::io::BufReader;
use std::io::Read;
Expand Down Expand Up @@ -328,7 +329,9 @@ fn main() -> color_eyre::Result<()> {
std::thread::spawn(move || {
let subscriber_name = format!("komorebi-bar-{}", random_word::gen(random_word::Lang::En));

let listener = komorebi_client::subscribe(&subscriber_name)
let listener = komorebi_client::subscribe_with_options(&subscriber_name, SubscribeOptions {
filter_state_changes: true,
})
.expect("could not subscribe to komorebi notifications");

tracing::info!("subscribed to komorebi notifications: \"{}\"", subscriber_name);
Expand Down
27 changes: 27 additions & 0 deletions komorebi-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ pub use komorebi::RuleDebug;
pub use komorebi::StackbarConfig;
pub use komorebi::State;
pub use komorebi::StaticConfig;
pub use komorebi::SubscribeOptions;
pub use komorebi::TabsConfig;

use komorebi::DATA_DIR;
Expand Down Expand Up @@ -96,3 +97,29 @@ pub fn subscribe(name: &str) -> std::io::Result<UnixListener> {

Ok(listener)
}

pub fn subscribe_with_options(
name: &str,
options: SubscribeOptions,
) -> std::io::Result<UnixListener> {
let socket = DATA_DIR.join(name);

match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
std::io::ErrorKind::NotFound => {}
_ => {
return Err(error);
}
},
};

let listener = UnixListener::bind(&socket)?;

send_message(&SocketMessage::AddSubscriberSocketWithOptions(
name.to_string(),
options,
))?;

Ok(listener)
}
8 changes: 1 addition & 7 deletions komorebi/src/container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::Serialize;
use crate::ring::Ring;
use crate::window::Window;

#[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Getters, JsonSchema)]
pub struct Container {
#[getset(get = "pub")]
id: String,
Expand All @@ -27,12 +27,6 @@ impl Default for Container {
}
}

impl PartialEq for Container {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}

impl Container {
pub fn hide(&self, omit: Option<isize>) {
for window in self.windows().iter().rev() {
Expand Down
40 changes: 37 additions & 3 deletions komorebi/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ pub enum SocketMessage {
RemoveTitleBar(ApplicationIdentifier, String),
ToggleTitleBars,
AddSubscriberSocket(String),
AddSubscriberSocketWithOptions(String, SubscribeOptions),
RemoveSubscriberSocket(String),
AddSubscriberPipe(String),
RemoveSubscriberPipe(String),
Expand All @@ -221,6 +222,12 @@ impl FromStr for SocketMessage {
}
}

#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct SubscribeOptions {
/// Only emit notifications when the window manager state has changed
pub filter_state_changes: bool,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Display, Serialize, Deserialize, JsonSchema)]
pub enum StackbarMode {
Always,
Expand Down Expand Up @@ -336,7 +343,16 @@ pub enum ApplicationIdentifier {
}

#[derive(
Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
Copy,
Clone,
Debug,
PartialEq,
Serialize,
Deserialize,
Display,
EnumString,
ValueEnum,
JsonSchema,
)]
pub enum FocusFollowsMouseImplementation {
/// A custom FFM implementation (slightly more CPU-intensive)
Expand Down Expand Up @@ -377,7 +393,16 @@ pub enum WindowContainerBehaviour {
}

#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
Clone,
Copy,
Debug,
PartialEq,
Serialize,
Deserialize,
Display,
EnumString,
ValueEnum,
JsonSchema,
)]
pub enum MoveBehaviour {
/// Swap the window container with the window container at the edge of the adjacent monitor
Expand Down Expand Up @@ -411,7 +436,16 @@ pub enum HidingBehaviour {
}

#[derive(
Clone, Copy, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, JsonSchema,
Clone,
Copy,
Debug,
PartialEq,
Serialize,
Deserialize,
Display,
EnumString,
ValueEnum,
JsonSchema,
)]
pub enum OperationBehaviour {
/// Process komorebic commands on temporarily unmanaged/floated windows
Expand Down
34 changes: 26 additions & 8 deletions komorebi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ lazy_static! {
Arc::new(Mutex::new(HashMap::new()));
pub static ref SUBSCRIPTION_SOCKETS: Arc<Mutex<HashMap<String, PathBuf>>> =
Arc::new(Mutex::new(HashMap::new()));
pub static ref SUBSCRIPTION_SOCKET_OPTIONS: Arc<Mutex<HashMap<String, SubscribeOptions>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref TCP_CONNECTIONS: Arc<Mutex<HashMap<String, TcpStream>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref HIDING_BEHAVIOUR: Arc<Mutex<HidingBehaviour>> =
Expand Down Expand Up @@ -297,18 +299,34 @@ pub struct Notification {
pub state: State,
}

pub fn notify_subscribers(notification: &str) -> Result<()> {
pub fn notify_subscribers(notification: Notification, state_has_been_modified: bool) -> Result<()> {
let is_subscription_event = matches!(
notification.event,
NotificationEvent::Socket(SocketMessage::AddSubscriberSocket(_))
| NotificationEvent::Socket(SocketMessage::AddSubscriberSocketWithOptions(_, _))
);

let notification = &serde_json::to_string(&notification)?;
let mut stale_sockets = vec![];
let mut sockets = SUBSCRIPTION_SOCKETS.lock();
let options = SUBSCRIPTION_SOCKET_OPTIONS.lock();

for (socket, path) in &mut *sockets {
match UnixStream::connect(path) {
Ok(mut stream) => {
tracing::debug!("pushed notification to subscriber: {socket}");
stream.write_all(notification.as_bytes())?;
}
Err(_) => {
stale_sockets.push(socket.clone());
let apply_state_filter = (*options)
.get(socket)
.copied()
.unwrap_or_default()
.filter_state_changes;

if !apply_state_filter || state_has_been_modified || is_subscription_event {
match UnixStream::connect(path) {
Ok(mut stream) => {
tracing::debug!("pushed notification to subscriber: {socket}");
stream.write_all(notification.as_bytes())?;
}
Err(_) => {
stale_sockets.push(socket.clone());
}
}
}
}
Expand Down
26 changes: 21 additions & 5 deletions komorebi/src/process_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ use crate::winevent_listener;
use crate::GlobalState;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::ANIMATION_DURATION;
use crate::ANIMATION_ENABLED;
use crate::ANIMATION_FPS;
Expand All @@ -79,6 +80,7 @@ use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
use crate::REMOVE_TITLEBARS;
use crate::SUBSCRIPTION_PIPES;
use crate::SUBSCRIPTION_SOCKETS;
use crate::SUBSCRIPTION_SOCKET_OPTIONS;
use crate::TCP_CONNECTIONS;
use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
use crate::WINDOWS_11;
Expand Down Expand Up @@ -187,6 +189,10 @@ impl WindowManager {
}
}

#[allow(clippy::useless_asref)]
// We don't have From implemented for &mut WindowManager
let initial_state = State::from(self.as_ref());

match message {
SocketMessage::CycleFocusWorkspace(_) | SocketMessage::FocusWorkspaceNumber(_) => {
if let Some(monitor) = self.focused_monitor_mut() {
Expand Down Expand Up @@ -1319,6 +1325,14 @@ impl WindowManager {
let socket_path = DATA_DIR.join(socket);
sockets.insert(socket.clone(), socket_path);
}
SocketMessage::AddSubscriberSocketWithOptions(ref socket, options) => {
let mut sockets = SUBSCRIPTION_SOCKETS.lock();
let socket_path = DATA_DIR.join(socket);
sockets.insert(socket.clone(), socket_path);

let mut socket_options = SUBSCRIPTION_SOCKET_OPTIONS.lock();
socket_options.insert(socket.clone(), options);
}
SocketMessage::RemoveSubscriberSocket(ref socket) => {
let mut sockets = SUBSCRIPTION_SOCKETS.lock();
sockets.remove(socket);
Expand Down Expand Up @@ -1574,12 +1588,14 @@ impl WindowManager {
| SocketMessage::IdentifyBorderOverflowApplication(_, _) => {}
};

let notification = Notification {
event: NotificationEvent::Socket(message.clone()),
state: self.as_ref().into(),
};
notify_subscribers(
Notification {
event: NotificationEvent::Socket(message.clone()),
state: self.as_ref().into(),
},
initial_state.has_been_modified(self.as_ref()),
)?;

notify_subscribers(&serde_json::to_string(&notification)?)?;
border_manager::send_notification(None);
transparency_manager::send_notification();
stackbar_manager::send_notification();
Expand Down
17 changes: 12 additions & 5 deletions komorebi/src/process_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ use crate::workspace_reconciliator::ALT_TAB_HWND;
use crate::workspace_reconciliator::ALT_TAB_HWND_INSTANT;
use crate::Notification;
use crate::NotificationEvent;
use crate::State;
use crate::DATA_DIR;
use crate::FLOATING_APPLICATIONS;
use crate::HIDDEN_HWNDS;
Expand Down Expand Up @@ -122,6 +123,10 @@ impl WindowManager {
}
}

#[allow(clippy::useless_asref)]
// We don't have From implemented for &mut WindowManager
let initial_state = State::from(self.as_ref());

// Make sure we have the most recently focused monitor from any event
match event {
WindowManagerEvent::FocusChange(_, window)
Expand Down Expand Up @@ -670,12 +675,14 @@ impl WindowManager {

serde_json::to_writer_pretty(&file, &known_hwnds)?;

let notification = Notification {
event: NotificationEvent::WindowManager(event),
state: self.as_ref().into(),
};
notify_subscribers(
Notification {
event: NotificationEvent::WindowManager(event),
state: self.as_ref().into(),
},
initial_state.has_been_modified(self.as_ref()),
)?;

notify_subscribers(&serde_json::to_string(&notification)?)?;
border_manager::send_notification(Some(event.hwnd()));
transparency_manager::send_notification();
stackbar_manager::send_notification();
Expand Down
48 changes: 48 additions & 0 deletions komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,54 @@ pub struct State {
pub has_pending_raise_op: bool,
}

impl State {
pub fn has_been_modified(&self, wm: &WindowManager) -> bool {
let new = Self::from(wm);

if self.monitors != new.monitors {
return true;
}

if self.is_paused != new.is_paused {
return true;
}

if self.new_window_behaviour != new.new_window_behaviour {
return true;
}

if self.float_override != new.float_override {
return true;
}

if self.cross_monitor_move_behaviour != new.cross_monitor_move_behaviour {
return true;
}

if self.unmanaged_window_operation_behaviour != new.unmanaged_window_operation_behaviour {
return true;
}

if self.work_area_offset != new.work_area_offset {
return true;
}

if self.focus_follows_mouse != new.focus_follows_mouse {
return true;
}

if self.mouse_follows_focus != new.mouse_follows_focus {
return true;
}

if self.has_pending_raise_op != new.has_pending_raise_op {
return true;
}

false
}
}

#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct GlobalState {
Expand Down

0 comments on commit 5da72e1

Please sign in to comment.