feat(wm): add replace configuration socket message

This commit introduces a new SocketMessage, ReplaceConfiguration, which
attempts to replace a running instance of WindowManager with another
created from a (presumably) different komorebi.json file.

This will likely be useful for people who have multiple different
monitor setups that they connect and disconnect from throughout the day,
but definitely needs more testing.

An experimental sub-widget which calls this SocketMessage has been added
to komorebi-bar to aid with initial testing.
This commit is contained in:
LGUG2Z
2024-09-18 10:48:55 -07:00
parent 21a2138330
commit 2916256e79
12 changed files with 292 additions and 69 deletions
+1
View File
@@ -133,6 +133,7 @@ pub enum SocketMessage {
ClearNamedWorkspaceLayoutRules(String),
// Configuration
ReloadConfiguration,
ReplaceConfiguration(PathBuf),
ReloadStaticConfiguration(PathBuf),
WatchConfiguration(bool),
CompleteConfiguration,
+1
View File
@@ -220,6 +220,7 @@ fn main() -> Result<()> {
Arc::new(Mutex::new(StaticConfig::preload(
config,
winevent_listener::event_rx(),
None,
)?))
} else {
Arc::new(Mutex::new(WindowManager::new(
+28
View File
@@ -56,6 +56,7 @@ use crate::window::Window;
use crate::window_manager;
use crate::window_manager::WindowManager;
use crate::windows_api::WindowsApi;
use crate::winevent_listener;
use crate::GlobalState;
use crate::Notification;
use crate::NotificationEvent;
@@ -1097,6 +1098,33 @@ impl WindowManager {
SocketMessage::ReloadConfiguration => {
Self::reload_configuration();
}
SocketMessage::ReplaceConfiguration(ref config) => {
// Check that this is a valid static config file first
if StaticConfig::read(config).is_ok() {
// Clear workspace rules; these will need to be replaced
WORKSPACE_RULES.lock().clear();
// Pause so that restored windows come to the foreground from all workspaces
self.is_paused = true;
// Bring all windows to the foreground
self.restore_all_windows()?;
// Create a new wm from the config path
let mut wm = StaticConfig::preload(
config,
winevent_listener::event_rx(),
self.command_listener.try_clone().ok(),
)?;
// Initialize the new wm
wm.init()?;
// This is equivalent to StaticConfig::postload for this use case
StaticConfig::reload(config, &mut wm)?;
// Set self to the new wm instance
*self = wm;
}
}
SocketMessage::ReloadStaticConfiguration(ref pathbuf) => {
self.reload_static_configuration(pathbuf)?;
}
+18 -12
View File
@@ -1027,26 +1027,32 @@ impl StaticConfig {
pub fn preload(
path: &PathBuf,
incoming: Receiver<WindowManagerEvent>,
unix_listener: Option<UnixListener>,
) -> Result<WindowManager> {
let content = std::fs::read_to_string(path)?;
let mut value: Self = serde_json::from_str(&content)?;
value.apply_globals()?;
let socket = DATA_DIR.join("komorebi.sock");
let listener = match unix_listener {
Some(listener) => listener,
None => {
let socket = DATA_DIR.join("komorebi.sock");
match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
match std::fs::remove_file(&socket) {
Ok(()) => {}
Err(error) => match error.kind() {
// Doing this because ::exists() doesn't work reliably on Windows via IntelliJ
ErrorKind::NotFound => {}
_ => {
return Err(error.into());
}
},
};
UnixListener::bind(&socket)?
}
};
let listener = UnixListener::bind(&socket)?;
let mut wm = WindowManager {
monitors: Ring::default(),
incoming_events: incoming,