mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-19 07:19:50 +02:00
feat(wm): optionally await config completion
This commit introduces a new flag to komorebi and komorebic, --await-configuration, which when enabled, will stop the process from processing window manager events and updating the layout until the 'komorebic complete-configuration' command has been sent. This should typically be added at the end of a user's komorebi.ahk configuration file if they decide to enable this feature. resolve #190
This commit is contained in:
12
README.md
12
README.md
@@ -267,6 +267,18 @@ komorebic.exe container-padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
|
||||
komorebic.exe workspace padding <MONITOR_INDEX> <WORKSPACE_INDEX> 0
|
||||
```
|
||||
|
||||
#### Multiple Layout Changes on Startup
|
||||
|
||||
Depending on what is in your configuration, when `komorebi` is started, you may experience the layout rapidly being adjusted
|
||||
with many retile events.
|
||||
|
||||
If you would like to avoid this, you can start `komorebi` with a flag which tells `komorebi` to wait until all configuration
|
||||
has been loaded before listening to and responding to window manager events: `komorebic start --await-configuration`.
|
||||
|
||||
If you start `komorebi` with the `--await-configuration` flag, you _must_ send the `komorebic complete-configuration` command
|
||||
at the end of your `komorebi.ahk` config. The layout will not be updated and `komorebi` will not respond to `komorebic`
|
||||
commands until this command has been received.
|
||||
|
||||
#### Floating Windows
|
||||
|
||||
Sometimes you will want a specific application to never be tiled, and instead float all the time. You add add rules to
|
||||
|
||||
@@ -96,6 +96,7 @@ pub enum SocketMessage {
|
||||
// Configuration
|
||||
ReloadConfiguration,
|
||||
WatchConfiguration(bool),
|
||||
CompleteConfiguration,
|
||||
InvisibleBorders(Rect),
|
||||
WorkAreaOffset(Rect),
|
||||
ResizeDelta(i32),
|
||||
|
||||
@@ -11,8 +11,6 @@ use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
use std::thread;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
@@ -20,6 +18,7 @@ use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
use crossbeam_channel::Receiver;
|
||||
use crossbeam_channel::Sender;
|
||||
use crossbeam_utils::Backoff;
|
||||
use lazy_static::lazy_static;
|
||||
#[cfg(feature = "deadlock_detection")]
|
||||
use parking_lot::deadlock;
|
||||
@@ -143,6 +142,7 @@ lazy_static! {
|
||||
};
|
||||
}
|
||||
|
||||
pub static INITIAL_CONFIGURATION_LOADED: AtomicBool = AtomicBool::new(false);
|
||||
pub static CUSTOM_FFM: AtomicBool = AtomicBool::new(false);
|
||||
pub static SESSION_ID: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
@@ -242,6 +242,8 @@ pub fn load_configuration() -> Result<()> {
|
||||
Command::new("AutoHotkey64.exe")
|
||||
.arg(config_v2.as_os_str())
|
||||
.output()?;
|
||||
} else {
|
||||
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
|
||||
};
|
||||
|
||||
Ok(())
|
||||
@@ -341,9 +343,9 @@ pub fn notify_subscribers(notification: &str) -> Result<()> {
|
||||
#[tracing::instrument]
|
||||
fn detect_deadlocks() {
|
||||
// Create a background thread which checks for deadlocks every 10s
|
||||
thread::spawn(move || loop {
|
||||
std::thread::spawn(move || loop {
|
||||
tracing::info!("running deadlock detector");
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
let deadlocks = deadlock::check_deadlock();
|
||||
if deadlocks.is_empty() {
|
||||
continue;
|
||||
@@ -364,8 +366,11 @@ fn detect_deadlocks() {
|
||||
#[clap(author, about, version)]
|
||||
struct Opts {
|
||||
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
||||
#[clap(long = "ffm")]
|
||||
#[clap(action, short, long = "ffm")]
|
||||
focus_follows_mouse: bool,
|
||||
/// Wait for 'komorebic complete-configuration' to be sent before processing events
|
||||
#[clap(action, short, long)]
|
||||
await_configuration: bool,
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
@@ -374,7 +379,9 @@ fn main() -> Result<()> {
|
||||
CUSTOM_FFM.store(opts.focus_follows_mouse, Ordering::SeqCst);
|
||||
|
||||
let arg_count = std::env::args().count();
|
||||
let has_valid_args = arg_count == 1 || (arg_count == 2 && CUSTOM_FFM.load(Ordering::SeqCst));
|
||||
let has_valid_args = arg_count == 1
|
||||
|| (arg_count == 2 && (opts.await_configuration || opts.focus_follows_mouse))
|
||||
|| (arg_count == 3 && opts.await_configuration && opts.focus_follows_mouse);
|
||||
|
||||
if has_valid_args {
|
||||
let session_id = WindowsApi::process_id_to_session_id()?;
|
||||
@@ -421,14 +428,21 @@ fn main() -> Result<()> {
|
||||
|
||||
wm.lock().init()?;
|
||||
listen_for_commands(wm.clone());
|
||||
std::thread::spawn(|| load_configuration().expect("could not load configuration"));
|
||||
|
||||
if opts.await_configuration {
|
||||
let backoff = Backoff::new();
|
||||
while !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
|
||||
backoff.snooze();
|
||||
}
|
||||
}
|
||||
|
||||
listen_for_events(wm.clone());
|
||||
|
||||
if CUSTOM_FFM.load(Ordering::SeqCst) {
|
||||
listen_for_movements(wm.clone());
|
||||
}
|
||||
|
||||
load_configuration()?;
|
||||
|
||||
let (ctrlc_sender, ctrlc_receiver) = crossbeam_channel::bounded(1);
|
||||
ctrlc::set_handler(move || {
|
||||
ctrlc_sender
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::num::NonZeroUsize;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -40,6 +39,7 @@ use crate::CUSTOM_FFM;
|
||||
use crate::FLOAT_IDENTIFIERS;
|
||||
use crate::HIDING_BEHAVIOUR;
|
||||
use crate::HOME_DIR;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
use crate::LAYERED_WHITELIST;
|
||||
use crate::MANAGE_IDENTIFIERS;
|
||||
use crate::OBJECT_NAME_CHANGE_ON_LAUNCH;
|
||||
@@ -55,7 +55,7 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
|
||||
.try_clone()
|
||||
.expect("could not clone unix listener");
|
||||
|
||||
thread::spawn(move || {
|
||||
std::thread::spawn(move || {
|
||||
tracing::info!("listening");
|
||||
for client in listener.incoming() {
|
||||
match client {
|
||||
@@ -574,6 +574,12 @@ impl WindowManager {
|
||||
SocketMessage::ReloadConfiguration => {
|
||||
Self::reload_configuration();
|
||||
}
|
||||
SocketMessage::CompleteConfiguration => {
|
||||
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
|
||||
INITIAL_CONFIGURATION_LOADED.store(true, Ordering::SeqCst);
|
||||
self.update_focused_workspace(false)?;
|
||||
}
|
||||
}
|
||||
SocketMessage::WatchConfiguration(enable) => {
|
||||
self.watch_configuration(enable)?;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::fs::OpenOptions;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -27,7 +26,7 @@ use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS;
|
||||
pub fn listen_for_events(wm: Arc<Mutex<WindowManager>>) {
|
||||
let receiver = wm.lock().incoming_events.lock().clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
std::thread::spawn(move || {
|
||||
tracing::info!("listening");
|
||||
loop {
|
||||
select! {
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::io::ErrorKind;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -191,14 +190,13 @@ impl WindowManager {
|
||||
pub fn init(&mut self) -> Result<()> {
|
||||
tracing::info!("initialising");
|
||||
WindowsApi::load_monitor_information(&mut self.monitors)?;
|
||||
WindowsApi::load_workspace_information(&mut self.monitors)?;
|
||||
self.update_focused_workspace(false)
|
||||
WindowsApi::load_workspace_information(&mut self.monitors)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub fn reload_configuration() {
|
||||
tracing::info!("reloading configuration");
|
||||
thread::spawn(|| load_configuration().expect("could not load configuration"));
|
||||
std::thread::spawn(|| load_configuration().expect("could not load configuration"));
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
@@ -247,7 +245,7 @@ impl WindowManager {
|
||||
// Editing in Notepad sends a NoticeWrite while editing in (Neo)Vim sends
|
||||
// a NoticeRemove, presumably because of the use of swap files?
|
||||
DebouncedEvent::NoticeWrite(_) | DebouncedEvent::NoticeRemove(_) => {
|
||||
thread::spawn(|| {
|
||||
std::thread::spawn(|| {
|
||||
load_configuration().expect("could not load configuration");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::sync::atomic::AtomicIsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_channel::Receiver;
|
||||
@@ -44,7 +43,7 @@ impl WinEventListener {
|
||||
let hook = self.hook.clone();
|
||||
let outgoing = self.outgoing_events.lock().clone();
|
||||
|
||||
thread::spawn(move || unsafe {
|
||||
std::thread::spawn(move || unsafe {
|
||||
let hook_ref = SetWinEventHook(
|
||||
EVENT_MIN as u32,
|
||||
EVENT_MAX as u32,
|
||||
@@ -96,7 +95,7 @@ impl MessageLoop {
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(sleep));
|
||||
std::thread::sleep(Duration::from_millis(sleep));
|
||||
|
||||
if !cb(value) {
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use color_eyre::eyre::anyhow;
|
||||
use color_eyre::Result;
|
||||
@@ -21,6 +22,7 @@ use crate::container::Container;
|
||||
use crate::ring::Ring;
|
||||
use crate::window::Window;
|
||||
use crate::windows_api::WindowsApi;
|
||||
use crate::INITIAL_CONFIGURATION_LOADED;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Getters, CopyGetters, MutGetters, Setters, JsonSchema)]
|
||||
pub struct Workspace {
|
||||
@@ -149,6 +151,10 @@ impl Workspace {
|
||||
offset: Option<Rect>,
|
||||
invisible_borders: &Rect,
|
||||
) -> Result<()> {
|
||||
if !INITIAL_CONFIGURATION_LOADED.load(Ordering::SeqCst) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let container_padding = self.container_padding();
|
||||
let mut adjusted_work_area = offset.map_or_else(
|
||||
|| *work_area,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
; Generated by komorebic.exe
|
||||
|
||||
Start(ffm) {
|
||||
Run, komorebic.exe start --ffm %ffm%, , Hide
|
||||
Start(ffm, await_configuration) {
|
||||
Run, komorebic.exe start %ffm% --await_configuration %await_configuration%, , Hide
|
||||
}
|
||||
|
||||
Stop() {
|
||||
@@ -252,6 +252,10 @@ WatchConfiguration(boolean_state) {
|
||||
Run, komorebic.exe watch-configuration %boolean_state%, , Hide
|
||||
}
|
||||
|
||||
CompleteConfiguration() {
|
||||
Run, komorebic.exe complete-configuration, , Hide
|
||||
}
|
||||
|
||||
WindowHidingBehaviour(hiding_behaviour) {
|
||||
Run, komorebic.exe window-hiding-behaviour %hiding_behaviour%, , Hide
|
||||
}
|
||||
|
||||
@@ -396,8 +396,11 @@ struct FocusFollowsMouse {
|
||||
#[derive(Parser, AhkFunction)]
|
||||
struct Start {
|
||||
/// Allow the use of komorebi's custom focus-follows-mouse implementation
|
||||
#[clap(long)]
|
||||
#[clap(action, short, long = "ffm")]
|
||||
ffm: bool,
|
||||
/// Wait for 'komorebic complete-configuration' to be sent before processing events
|
||||
#[clap(action, short, long)]
|
||||
await_configuration: bool,
|
||||
}
|
||||
|
||||
#[derive(Parser, AhkFunction)]
|
||||
@@ -627,6 +630,8 @@ enum SubCommand {
|
||||
/// Enable or disable watching of ~/komorebi.ahk (if it exists)
|
||||
#[clap(arg_required_else_help = true)]
|
||||
WatchConfiguration(WatchConfiguration),
|
||||
/// Signal that the final configuration option has been sent
|
||||
CompleteConfiguration,
|
||||
/// Set the window behaviour when switching workspaces / cycling stacks
|
||||
#[clap(arg_required_else_help = true)]
|
||||
WindowHidingBehaviour(WindowHidingBehaviour),
|
||||
@@ -910,19 +915,33 @@ fn main() -> Result<()> {
|
||||
|
||||
let script = exec.map_or_else(
|
||||
|| {
|
||||
if arg.ffm {
|
||||
String::from(
|
||||
"Start-Process komorebi.exe -ArgumentList '--ffm' -WindowStyle hidden",
|
||||
if arg.ffm | arg.await_configuration {
|
||||
format!(
|
||||
"Start-Process komorebi.exe -ArgumentList {} -WindowStyle hidden",
|
||||
if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'"
|
||||
} else if arg.ffm {
|
||||
"'--ffm'"
|
||||
} else {
|
||||
"'--await-configuration'"
|
||||
}
|
||||
)
|
||||
} else {
|
||||
String::from("Start-Process komorebi.exe -WindowStyle hidden")
|
||||
}
|
||||
},
|
||||
|exec| {
|
||||
if arg.ffm {
|
||||
if arg.ffm | arg.await_configuration {
|
||||
format!(
|
||||
"Start-Process '{}' -ArgumentList '--ffm' -WindowStyle hidden",
|
||||
exec
|
||||
"Start-Process '{}' -ArgumentList {} -WindowStyle hidden",
|
||||
exec,
|
||||
if arg.ffm && arg.await_configuration {
|
||||
"'--ffm','--await-configuration'"
|
||||
} else if arg.ffm {
|
||||
"'--ffm'"
|
||||
} else {
|
||||
"'--await-configuration'"
|
||||
}
|
||||
)
|
||||
} else {
|
||||
format!("Start-Process '{}' -WindowStyle hidden", exec)
|
||||
@@ -1107,6 +1126,9 @@ fn main() -> Result<()> {
|
||||
SubCommand::WatchConfiguration(arg) => {
|
||||
send_message(&SocketMessage::WatchConfiguration(arg.boolean_state.into()).as_bytes()?)?;
|
||||
}
|
||||
SubCommand::CompleteConfiguration => {
|
||||
send_message(&SocketMessage::CompleteConfiguration.as_bytes()?)?;
|
||||
}
|
||||
SubCommand::IdentifyObjectNameChangeApplication(target) => {
|
||||
send_message(
|
||||
&SocketMessage::IdentifyObjectNameChangeApplication(target.identifier, target.id)
|
||||
|
||||
Reference in New Issue
Block a user