Previously, the implementation of maximized and monocle windows assumed
that the only valid state for them to transition to would be to restore
them to the index that they were maximized/monocle-d from in their host
workspace.
This is not exclusively the case as it is also possible for them to be
closed when they are in a maximized or monocle state.
This commit updates the Workspace.remove_window() fn to also look for
the hwnd to be removed in the monocle container and maximized window, if
they exist.
fix#19
This commit adds two commands to allow the user to send the currently
focused container to a different workspace or monitor as a background
operation, without following the moved container to the destination
workspace or monitor.
resolve#20
Decided there should be a quick way to toggle the native ffm
functionality, it gets especially annoying when trying to click drop
downs from the system tray etc.
re #7
This commit removes the unnecessary eyre dependency and instead uses the
relevant imports from color-eyre.
Additionally, after reading the eyre readme a little more closely, I
have switched out .compat() for the ok_or() combinator function as
suggested here: https://github.com/yaahc/eyre#compatibility-with-anyhow.
This commit pushes as much of the generation logic as possible to the
derive-ahk crate, so that when it is used in komorebic, we only need to
do an as_bytes() call to prepare it for being written to a file.
Besides that, this commit changes the generation command name to
'ahk-library' for clarity, and adds both additional samples and
instructions in the readme file and Scoop post-install hook.
Woke up today and thought this would be a cool way to learn more about
deriving functionality with proc macros.
Hopefully having this wrapper/helper library will make first time
configuration for new users easier.
Silly boolean error meant that if a link was clicked in one
monitor/workspace, the browser, if not on the same workspace, would be
brought into the workspace the link was clicked in.
This was because I was checking if the focused monitor != the known
monitor && the focused workspace != the known workspace, when in fact,
we don't need both of those conditions to be true in order to switch to
where the browser is, we only need one of them to be true.
After changing the && (and) to a || (or), the behaviour is now as
expected, and clicking a link will switch to the workspace where the
browser is open.
I tried running prebuilt binaries from GitHub Actions on a fresh Windows
11 VM and was faced with the error "The code execution cannot proceed
because VCRUNTIME140.dll was not found."
After Googling around a little I think this may help with that issue;
definitely don't want to have to troubleshoot this for non-developers
who just want to use a tiling window manager.
Realised that I hadn't turned on super pedantic mode for clippy in the
komorebi-core and komorebic crates. This commit ensures the same clippy
config across all crates and applies the lint suggestions that arose as
a result of turning on the same config everywhere.
This commit removes the restriction on adding title rules for tray
applications and forcibly managed applications since there wasn't any
good reason for disallowing them.
Also updated the sample config and the readme to add a section for
common first time tips and to remove the big JSON blob showing an
outdated example of output from the state command.
Just a little bit of clean up to make sure that the float rule data
structures match the same emerging pattern as the data structures for
other kinds of rules.
Also some refactoring of Window.should_manage to ensure stricter scoping
where locks are gained on global static variables.
Following on from 8ffe6f78b7, this commit
introduces a command to add rules to forcibly manage windows that don't
get picked up by the rough heuristics that are able to target most
windows for management in Window.should_manage.
Since there is again no overlap (or at least, no undesired overlap)
between executable names and classes, I'll keep both class and exe names
in a single lookup vec.
re #16
Added commands to forcibly manage and unmanage windows if they don't get
picked up for tiling automatically. This commit adds support for running
those operations on the currently focused window, but if there is a need
to specify a hwnd to operate on, that could be added pretty easily too
in the future, though I'd like to keep the complexity of looking up and
passing hwnds to a command out of the CLI if possible.
This commit also fixes an issue with restoring floating windows. I'm not
sure what happened, but at some point, for me at least,
WindowsApi::top_visible_window started returning explorer.exe all the
time, so I've switched this out for WindowsApi::foreground_window.
I have a feeling I was using TopWindow before, thinking it was
GetForegroundWindow, which it isn't, and it wasn't reliable, so I
created the top_visible_window abstraction on top of it, which also
turned out to be unreliable. Anyway, it's working now.
I think the next step will be to create a manage-rule command to
compliment the float-rule command which users can use to handle edge
cases with their apps in their configuration.
re #16
The win_event_hook was still just a messy copy-paste from the yatta
days.
This commit pushes the logic around deciding if we should emit a
WindowManagerEvent::Show from a WinEvent::ObjectNameChange up to the
WindowManagerEvent::from_win_event method.
Now, the win_event_hook is just calling other functions that decide what
to do with the window and passing on the results.
This feature allows users to specify which monitor/workspace an
application's window, identified either by executable name or window
class name, should be assigned to.
A new fn, WindowManager.enforce_workspace_rules, is called whenever a
new rule is added, and periodically whenever an event is processed by
komorebi (just after orphan windows are repead, before the matching and
processing of the specific event).
Both class and exe identifiers are stored in the same HashMap for the
sake of simplicity, as I couldn't think of any situations where there
might be a clash between the two identifiers.
Did some light refactoring of window_manager.rs to make the new()
constructor a static method on the WindowManager struct.
Also fixed a bug in Workspace.new_container_for_window where the focused
index was not getting set correctly when the workspace had no
containers.
An issue was reported in which switching between Windows Virtual
Desktops was causing issues with the layout generation. This was due to
WinEvents being emitted from other Virtual Desktops ending up in the WM
state when they shouldn't.
This commit introduces a check to ensure that the WM will only listen to
events and commands emitted from the Windows Virtual Desktop that it was
started on.
fix#15
I used a parking_lot to detect what I suspected to be the deadlock
resulting in issue #13.
I was pleasantly surprised by the alternative to std::sync::Mutex
provided by parking_lot, especially not having to unlock it to use it,
and of course the excellent and intuitive (even if experimental)
deadlock detector.
I have decided to use parking_lot::Mutex as an almost-drop-in
replacement for std::sync::Mutex, as I expect that this isn't the last
time komorebi will have a deadlocking issue, and I have put the deadlock
detection code which runs in a separate thread behind a
"deadlock_detection" feature.
The actual deadlock itself was solved by scoping the first lock in the
handler for WindowManagerEvent::Hide and then executing any required
operations (some of which, like window.maximize(), may require another
lock on HIDDEN_HWNDS) in a separate scope once the previous lock has
been dropped.
In the future I should look at integrating globals like HIDDEN_HWNDS
into WindowManager in a way that won't lead to double-mutable-borrow
issues.
fix#13
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
This commit adds a log command directly to the komorebic cli to make it
easier for users to check the logs if they don't have tail installed or
are not familiar with it.
A separate logfile with ANSI color codes is now being written to the
user's tempdir, which is tailed by the log command until the process is
halted by a Ctrl-C signal.
For future upgrades of komorebi via Scoop, the proc will most likely be
running on the user's system. This commit adds a pre_install hook to run
'komorebi stop' if komorebi is running at the time of the upgrade.
resolve#11
Some users prefer to use AutoHotKey v2, so this commit adds a check for
both komorebi.ahk and komorebi.akh2 files, and the corresponding AHK
executables, whenever commands around configuration loading are run (on
startup, when manually reloading, and when watching for changes).
If both files exist in the home directory, komorebi.ahk will be
preferred (at least until AHKv2 is out of beta).
An example of a configuration file compatible with AHKv2 by @crosstyan
has been added to the documentation.
resolve#10
This commit adapts a basic GoReleaser configuration to work for Rust
projects, allowing us to automatically create releases on GitHub via
GitHub Actions whenever a semantic version tag (vX.Y.Z) is pushed, with
custom changelogs generated by kokai, and zipped binaries attached to
the release.
Those zipped binaries are then used to create a Scoop release in a
custom bucket.
Due to the way that Scoop uses shims, when running the 'komorebic start'
command, there needs to be an explicit check to try and determine if
komorebi has been installed via Scoop. This is done by checking for a
komorebi.ps1 shim in the Path.
Scoop shims cannot be used with the Start-Process PS command, so
instead, we replicate in code what the komorebi.ps1 script is doing
(finding the path to the current version of the executable), and then
passing the entire path to the Start-Process command that gets called to
start komorebi.
The README has been updated to reflect the availability of prebuilt
binaries and how to get started with them.
Following on from the last commit, I jumped on the Rust Community
Discord and @danielhenrymantilla was kind enough to show me a working
example of how an optional macro argument can be used together with
cfg_attr to conditionally add a doc comment.
I took his example and reworked it a little, in the process refactoring
all the macros in the komorebic crate to give them a sense of internal
consistency.
The latest clap beta introduced a lot of breaking changes for komorebic,
so I decided it was a good time to refactor a little and add
documentation to all of the cli commands.
The primary change for komorebic is that subcommands now only take
structs as arguments, so every enum must be wrapped in a struct. Some
macros have been introduced to ease this.
Using on|off alongside enable|disable for BooleanState arguments has
been deprecated, going forward only enable|disable will be supported.
The commands to introduce float rules have been refactored to make use
of ApplicationTarget, and a single command 'float-rule' has been
introduced in the cli.
Finally I took some time to standardise the sample AHK config a little,
primarily making sure that command prompt windows are never shown for
any of the configuration commands.
BREAKING CHANGE: float-exe, float-class, and float-title have been
deprecated in favour of float-rule in komorebic. workspace-tiling now
only accepts enable|disable as valid inputs to the final arg,
deprecating the previously also valid on|off.
re #8
Clap just released a new beta which breaks a bunch of stuff. Not looking
forward to digging through the changes to do that update.
For now, users can run 'cargo install --locked' to ensure that the
previous beta of Clap is used, thus allowing them to continue compiling
from source.
resolve#8
Issue #6 highlighted a workflow that I don't personally use, but I am
sure is common among other Windows users, which is to use the Close
button to minimize an application to the tray.
Since this is largely a configurable option in those applications
(Discord etc.), I have implemented a command for the user to identify
those applications themselves when configuring the window manager,
instead of adding them to the previous Vec of known multi-window
applications that need to be identified by default.
Close/minimize to tray applications can be identified either by their
class or their executable name.
I figure it is pretty important to know the rules defined on the window
manager instance, so I have exposed these on a new window_manager::State
struct which is now what get returns from the 'komorebic.exe state'
command.
resolve#6
Adds two new commands that enable the manual reloading of an AHK config
file in the default location and the watching and automatic reloading of
an AHK config file in the default location.
There are some applications such as Firefox where, if they are focused
when a workspace switch takes place, an additional Show event will be
fired. This results in the window being associated with both the
original workspace and the workspace being switched to.
This commit adds a check when handling WindowManagerEvent::Show to try
and ignore events from windows that are already known to be associated
with other workspaces (which are not the currently focused workspace).
The two bugs raised in issues #1 and #2 were due to panics that were not
visible in the logs, which left the process hanging and unresponsive,
ultimately needing to be force killed with a command like 'Stop-Process
-Name komorebi'.
The only way to even verify that a panic had taken place and what the
panic related to, was to run '$env:RUST_BACKTRACE ='full';
komorebi.exe', wait for the panic, then restore the now-hidden window
with 'komorebic restore-windows' to finally see the panic message.
This commit integrates an example from the 'tracing' repo, which
through the addition of a panic hook, logs out panics as errors.
Hopefully this will debugging much easier in the future.
re #1, re #2
While investigating issue #2 I was able to reproduce it and view the
panic that causes the komorebi process to become non-responsive.
When switching to a columnar layout (which is the default for the 2nd
workspace in the sample ahk config), there is the possibility to cause a
divide by zero panic if the len passed to Layout::calculate is 0.
I have remedied this by changing the type of len from usize to
NonZeroUsize, and also by ensuring that Layout::calculate is only called
from within the komorebi crate if the workspace has at least one
container.
While moving containers around I also noticed that creating a new
container for a window may also cause a panic if focused_idx + 1 is
greater than the length of the VecDeque of containers, so this was
addressed by pushing to the back of the VecDeque in that case.
re #2
Noticed in the logs when looking at issue #2 that an "Access is denied.
(os error 5)" error was being reported when trying to attach to the
thread of the special Desktop Window, which only happens when switching
to a workspace which doesn't contain any windows. Calling
WindowsApi::set_foreground_window on the HWND directly seems to be the
better option here.
Added two commands, 'komorebic toggle-tiling' and 'komorebic
workspace-tiling MONITOR_IDX WORKSPACE_IDX on|off' which allow for
tiling on the currently focused workspace to be toggled on and off, and
for the tiling for a specific workspace to be set to on or off (useful
if you want a specific workspace to always have tiling set to off at
startup).
resolve#5
This commit adds a new command, 'komorebic.exe new-workspace', which
will append a new, empty workspace, to the list of workspaces on the
currently focused monitor, and then switch focus to it.
Also took the opportunity to clean up some unnecessary unwraps in
komorebic/src/main.rs.
resolve#4
When AutoHotKey is detected, and %USERPROFILE%\komorebi.ahk exists,
komorebi.exe will now try to run the ahk script after starting the
command listener.
For this to work smoothly, it is important to set the #SingleInstance
directive to Force in komorebi.ahk, which will ensure that duplicates of
the script are not run, and a new version of the script is loaded
without displaying a GUI confirmation prompt.
resolve#3
A user, possibly using multiple monitors, reported a panic on startup
which I traced back to an unchecked remove op on a Vec. Spent so much
time working with VecDeque that I forgot that removing from a Vec panics
instead of returning an Option<T>.
This change ensures that when trying to remove a container's resize
dimensions in a workspace, we check that the container actually has a
corresponding resize dimension before calling remove.
Similarly, in order to ensure consistency with workspace updates which
always resize the length of the resize dimensions to match the length of
the number of container layouts, sets the length of the resize
dimensions array when initialising a workspace in
WindowsApi::load_workspace_information.
fix#1
This commit introduces the getset crate to reduce a lot of the
boilerplate, especially in workspace.rs, around different variations of
getters. Hopefully this will make the codebase easier to navigate for
contributors in the future.
Also trying to avoid pinning to patch versions and minor versions
wherever possible.
Using macros to generate common type-specific Ring element accessors.
Should make codebase navigation a little easier my making logic-heavy
functions more visible in impl blocks.
Previously resize constraints on odd and even container numbers were not
being enforced consistently. Now, instead of trying to enforce them on
individual operations, every time an update operation is called for a
workspace, the resize constraints will be enforced before trying to
calculate and apply an updated layout.