feat(wm): add focus-workspaces cmd

This commit adds a new command, focus-workspaces, to allow the user to
change workspaces across all monitors at the same time. I'm not
convinced of the stability of this command and I would strongly
discourage using komorebi in this manner.

resolve #426
This commit is contained in:
LGUG2Z
2023-06-03 17:24:36 -07:00
parent 7ba7067c96
commit 9103ce2b2b
13 changed files with 130 additions and 89 deletions

3
.czrc Normal file
View File

@@ -0,0 +1,3 @@
{:
"path": "cz-conventional-changelog"
}

View File

@@ -24,31 +24,19 @@ impl ApplicationOptions {
pub fn raw_cfgen(&self, kind: &ApplicationIdentifier, id: &str) -> String {
match self {
ApplicationOptions::ObjectNameChange => {
format!(
"komorebic.exe identify-object-name-change-application {} \"{}\"",
kind, id
)
format!("komorebic.exe identify-object-name-change-application {kind} \"{id}\"",)
}
ApplicationOptions::Layered => {
format!(
"komorebic.exe identify-layered-application {} \"{}\"",
kind, id
)
format!("komorebic.exe identify-layered-application {kind} \"{id}\"",)
}
ApplicationOptions::BorderOverflow => {
format!(
"komorebic.exe identify-border-overflow-application {} \"{}\"",
kind, id
)
format!("komorebic.exe identify-border-overflow-application {kind} \"{id}\"",)
}
ApplicationOptions::TrayAndMultiWindow => {
format!(
"komorebic.exe identify-tray-application {} \"{}\"",
kind, id
)
format!("komorebic.exe identify-tray-application {kind} \"{id}\"",)
}
ApplicationOptions::Force => {
format!("komorebic.exe manage-rule {} \"{}\"", kind, id)
format!("komorebic.exe manage-rule {kind} \"{id}\"")
}
}
}
@@ -143,7 +131,7 @@ impl ApplicationConfigurationGenerator {
lines.push(format!("# {}", app.name));
if let Some(options) = app.options {
for opt in options {
if let ApplicationOptions::TrayAndMultiWindow = opt {
if matches!(opt, ApplicationOptions::TrayAndMultiWindow) {
lines.push(String::from("# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line"));
}
@@ -161,7 +149,7 @@ impl ApplicationConfigurationGenerator {
float_rules.push(float_rule.clone());
if let Some(comment) = float.comment {
lines.push(format!("# {}", comment));
lines.push(format!("# {comment}"));
};
lines.push(float_rule);
@@ -192,7 +180,7 @@ impl ApplicationConfigurationGenerator {
lines.push(format!("; {}", app.name));
if let Some(options) = app.options {
for opt in options {
if let ApplicationOptions::TrayAndMultiWindow = opt {
if matches!(opt, ApplicationOptions::TrayAndMultiWindow) {
lines.push(String::from("; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line"));
}
@@ -212,7 +200,7 @@ impl ApplicationConfigurationGenerator {
float_rules.push(float_rule.clone());
if let Some(comment) = float.comment {
lines.push(format!("; {}", comment));
lines.push(format!("; {comment}"));
};
lines.push(float_rule);

View File

@@ -95,6 +95,7 @@ pub enum SocketMessage {
CycleFocusWorkspace(CycleDirection),
FocusMonitorNumber(usize),
FocusWorkspaceNumber(usize),
FocusWorkspaceNumbers(usize),
FocusMonitorWorkspaceNumber(usize, usize),
FocusNamedWorkspace(String),
ContainerPadding(usize, usize, i32),

View File

@@ -20,6 +20,9 @@ RunWait('komorebic.exe identify-border-overflow-application class "Photoshop"',
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Akiflow.exe"', , "Hide")
; Android Studio
RunWait('komorebic.exe identify-object-name-change-application exe "studio64.exe"', , "Hide")
; ArmCord
RunWait('komorebic.exe identify-border-overflow-application exe "ArmCord.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -274,6 +277,9 @@ RunWait('komorebic.exe identify-tray-application class "DocEditorsWindowClass"',
RunWait('komorebic.exe identify-border-overflow-application exe "Obsidian.exe"', , "Hide")
RunWait('komorebic.exe manage-rule exe "Obsidian.exe"', , "Hide")
; OneDrive
RunWait('komorebic.exe float-rule class "OneDriveReactNativeWin32WindowClass"', , "Hide")
; OpenRGB
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "OpenRGB.exe"', , "Hide")
@@ -289,6 +295,8 @@ RunWait('komorebic.exe identify-border-overflow-application exe "Plexamp.exe"',
RunWait('komorebic.exe float-rule exe "PowerToys.ColorPickerUI.exe"', , "Hide")
; Target image resizer dialog
RunWait('komorebic.exe float-rule exe "PowerToys.ImageResizer.exe"', , "Hide")
; Target Peek popup
RunWait('komorebic.exe float-rule exe "PowerToys.Peek.UI.exe"', , "Hide")
; Process Hacker
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -420,6 +428,10 @@ RunWait('komorebic.exe float-rule exe "TranslucentTB.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "TranslucentTB.exe"', , "Hide")
; Unity Hub
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
RunWait('komorebic.exe identify-tray-application exe "Unity Hub.exe"', , "Hide")
; Unreal Editor
RunWait('komorebic.exe identify-border-overflow-application exe "UnrealEditor.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -435,6 +447,9 @@ RunWait('komorebic.exe identify-object-name-change-application exe "devenv.exe"'
; Visual Studio Code
RunWait('komorebic.exe identify-border-overflow-application exe "Code.exe"', , "Hide")
; Visual Studio Code - Insiders
RunWait('komorebic.exe identify-border-overflow-application exe "Code - Insiders.exe"', , "Hide")
; Voice.ai
RunWait('komorebic.exe identify-border-overflow-application exe "VoiceAI.exe"', , "Hide")
; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line

View File

@@ -20,6 +20,9 @@ komorebic.exe identify-border-overflow-application class "Photoshop"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Akiflow.exe"
# Android Studio
komorebic.exe identify-object-name-change-application exe "studio64.exe"
# ArmCord
komorebic.exe identify-border-overflow-application exe "ArmCord.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -274,6 +277,9 @@ komorebic.exe identify-tray-application class "DocEditorsWindowClass"
komorebic.exe identify-border-overflow-application exe "Obsidian.exe"
komorebic.exe manage-rule exe "Obsidian.exe"
# OneDrive
komorebic.exe float-rule class "OneDriveReactNativeWin32WindowClass"
# OpenRGB
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "OpenRGB.exe"
@@ -289,6 +295,8 @@ komorebic.exe identify-border-overflow-application exe "Plexamp.exe"
komorebic.exe float-rule exe "PowerToys.ColorPickerUI.exe"
# Target image resizer dialog
komorebic.exe float-rule exe "PowerToys.ImageResizer.exe"
# Target Peek popup
komorebic.exe float-rule exe "PowerToys.Peek.UI.exe"
# Process Hacker
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -420,6 +428,10 @@ komorebic.exe float-rule exe "TranslucentTB.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "TranslucentTB.exe"
# Unity Hub
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
komorebic.exe identify-tray-application exe "Unity Hub.exe"
# Unreal Editor
komorebic.exe identify-border-overflow-application exe "UnrealEditor.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line
@@ -435,6 +447,9 @@ komorebic.exe identify-object-name-change-application exe "devenv.exe"
# Visual Studio Code
komorebic.exe identify-border-overflow-application exe "Code.exe"
# Visual Studio Code - Insiders
komorebic.exe identify-border-overflow-application exe "Code - Insiders.exe"
# Voice.ai
komorebic.exe identify-border-overflow-application exe "VoiceAI.exe"
# If you have disabled minimize/close to tray for this application, you can delete/comment out the next line

View File

@@ -122,8 +122,7 @@ lazy_static! {
home
} else {
panic!(
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
home_path
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory",
);
}
})
@@ -344,7 +343,7 @@ pub fn notify_subscribers(notification: &str) -> Result<()> {
let mut stale_subscriptions = vec![];
let mut subscriptions = SUBSCRIPTION_PIPES.lock();
for (subscriber, pipe) in subscriptions.iter_mut() {
match writeln!(pipe, "{}", notification) {
match writeln!(pipe, "{notification}") {
Ok(_) => {
tracing::debug!("pushed notification to subscriber: {}", subscriber);
}

View File

@@ -97,7 +97,7 @@ pub fn listen_for_commands(wm: Arc<Mutex<WindowManager>>) {
#[tracing::instrument]
pub fn listen_for_commands_tcp(wm: Arc<Mutex<WindowManager>>, port: usize) {
let listener =
TcpListener::bind(format!("0.0.0.0:{}", port)).expect("could not start tcp server");
TcpListener::bind(format!("0.0.0.0:{port}")).expect("could not start tcp server");
std::thread::spawn(move || {
tracing::info!("listening on 0.0.0.0:43663");
@@ -557,6 +557,29 @@ impl WindowManager {
self.show_border()?;
};
}
SocketMessage::FocusWorkspaceNumbers(workspace_idx) => {
// This is to ensure that even on an empty workspace on a secondary monitor, the
// secondary monitor where the cursor is focused will be used as the target for
// the workspace switch op
if let Some(monitor_idx) = self.monitor_idx_from_current_pos() {
self.focus_monitor(monitor_idx)?;
}
let focused_monitor_idx = self.focused_monitor_idx();
for (i, monitor) in self.monitors_mut().iter_mut().enumerate() {
if i != focused_monitor_idx {
monitor.focus_workspace(workspace_idx)?;
monitor.load_focused_workspace(false)?;
}
}
self.focus_workspace(workspace_idx)?;
if BORDER_ENABLED.load(Ordering::SeqCst) {
self.show_border()?;
};
}
SocketMessage::FocusMonitorWorkspaceNumber(monitor_idx, workspace_idx) => {
self.focus_monitor(monitor_idx)?;
self.focus_workspace(workspace_idx)?;
@@ -688,8 +711,8 @@ impl WindowManager {
}
}
}
// Otherwise proceed with the resizing logic for individual window containers in the
// assumed BSP layout
// Otherwise proceed with the resizing logic for individual window containers in the
// assumed BSP layout
} else {
match axis {
Axis::Horizontal => {
@@ -773,9 +796,10 @@ impl WindowManager {
}
}
FocusFollowsMouseImplementation::Windows => {
if let Some(FocusFollowsMouseImplementation::Komorebi) =
self.focus_follows_mouse
{
if matches!(
self.focus_follows_mouse,
Some(FocusFollowsMouseImplementation::Komorebi)
) {
tracing::warn!(
"the windows implementation of focus follows mouse cannot be enabled while the komorebi implementation is enabled"
);
@@ -820,9 +844,10 @@ impl WindowManager {
}
}
FocusFollowsMouseImplementation::Windows => {
if let Some(FocusFollowsMouseImplementation::Komorebi) =
self.focus_follows_mouse
{
if matches!(
self.focus_follows_mouse,
Some(FocusFollowsMouseImplementation::Komorebi)
) {
tracing::warn!(
"the windows implementation of focus follows mouse cannot be toggled while the komorebi implementation is enabled"
);
@@ -958,7 +983,7 @@ impl WindowManager {
}
SocketMessage::AddSubscriber(ref subscriber) => {
let mut pipes = SUBSCRIPTION_PIPES.lock();
let pipe_path = format!(r"\\.\pipe\{}", subscriber);
let pipe_path = format!(r"\\.\pipe\{subscriber}");
let pipe = connect(&pipe_path).map_err(|_| {
anyhow!("the named pipe '{}' has not yet been created; please create it before running this command", pipe_path)
})?;
@@ -1286,11 +1311,7 @@ pub fn read_commands_tcp(
break;
}
Ok(size) => {
let message = if let Ok(message) =
SocketMessage::from_str(&String::from_utf8_lossy(&buf[..size]))
{
message
} else {
let Ok(message) = SocketMessage::from_str(&String::from_utf8_lossy(&buf[..size])) else {
tracing::warn!("client sent an invalid message, disconnecting: {addr}");
let mut connections = TCP_CONNECTIONS.lock();
connections.remove(addr);

View File

@@ -18,7 +18,10 @@ pub fn listen_for_movements(wm: Arc<Mutex<WindowManager>>) {
loop {
let focus_follows_mouse = wm.lock().focus_follows_mouse;
if let Some(FocusFollowsMouseImplementation::Komorebi) = focus_follows_mouse {
if matches!(
focus_follows_mouse,
Some(FocusFollowsMouseImplementation::Komorebi)
) {
match receiver.next_event() {
// Don't want to send any raise events while we are dragging or resizing
Event::MouseButton { action, .. } => match action {

View File

@@ -45,20 +45,20 @@ impl Display for Window {
let mut display = format!("(hwnd: {}", self.hwnd);
if let Ok(title) = self.title() {
write!(display, ", title: {}", title)?;
write!(display, ", title: {title}")?;
}
if let Ok(exe) = self.exe() {
write!(display, ", exe: {}", exe)?;
write!(display, ", exe: {exe}")?;
}
if let Ok(class) = self.class() {
write!(display, ", class: {}", class)?;
write!(display, ", class: {class}")?;
}
write!(display, ")")?;
write!(f, "{}", display)
write!(f, "{display}")
}
}

View File

@@ -984,7 +984,10 @@ impl WindowManager {
#[tracing::instrument(skip(self))]
fn handle_unmanaged_window_behaviour(&self) -> Result<()> {
if let OperationBehaviour::NoOp = self.unmanaged_window_operation_behaviour {
if matches!(
self.unmanaged_window_operation_behaviour,
OperationBehaviour::NoOp
) {
let workspace = self.focused_workspace()?;
let focused_hwnd = WindowsApi::foreground_window()?;
if !workspace.contains_managed_window(focused_hwnd) {

View File

@@ -31,62 +31,49 @@ impl Display for WindowManagerEvent {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Manage(window) => {
write!(f, "Manage (Window: {})", window)
write!(f, "Manage (Window: {window})")
}
Self::Unmanage(window) => {
write!(f, "Unmanage (Window: {})", window)
write!(f, "Unmanage (Window: {window})")
}
Self::Destroy(winevent, window) => {
write!(f, "Destroy (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Destroy (WinEvent: {winevent}, Window: {window})")
}
Self::FocusChange(winevent, window) => {
write!(
f,
"FocusChange (WinEvent: {}, Window: {})",
winevent, window
)
write!(f, "FocusChange (WinEvent: {winevent}, Window: {window})",)
}
Self::Hide(winevent, window) => {
write!(f, "Hide (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Hide (WinEvent: {winevent}, Window: {window})")
}
Self::Cloak(winevent, window) => {
write!(f, "Cloak (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Cloak (WinEvent: {winevent}, Window: {window})")
}
Self::Minimize(winevent, window) => {
write!(f, "Minimize (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Minimize (WinEvent: {winevent}, Window: {window})")
}
Self::Show(winevent, window) => {
write!(f, "Show (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Show (WinEvent: {winevent}, Window: {window})")
}
Self::Uncloak(winevent, window) => {
write!(f, "Uncloak (WinEvent: {}, Window: {})", winevent, window)
write!(f, "Uncloak (WinEvent: {winevent}, Window: {window})")
}
Self::MoveResizeStart(winevent, window) => {
write!(
f,
"MoveResizeStart (WinEvent: {}, Window: {})",
winevent, window
"MoveResizeStart (WinEvent: {winevent}, Window: {window})",
)
}
Self::MoveResizeEnd(winevent, window) => {
write!(
f,
"MoveResizeEnd (WinEvent: {}, Window: {})",
winevent, window
)
write!(f, "MoveResizeEnd (WinEvent: {winevent}, Window: {window})",)
}
Self::MouseCapture(winevent, window) => {
write!(
f,
"MouseCapture (WinEvent: {}, Window: {})",
winevent, window
)
write!(f, "MouseCapture (WinEvent: {winevent}, Window: {window})",)
}
Self::Raise(window) => {
write!(f, "Raise (Window: {})", window)
write!(f, "Raise (Window: {window})")
}
Self::DisplayChange(window) => {
write!(f, "DisplayChange (Window: {})", window)
write!(f, "DisplayChange (Window: {window})")
}
}
}

View File

@@ -158,7 +158,7 @@ pub extern "system" fn border_window(
lparam: LPARAM,
) -> LRESULT {
unsafe {
match message as u32 {
match message {
WM_PAINT => {
let border_rect = *BORDER_RECT.lock();
let mut ps = PAINTSTRUCT::default();
@@ -194,7 +194,7 @@ pub extern "system" fn hidden_window(
lparam: LPARAM,
) -> LRESULT {
unsafe {
match message as u32 {
match message {
WM_DISPLAYCHANGE => {
let event_type = WindowManagerEvent::DisplayChange(Window { hwnd: window.0 });
WINEVENT_CALLBACK_CHANNEL

View File

@@ -60,8 +60,7 @@ lazy_static! {
home
} else {
panic!(
"$Env:KOMOREBI_CONFIG_HOME is set to '{}', which is not a valid directory",
home_path
"$Env:KOMOREBI_CONFIG_HOME is set to '{home_path}', which is not a valid directory",
);
}
},
@@ -153,6 +152,7 @@ gen_target_subcommand_args! {
SendToWorkspace,
FocusMonitor,
FocusWorkspace,
FocusWorkspaces,
MoveWorkspaceToMonitor,
}
@@ -789,6 +789,9 @@ enum SubCommand {
/// Focus the specified workspace on the focused monitor
#[clap(arg_required_else_help = true)]
FocusWorkspace(FocusWorkspace),
/// Focus the specified workspace on all monitors
#[clap(arg_required_else_help = true)]
FocusWorkspaces(FocusWorkspaces),
/// Focus the specified workspace on the target monitor
#[clap(arg_required_else_help = true)]
FocusMonitorWorkspace(FocusMonitorWorkspace),
@@ -1019,7 +1022,7 @@ enum SubCommand {
pub fn send_message(bytes: &[u8]) -> Result<()> {
let socket = DATA_DIR.join("komorebi.sock");
let mut stream = UnixStream::connect(&socket)?;
let mut stream = UnixStream::connect(socket)?;
Ok(stream.write_all(bytes)?)
}
@@ -1103,7 +1106,7 @@ fn main() -> Result<()> {
let locked = file.lock();
#[allow(clippy::significant_drop_in_scrutinee)]
for line in locked.lines().flatten() {
println!("{}", line);
println!("{line}");
}
}
SubCommand::Focus(arg) => {
@@ -1404,13 +1407,13 @@ fn main() -> Result<()> {
"'--await-configuration'".to_string()
},
|port| if arg.ffm {
format!("'--ffm','--tcp-port={}'", port)
format!("'--ffm','--tcp-port={port}'")
} else if arg.await_configuration {
format!("'--await-configuration','--tcp-port={}'", port)
format!("'--await-configuration','--tcp-port={port}'")
} else if arg.ffm && arg.await_configuration {
format!("'--ffm','--await-configuration','--tcp-port={}'", port)
format!("'--ffm','--await-configuration','--tcp-port={port}'")
} else {
format!("'--tcp-port={}'", port)
format!("'--tcp-port={port}'")
}
)
)
@@ -1432,18 +1435,18 @@ fn main() -> Result<()> {
"'--await-configuration'".to_string()
},
|port| if arg.ffm {
format!("'--ffm','--tcp-port={}'", port)
format!("'--ffm','--tcp-port={port}'")
} else if arg.await_configuration {
format!("'--await-configuration','--tcp-port={}'", port)
format!("'--await-configuration','--tcp-port={port}'")
} else if arg.ffm && arg.await_configuration {
format!("'--ffm','--await-configuration','--tcp-port={}'", port)
format!("'--ffm','--await-configuration','--tcp-port={port}'")
} else {
format!("'--tcp-port={}'", port)
format!("'--tcp-port={port}'")
}
)
)
} else {
format!("Start-Process '{}' -WindowStyle hidden", exec)
format!("Start-Process '{exec}' -WindowStyle hidden")
}
},
);
@@ -1453,10 +1456,10 @@ fn main() -> Result<()> {
while !running {
match powershell_script::run(&script) {
Ok(_) => {
println!("{}", script);
println!("{script}");
}
Err(error) => {
println!("Error: {}", error);
println!("Error: {error}");
}
}
@@ -1538,6 +1541,9 @@ fn main() -> Result<()> {
SubCommand::FocusWorkspace(arg) => {
send_message(&SocketMessage::FocusWorkspaceNumber(arg.target).as_bytes()?)?;
}
SubCommand::FocusWorkspaces(arg) => {
send_message(&SocketMessage::FocusWorkspaceNumbers(arg.target).as_bytes()?)?;
}
SubCommand::FocusMonitorWorkspace(arg) => {
send_message(
&SocketMessage::FocusMonitorWorkspaceNumber(