Skip to content

Commit

Permalink
feat(wm): add native window maximization toggle
Browse files Browse the repository at this point in the history
Windows that have been maximized do not retain their maximized state
across workspaces as workspaces are built on top of sending SW_HIDE and
SW_SHOW events which at various points of the event loop end up
overriding SW_SHOWMAXIMIZED and SW_SHOWMAXIMIZE.

To handle this use case, I have added a new 'komorebic toggle-maximize'
command which sends SW_MAXIMIZE for a window and keeps a record of the
window in the focused workspace in the same way that monocle windows are
tracked.

In this way, komorebi can know when switching to a workspace if it has
to restore a window to a native maximized state.

Some additional edge cases are caught in this commit in showing and
hiding workspaces, to also account for floating windows and monocle
containers.

resolve #12
  • Loading branch information
LGUG2Z committed Aug 18, 2021
1 parent 13b335c commit 0725549
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 15 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,47 @@ You can run `komorebic.exe` to get a full list of the commands that you can use
keybindings with. You can run `komorebic.exe <COMMAND> --help` to get a full explanation of the arguments required for
each command.

```
start Start komorebi.exe as a background process
stop Stop the komorebi.exe process and restore all hidden windows
state Show a JSON representation of the current window manager state
log Tail komorebi.exe's process logs (cancel with Ctrl-C)
focus Change focus to the window in the specified direction
move Move the focused window in the specified direction
stack Stack the focused window in the specified direction
resize Resize the focused window in the specified direction
unstack Unstack the focused window
cycle-stack Cycle the focused stack in the specified cycle direction
move-to-monitor Move the focused window to the specified monitor
move-to-workspace Move the focused window to the specified workspace
focus-monitor Focus the specified monitor
focus-workspace Focus the specified workspace on the focused monitor
new-workspace Create and append a new workspace on the focused monitor
adjust-container-padding Adjust container padding on the focused workspace
adjust-workspace-padding Adjust workspace padding on the focused workspace
flip-layout Flip the layout on the focused workspace (BSP only)
promote Promote the focused window to the top of the tree
retile Force the retiling of all managed windows
ensure-workspaces Create at least this many workspaces for the specified monitor
container-padding Set the container padding for the specified workspace
workspace-padding Set the workspace padding for the specified workspace
workspace-layout Set the layout for the specified workspace
workspace-tiling Enable or disable window tiling for the specified workspace
workspace-name Set the workspace name for the specified workspace
toggle-pause Toggle the window manager on and off across all monitors
toggle-tiling Toggle window tiling on the focused workspace
toggle-float Toggle floating mode for the focused window
toggle-monocle Toggle monocle mode for the focused container
toggle-maximize Toggle native window fullscreen for the focused window
restore-windows Restore all hidden windows (debugging command)
reload-configuration Reload ~/komorebi.ahk (if it exists)
watch-configuration Toggle the automatic reloading of ~/komorebi.ahk (if it exists)
float-rule Add a rule to always float the specified application
identify-tray-application Identify an application that closes to the system tray
focus-follows-mouse Enable or disable focus follows mouse for the operating system
help Print this message or the help of the given subcommand(s)
```

## Features

- [x] Multi-monitor
Expand All @@ -158,6 +199,7 @@ each command.
- [x] Identify 'close/minimize to tray' applications
- [x] Toggle floating windows
- [x] Toggle monocle window
- [x] Toggle native maximization
- [x] Toggle focus follows mouse
- [x] Toggle automatic tiling
- [x] Pause all window management
Expand Down
1 change: 1 addition & 0 deletions komorebi-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub enum SocketMessage {
Promote,
ToggleFloat,
ToggleMonocle,
ToggleMaximize,
// Current Workspace Commands
AdjustContainerPadding(Sizing, i32),
AdjustWorkspacePadding(Sizing, i32),
Expand Down
5 changes: 5 additions & 0 deletions komorebi.sample.ahk
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ return
Run, komorebic.exe toggle-monocle, , Hide
return

; Toggle native maximize for the focused window, Alt + Shift + =
!+=::
Run, komorebic.exe toggle-maximize, , Hide
return

; Flip horizontally, Alt + X
!x::
Run, komorebic.exe flip-layout horizontal, , Hide
Expand Down
13 changes: 11 additions & 2 deletions komorebi/src/monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,23 @@ impl Monitor {
}
}

#[tracing::instrument(skip(self))]
pub fn move_container_to_workspace(
&mut self,
target_workspace_idx: usize,
follow: bool,
) -> Result<()> {
let container = self
let workspace = self
.focused_workspace_mut()
.context("there is no workspace")?
.context("there is no workspace")?;

if workspace.maximized_window().is_some() {
return Err(eyre::anyhow!(
"cannot move native maximized window to another monitor or workspace"
));
}

let container = workspace
.remove_focused_container()
.context("there is no container")?;

Expand Down
1 change: 1 addition & 0 deletions komorebi/src/process_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl WindowManager {
}
SocketMessage::ToggleFloat => self.toggle_float()?,
SocketMessage::ToggleMonocle => self.toggle_monocle()?,
SocketMessage::ToggleMaximize => self.toggle_maximize()?,
SocketMessage::ContainerPadding(monitor_idx, workspace_idx, size) => {
self.set_container_padding(monitor_idx, workspace_idx, size)?;
}
Expand Down
2 changes: 1 addition & 1 deletion komorebi/src/process_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ impl WindowManager {

let workspace = self.focused_workspace_mut()?;

if workspace.containers().is_empty() || !workspace.contains_window(window.hwnd) {
if !workspace.contains_window(window.hwnd) {
workspace.new_container_for_window(*window);
self.update_focused_workspace(false)?;
}
Expand Down
12 changes: 12 additions & 0 deletions komorebi/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,18 @@ impl Window {
WindowsApi::restore_window(self.hwnd());
}

pub fn maximize(self) {
let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock().unwrap();
if let Some(idx) = programmatically_hidden_hwnds
.iter()
.position(|&hwnd| hwnd == self.hwnd)
{
programmatically_hidden_hwnds.remove(idx);
}

WindowsApi::maximize_window(self.hwnd());
}

pub fn focus(self) -> Result<()> {
// Attach komorebi thread to Window thread
let (_, window_thread_id) = WindowsApi::window_thread_process_id(self.hwnd());
Expand Down
44 changes: 41 additions & 3 deletions komorebi/src/window_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ impl WindowManager {
.update_focused_workspace()?;

if mouse_follows_focus {
if let Ok(window) = self.focused_window_mut() {
if let Some(window) = self.focused_workspace()?.maximized_window() {
window.focus()?;
} else if let Ok(window) = self.focused_window_mut() {
window.focus()?;
} else {
let desktop_window = Window {
Expand Down Expand Up @@ -310,9 +312,17 @@ impl WindowManager {
tracing::info!("moving container");

let monitor = self.focused_monitor_mut().context("there is no monitor")?;
let container = monitor
let workspace = monitor
.focused_workspace_mut()
.context("there is no workspace")?
.context("there is no workspace")?;

if workspace.maximized_window().is_some() {
return Err(eyre::anyhow!(
"cannot move native maximized window to another monitor or workspace"
));
}

let container = workspace
.remove_focused_container()
.context("there is no container")?;

Expand Down Expand Up @@ -541,6 +551,34 @@ impl WindowManager {
workspace.reintegrate_monocle_container()
}

#[tracing::instrument(skip(self))]
pub fn toggle_maximize(&mut self) -> Result<()> {
let workspace = self.focused_workspace_mut()?;

match workspace.maximized_window() {
None => self.maximize_window()?,
Some(_) => self.unmaximize_window()?,
}

self.update_focused_workspace(false)
}

#[tracing::instrument(skip(self))]
pub fn maximize_window(&mut self) -> Result<()> {
tracing::info!("maximizing windowj");

let workspace = self.focused_workspace_mut()?;
workspace.new_maximized_window()
}

#[tracing::instrument(skip(self))]
pub fn unmaximize_window(&mut self) -> Result<()> {
tracing::info!("unmaximizing window");

let workspace = self.focused_workspace_mut()?;
workspace.reintegrate_maximized_window()
}

#[tracing::instrument(skip(self))]
pub fn flip_layout(&mut self, layout_flip: LayoutFlip) -> Result<()> {
tracing::info!("flipping layout");
Expand Down
5 changes: 5 additions & 0 deletions komorebi/src/windows_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ use bindings::Windows::Win32::UI::WindowsAndMessaging::SHOW_WINDOW_CMD;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPIF_SENDCHANGE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SPI_SETACTIVEWINDOWTRACKING;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_HIDE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_MAXIMIZE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SW_RESTORE;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_ACTION;
use bindings::Windows::Win32::UI::WindowsAndMessaging::SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS;
Expand Down Expand Up @@ -259,6 +260,10 @@ impl WindowsApi {
Self::show_window(hwnd, SW_RESTORE);
}

pub fn maximize_window(hwnd: HWND) {
Self::show_window(hwnd, SW_MAXIMIZE);
}

pub fn set_foreground_window(hwnd: HWND) -> Result<()> {
match WindowsResult::from(unsafe { SetForegroundWindow(hwnd) }) {
WindowsResult::Ok(_) => Ok(()),
Expand Down
Loading

0 comments on commit 0725549

Please sign in to comment.