Skip to content

Commit

Permalink
feat(wm): optionally await config completion
Browse files Browse the repository at this point in the history
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
  • Loading branch information
LGUG2Z committed Jul 31, 2022
1 parent 5b91e22 commit a6d46db
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 29 deletions.
12 changes: 12 additions & 0 deletions README.md
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions komorebi-core/src/lib.rs
Expand Up @@ -96,6 +96,7 @@ pub enum SocketMessage {
// Configuration
ReloadConfiguration,
WatchConfiguration(bool),
CompleteConfiguration,
InvisibleBorders(Rect),
WorkAreaOffset(Rect),
ResizeDelta(i32),
Expand Down
30 changes: 22 additions & 8 deletions komorebi/src/main.rs
Expand Up @@ -11,15 +11,14 @@ 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;
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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -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;
Expand All @@ -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]
Expand All @@ -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()?;
Expand Down Expand Up @@ -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
Expand Down
10 changes: 8 additions & 2 deletions komorebi/src/process_command.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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)?;
}
Expand Down
3 changes: 1 addition & 2 deletions komorebi/src/process_event.rs
@@ -1,6 +1,5 @@
use std::fs::OpenOptions;
use std::sync::Arc;
use std::thread;

use color_eyre::eyre::anyhow;
use color_eyre::Result;
Expand All @@ -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! {
Expand Down
8 changes: 3 additions & 5 deletions komorebi/src/window_manager.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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))]
Expand Down Expand Up @@ -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");
});
}
Expand Down
5 changes: 2 additions & 3 deletions komorebi/src/winevent_listener.rs
@@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -96,7 +95,7 @@ impl MessageLoop {
}
}

thread::sleep(Duration::from_millis(sleep));
std::thread::sleep(Duration::from_millis(sleep));

if !cb(value) {
break;
Expand Down
6 changes: 6 additions & 0 deletions komorebi/src/workspace.rs
@@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 6 additions & 2 deletions komorebic.lib.sample.ahk
@@ -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() {
Expand Down Expand Up @@ -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
}
Expand Down
36 changes: 29 additions & 7 deletions komorebic/src/main.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit a6d46db

Please sign in to comment.