This commit makes it so a floating window only has the floating border
when it is focused, if not it has the `Unfocused` border. It also makes
the 'focused_container' have the `Unfocused` border when it is not the
foreground window, for example when we have a floating window focused
instead.
This commit also changes the border's `window_kind` so that the stored
borders actually have that value so we can check it later (This value
wasn't being updated).
This commit also makes it so we properly invalidate the borders in the
situations discussed above (for example when changing focus to/from a
floating window we need the floating window border to update its ZOrder
as well as the previously focused window).
Lastly this commit, changes the `WM_PAINT` code part of the border so
that it now sets the position of border so that the border's ZOrder
updates to it's tracking window ZOrder.
This commit introduces a number of changes to the border manager module
to enable borders to track the movements of windows as they are being
animated.
As part of these changes, the code paths for borders to track user
movement of windows have also been overhauled.
The biggest conceptual change introduced here is borrowed from
@lukeyou05's work on tacky-borders, where the primary event listener of
the komorebi process now forwards EVENT_OBJECT_LOCATIONCHANGE and
EVENT_OBJECT_DESTROY messages from application windows directly on to
their borders.
These events are handled directly in the border window callbacks,
outside of the main border manager module event processing loop.
In order to handle these events more performantly in the border window
callbacks, a number of state trackers have been added to the Border
struct.
When handling EVENT_OBJECT_NAMECHANGE, these values are read directly
from the struct, whereas when handling WM_PAINT, which is sent by the
system whenever we invalidate a border window, we update the state
values on the Border structs from the various atomic configuration
variables in the mod.rs file.
Another trick I borrowed from tacky-borders is to store a pointer to the
Border object alongside a border window whenever it is created with
CreateWindowExW, which can be accessed within the callback as
GWLP_USERDATA.
There is some unfortunate introduction of unsafe code to make this
happen, but the callback uses null checks to exit the callback early to
ensure (to the best of my ability) that there are no pointer
dereferencing issues once we start making border changes in the context
of the callback.
There are a few other Direct2D related optimizations throughout this
commit, mainly avoiding the recreation of objects like brush properties
and brushes.
Finally, the border_z_order option is now deprecated as the border
window is now tracking the z-ordering of the application window it is
associated with by default - this should resolve a whole host of subtle
border z-ordering issues, especially when dragging windows around using
the mouse.
This work would not have been possible without the guidance of
@lukeyou05, so if you like this feature, please make sure you thank him
too!
This commit overhauls the "Komorebi" borders implementation to use
Direct2D, which enables anti-aliasing for rounded borders.
A lot of the heavy lifting was done by @lukeyou05 in the tacky-borders
project, which this commit largely adapts to komorebi. @lukeyou05
provided an incredible amount of guidance and feedback on the
implementation of this feature on the komorebi Discord.
This commit is a squashed interactive rebase of the following commits:
238271a71e
feat(borders): initial impl of direct2d border drawing
5525a382b9
feat(borders): avoid multiple render target creation calls
431970d7b6
feat(borders): reduce redraws to improve perf
47cb19e54a
feat(borders): remove black pixels around direct2d corners
3857d1a46c
feat(borders): clean up render targets on destroy
Not a huge fan of these updates in the windows-rs crate which swap the
isize values which were previously wrapped in various handles with *mut
core::ffi:c_void pointers.
In order to at least keep this codebase sane, all of the wrapper
functions exposed in WindowsApi now take isize wherever they previously
took HWND, HMONITOR, HINSTANCE etc.
Going forward any pub fn in WindowsApi should prefer isize over Windows
handles which wrap c_void pointers.
This commit demotes the komorebi-core crate to a module (core) inside of
the komorebi lib, resulting in the komorebi-client crate lib becoming
the single public interface for programming in Rust against komorebi.
komorebic and komorebi-gui now consume komorebi-client exclusively as
the means for sending and receiving messages to and from komorebi, so
that anyone wishing to integrate with komorebi will have all of the same
functionality to them as I do.
This commit adds the ability for users to select between komorebi's
implementation of borders with variable widths and the native Windows 11
implementation of thin "accent" borders.
The new border_implementation configuration option defaults to
BorderImplementation::Komorebi in order to preserve compat with existing
user configurations.
This option will be most useful for people who prefer ultra-thin
borders, and people who want borders to be animated along with
application windows if they are using the animation feature being worked
on in PR #685.
This commit ensures that message handling threads for windows that are
created by komorebi are exited properly when the windows are destroyed.
For some reason, passing the HWND returned by CreateWindowExW to
GetMessageW continues returning TRUE even after the window has been
destroyed.
If HWND::NULL (via the Default trait) is passed to GetMessageW, which
retrieves messages for any window belonging to the current thread (our
threads here only own a single window), GetMessageW returns FALSE as
expected after the window is destroyed.
re #862
This commit ensures that we only invalidate a border rect to send a
WM_PAINT message either when the position of the focus state of the
border has changed.
re #862
This commit pushes up the calls to BeginPaint and EndPaint in the border
callback function to ensure that the client area of the border rect is
always validated after calls to InvalidateRect from the update fn.
The callback now also logs errors whenever it is not possible to get the
border rect to operate on for any reason.
There was a call at the end of this logic to ValidateRect which has been
removed as the validation is already handled by the call to BeginPaint.
re #862
This commit ensures that HPEN and HBRUSH objects created to draw window
borders are explicitly destroyed with calls to DeleteObject after
EndPaint has been called.
re #855
This commit renames a number of border-related code refs, removing the
ActiveWindow prefix since these borders are no longer just for the
active window.
Aliases have been added to preserve backwards compat for existing
configs.
An example AHK configuration file has been added to the Common Workflows
section of the docs site.
A link to the docs site has been added to the output of komorebic start.
A note has been added recommending that users disable system animations
for the best experience in the Getting Started guide.
This commit ensures that both windows and borders will make a comparison
to the current values returned by WindowRect before attempting to make
update calls to SetWindowPos. This should greatly reduce visual
flickering in both the borders and sensitive apps like JetBrains IDEs.
This commit ensures that if and when either the border_manager or
workspace_reconciliator notification handlers crash in their respective
threads, they will be restarted instead of killing the whole thread.
This is acheived via a loop running in the spawned threads of both
listeners, which will report whichever error killed the notification
handler functions.
Clippy annotations to deny expect and unwrap calls have been added to
these two modules.
Calls to DestroyWindow for expired borders were sporadically failing in
unpredictable circumstances, so these have been switched out with calls
to CloseWindow which has been working well so far with the stackbar
feature.
This commit ensures that even border hwnds that may now be untracked as
a result of events such as monitor changes will now be reaped in the
destroy_all_borders function.
This commit removes all border-related code from process_command,
process_event etc. and centralizes it in the new border_manager module.
Instead of trying to figure out where in process_event and
process_command we should make border-related changes, a notification
gets sent to a channel that border_manager listens to whenever an event
or command has finished processing.
The border_manager listener, upon receiving a notification, acquires a
lock on the WindowManager instance and updates borders for the focused
workspace on every monitor; this allows us to centralize all edge case
handling within the border_manager listener's loop.
Borders on workspaces that lose focus are now destroyed and recreated
when those workspaces regain focus, instead of trying to share
individual border instances across workspaces.
A number of common edge cases that have been addressed in this commit
are:
* Paused window manager
* Floating workspaces
* Maximized windows
* Fullscreen videos
* Monocle containers
* Ghost borders on workspace switching
* Incorrect focused window border colours
Global state related to borders has also been moved into the
border_manager module, which also tracks the state of border objects
(BORDER_STATE), their rects (RECT_STATE) and their focus kinds
(FOCUS_STATE).
This allows us to now track multiple borders per-container, enabling
unfocused border windows for the first time.
Additionally, the Z-Order for border windows is now also configurable.
ActiveWindowBorderColours has been expanded to include Unfocused, but in
order to not introduce a breaking configuration change for end users,
all members of this struct have been made Option<Colour>.