Commit Graph

1290 Commits

Author SHA1 Message Date
dependabot[bot]
c48e1db0ff chore(deps): bump random_word from 0.4.3 to 0.5.0
Bumps [random_word](https://github.com/MitchellRhysHall/random_word) from 0.4.3 to 0.5.0.
- [Commits](https://github.com/MitchellRhysHall/random_word/commits)

---
updated-dependencies:
- dependency-name: random_word
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 08:32:59 -07:00
alex-ds13
ea9752d5e1 fix(wm): focus correct window if monocled stack
Previously if we had a stack on a monocle container and tried to cycle
stack or move the window within the stack or even using the focus stack
window from a bar it would focus the wrong window and temporarely show
that wrong window. This commit fixes this.
2025-03-10 08:23:49 -07:00
Csaba
acf780767c feat(bar): add changing icons based on the time
This commit adds a little Easter egg on the time widget.

Use the `changing_icon` setting to enable this feature.

Based on the current time, the widget will use different icons to
indicate certain activities of the day.

00:00 MOON
06:00 ALARM
06:01 BREAD
06:30 BARBELL
08:00 COFFEE
08:30 CLOCK
12:00 HAMBURGER
12:30 CLOCK_AFTERNOON
18:00 FORK_KNIFE
18:30 MOON_STARS
2025-03-09 11:48:46 -07:00
Jerry Kingsbury
8e588d0284 test(wm): add workspace and monitor tests
Added tests to test switching monitor focus, setting workspace name, and
monitor size.
2025-03-08 13:49:51 -08:00
dependabot[bot]
91ff9b8852 chore(deps): bump ring from 0.17.11 to 0.17.13
Bumps [ring](https://github.com/briansmith/ring) from 0.17.11 to 0.17.13.
- [Changelog](https://github.com/briansmith/ring/blob/main/RELEASES.md)
- [Commits](https://github.com/briansmith/ring/commits)

---
updated-dependencies:
- dependency-name: ring
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-07 16:47:46 -08:00
Csaba
81a7951312 feat(bar): added timezone to time and date widgets
This commit adds the timezone on the time and date widgets as a new
setting.

In case the timezone is invalid, the output is replaced with an error
message.

Use a custom format to display additional information.

resolve #1312
2025-03-07 16:28:45 -08:00
LGUG2Z
555308db5f test(wm): don't rely on datadir for socket location
This commit avoids relying on DATA_DIR for the test socket location as
this path is not available to us in GitHub Actions runners.
2025-03-07 16:10:16 -08:00
LGUG2Z
c90769f5fa chore(deny): allow unmaintained crate paste 2025-03-07 16:01:41 -08:00
LGUG2Z
76002385ab test(wm): colocate tests with src files, add socket msg test 2025-03-06 19:39:59 -08:00
Jerry Kingsbury
f40e80cd61 test(wm): add window manager unit tests
Created a test that creates the WM instance and ensures the instance is
running. The test creates a custom socket and then cleans up the socket
file after completion.

Created a test that creates a WM instance, monitor instance, and
workpace. The tests checks to ensure that the expected workspace is
focused properly.

Included recommended fixes to ensure that the focus_workspace function
is used correctly and that the test accurately checks the workspaces
length, current workspace index, and switching to an existing workspace.
2025-03-06 15:34:30 -08:00
LGUG2Z
e4f9d8af86 chore(deps): add deny.toml and dependencies.json 2025-03-06 15:34:27 -08:00
LGUG2Z
0c64432c25 feat(cli): add cycle-empty-workspace cmd
This commit adds a variation of the cycle-workspace command which will
attempt to focus the next empty workspace in the given direction (with
the usual wraparound). If there are no empty workspaces available, this
command will do nothing.
2025-03-05 15:52:21 -08:00
LGUG2Z
fe20caa56a fix(client): expose schemars feature and allow opt-out 2025-03-05 15:49:46 -08:00
Csaba
02a2796e7d fix(bar): correct time widget binary format alignment
This commit adds a bit of offset to the time widget's binary clocks so
they are more in the middle.

It also fixes some visual changes that were caused by upgrading to
eframe 0.31
2025-03-04 14:57:21 -08:00
LGUG2Z
a0eb025cec fix(bar): add opt schemars derive on test case 2025-03-04 08:39:47 -08:00
alex-ds13
70a61376a8 fix(borders): ignore borders on all stack window updates 2025-03-04 08:20:35 -08:00
LGUG2Z
1761919707 perf(client): disable schemars feature on komorebi lib dep 2025-03-03 21:49:43 -08:00
LGUG2Z
1325da4e81 chore(deps): cargo update 2025-03-03 21:46:03 -08:00
alex-ds13
0776ca1565 feat(border): cache borders on all workspaces
This commit refactors the `border_manager` with the following changes:

- Rework the way the borders are created and their pointer is sent to
  the window message handler. Now we also store the same pointer as a
  `Box<Border>` on the `border_manager`'s `BORDER_STATE`. This means
  that the borders we have are exactly the same ones that the border
  window itself is accessing so we can now store the border's info
  inside the `Border` struct and all of it will be accessible by the
  border window as well. This makes it so the "ACTUAL" border struct is
  the one created on the thread of the `Border::create()` function and
  when that thread finishes (after the border window closes) it will
  handle the drop of the border itself. However this means we need to be
  careful with our own stored `Box<Border>` since it will point to the
  same memory we can't let the compiler dropped them as usual or
  otherwise it would create heap corruption errors. So this commit
  creates a special function called `destroy_border()` to actually close
  the window border without dropping the border, since it will be later
  dropped by the thread that created it.

- Remove `BORDERS_MONITORS`, `FOCUS_STATE` and `RENDER_TARGETS` arc
  mutexes, since now this info is stored on the border itself.

- Change the `BORDER_STATE` to now map an id (container or window) to a
  `Box<Border>`.

- Change the `WINDOWS_BORDERS` to now map a window hwnd to a border id.

- Create new struct `BorderInfo` which as the border window hwnd and the
  border kind. This struct is what is now returned by the function
  `window_border()` which checks if some window as a border attached to
  it and if it does it returns this info. There is no need to clone the
  entire border. If in the future we need more info we can just add it
  to this struct.

- Change the way we clear the `BORDER_STATE`. Like mentioned before we
  need to be sure we don't drop the `Box<Border>` when removing it, so
  now we use the `.drain` function to remove all the borders as an
  iterator and we call the `destroy_border()` on each border we are
  removing.

- We now check if a border's `tracking_hwnd` has changed and if it does
  we simply update it instead of destroying the border and create a new
  one.

- Create function `delete_border` so that we can remove a border
  properly from outside the `border_manager`.

- Create function `hide_border` which spawns a new thread that searches
  if a window hwnd has a border attached to it and if it does it hides
  said border window. This function is called on every `window.hide()`.

- Create function `show_border` which spawns a new thread that searches
  if a window hwnd has a border attached to it and if it does it restores
  said border window. This function is called on every `window.restore()`.

- This commit also changes the previous `window.hide()` and
  `window.restore()` functions to be named:
  - `window.hide_with_border(hide_border: bool)`: this is the same
    function as before but adds a check at the end in case `hide_border`
    is true it calls `border_manager::hide_border()`. A new function was
    created with the same name as before `window.hide()` which by
    default calls this new function with `hide_border = true`.
  - `window.restore_with_border(restore_border: bool)`: this is the same
    function as before but adds a check at the end in case `hide_border`
    is true it calls `border_manager::hide_border()`. A new function was
    created with the same name as before `window.hide()` which by
    default calls this new function with `hide_border = true`.

- This commit creates a new function on `Container` called
  `load_focused_window_ignore_borders()` which performs the same as
  `load_focused_window()` but it ignores the borders when hiding and
  restoring the windows. This function, along with the
  `hide_with_border(false)` and `restore_with_border(false)` are used on
  all functions related to changing focus on a stack since if we let the
  borders be hidden and restored when cycling or changing focus on a
  stack the border would flicker slightly, this prevents that. Ignore
  borders when clicking on the stackbar as well.
  (P.S. there might still be other places that I forgot to use these new
  functions, but if that is the case then what will happen is a simple
  flicker of the stack border...)

- The `remove_window` from `Workspace` needs to call the
  `border_manager::delete_border()` so that wew make sure we remove that
  windows's border window as well if it exists. This is essential when
  enforcing workspace rules, otherwise the border would be left behind.

- Lastly, but not least, now that we hide the borders windows along with
  their tracking window, we no longer remove the borders when swapping
  workspaces or when toggling monocle, etc. Instead we keep all borders
  of all workspaces cached and simply hide them. They are only removed
  when their tracking window is closed or cloaked on a stack (since on a
  stack we only keep one border for all the entire stack container).
  This means that when changing between workspaces we no longer see the
  borders showing up delayed after the windows show up. Now both the
  window and it's border show up as if they are one and the same.
2025-03-03 21:15:45 -08:00
alex-ds13
724b0b7692 fix(border): update border when moving from admin windows 2025-03-03 21:13:37 -08:00
LGUG2Z
b53de81754 perf(cargo): make schemars derives optional
This commit makes all schemars::JsonSchema derives optional. After
analyzing the output of cargo build timings and llvm-lines, it was clear
that the majority of the 2m+ incremental dev build times was taken up by
codegen, and the majority of it by schemars.

Developers can now run cargo commands with --no-default-features to
disable schemars::JsonSchema codegen, and all justfile commands have
been updated to take this flag by default, with the exception of the
jsonschema target, which will compile with all derives required to
export the various jsonschema files.

Incremental dev build times for komorebi.exe on my machine are now at
around ~18s, while clean dev build times for the entire workspace are at
around ~1m.
2025-03-03 21:12:46 -08:00
Csaba
a837fea40c feat(bar): add icons to workspace-layer widget
This commits adds the ability to set icons for the `workspace-layer` with
the `DisplayFormat` and a setting to specify if it should `show_when_tiling`
or not.

collab with @alex-ds13
2025-03-03 08:13:29 -08:00
LGUG2Z
dd577c0eb3 fix(wm): preserve resize dimensions on offset toggle
This commit ensures that the resize dimensions will be reserved for
other monitors and workspaces when the
toggle-window-based-work-area-offset command is used.
2025-03-03 08:13:29 -08:00
Csaba
7d497c3e14 fix(bar): always add stroke on selected_frame
This commit fixes a breaking change on the selected_frame that was
introduced by eframe version 0.31.

In this version, the stroke is drawn on the inside of a Frame instead it
being drawn on the outside like before.

This now means that a stroke needs to be added on all the states of the
Frame in order to avoid all the elements to be moving around on hover.
2025-03-03 08:13:29 -08:00
LGUG2Z
e6398c29f8 docs(readme): add active individual commercial use licenses count 2025-02-27 18:15:43 -08:00
LGUG2Z
ca893140f5 chore(deps): bump eframe to 0.31 2025-02-27 16:50:59 -08:00
alex-ds13
b26910aa58 fix(wm): allow stacking in all dirs, improve stack border rendering
Previously the stacking logic would sometimes change the focused
container without actually changing focus to said container.

This resulted in the stack showing up with an unfocused border even
though we had focus on one windows belonging to the stack (just not the
right one).

Also before it wasn't possible to stack windows on some directions
when we were already on a stack.

This commit fixes both issues.
2025-02-26 19:23:40 -08:00
alex-ds13
487c217497 fix(borders): address memory leaks
This commit fixes multiple issues with the borders which were resulting
in multiple borders being created and not completely destroyed which
meant that the amount of borders in memory kept increasing indefinitely
the more we used komorebi. To do so this commit does the following:

- Clear all the maps on `destroy_all_borders`.

- Create function `remove_border` which should always be used when we
  want to remove a border since this function destroys the border window
  and removes all its related data and clones from all the maps.

- Create function `remove_borders` which should always be used when we
  want to remove multiple borders or filter the current existing
  borders. It takes in a `condition` function that takes a ref to the
  border's container id and a ref to the border and should return a
  bool. This function is then applied to each existing border and if it
  evaluates to true it will call `remove_border` on it.

- Apply these new functions on all the code that was previously manually
  removing borders.

- When a container is a stack, we now check it's unfocused windows, in
  case they had borders attached to them we remove them, otherwise these
  borders would persist and be drawn below other borders.

- We now check if a container's border was previously tracking a different
  window, if it was we destroy that border and remove it's hwnd from the
  `FOCUS_STATE` and then we check if its `tracking_hwnd` still points to
  the same border and remove it if it does (if it doesn't that means
  that a new border was already attached to that window so we don't
  remove it).

  We don't call `remove_border` here since we don't want to actually
  remove the border but instead we replace it with a new one tracking
  the correct window. (I've tried updating the `tracking_hwnd` instead
  of destroying the border and creating a new one but that didn't work
  since it still kept tracking the previous window...).
2025-02-26 19:22:22 -08:00
alex-ds13
4031fbf033 feat(wm): move all windows on ws layer toggle
This commit makes it so when you toggle between workspace layers it
moves all windows of that layer to the top of the z-order.

So if you move to `Floating` layer, then all floating windows are moved
to the top, and if you go back to the `Tiling` layer, all tiled
containers are moved to the top.
2025-02-26 19:21:33 -08:00
LGUG2Z
dadc40777f chore(deps): bump shadow-rs from 0.38 to 1 2025-02-24 21:12:31 -08:00
Csaba
59544edb74 fix(bar): use accent color for active widget components
This commit makes sure that the accent color is used on certain bar
components, such as active workspace, selected layout and focused
window.

This will now make these components stand out even more for a better
visual indication.

fix #1288
2025-02-24 17:36:26 -08:00
LGUG2Z
5e2c18cad3 chore(deps): bump win32-display-data 2025-02-24 17:28:50 -08:00
Csaba
d69dfeb715 feat(bar): add opts to show all icons on workspace widget
This commit adds 3 new display options on the komorebi workspace widget
to show all icons.
2025-02-24 08:12:07 -08:00
LGUG2Z
3a208b577c chore(just): add wpm target 2025-02-23 20:24:30 -08:00
alex-ds13
20817b094d fix(wm): prevent floating focus change event infinite loops
This commit stops the `FocusChange` event from focusing a floating
window when it is the one emitting said `FocusChange` event, since it's
not needed and is the cause of some flicker bugs reported!

If that floating window was the one emitting the `FocusChange` event
then it means it is already the foreground window, and there is no
reason for us to focus the window again (since that will create an
infinite loop of events).

When the window emitting this event is not floating we don't try to
focus the window, we simply set the focus index for the container of
that window and the focused index for the window of that container.

Except in one case, which is if the workspace has a monocle container,
then it does focus a window, it focuses the monocle window to make sure
the monocle keeps showing in front of everything and doesn't let
anything come in front of it.
2025-02-23 20:16:32 -08:00
alex-ds13
394709e356 fix(reaper): avoid deadlocks at startup
Previously the reaper at startup would lock it's own `HWNDS_CACHE` and
then try to lock the WM to get its `known_hwnds`.

However if there was an event in the meantime, process_event would lock
the WM first and then it would try to lock the reaper's `HWNDS_CACHE` to
update it.  This would deadlock since that would be locked by the reaper
waiting for the WM lock to be released.

This commit now makes it so we pass the `known_hwnds` to the reaper as
an argument at startup and it also rearranges the order of loading the
listeners.

Now komorebi first loads all the manager-type listeners, and only
afterwards does it load the commands and events listeners.

After some testing this seems to be the best order that doesn't cause
any issues at all!

There were some other issues that I've noticed before when starting
komorebi while having other 3rd parties trying to subscribe to it (like
komorebi-bar and YASB), which would make those subscribers lock the
`process_command` thread. This doesn't seem to be happening on my tests
anymore with this new order.
2025-02-23 20:10:29 -08:00
alex-ds13
990a339d4e fix(bar): apply work area offset on monitor reconnect
This commit makes sure the bar applies the the `work_area_offset`
correctly after a monitor reconnects; if it is the first time a monitor
is connecting, the cached offset won't exist yet.
2025-02-23 15:25:19 -08:00
alex-ds13
f0222dd4ab fix(wm): properly load monitor on first connect
This commit fixes an issue where if you started komorebi without a
monitor connected, and then connected it later, it wasn't properly
loading the data returned from `win32-display-data`.
2025-02-23 15:24:15 -08:00
LGUG2Z
974e5a2b20 refactor(bar): add extend_enum! macro
This commit adds an extend_enum! macro and some unit tests to provide
an ergonomic way to specialize configs for specific (sub)widgets.

The macro takes the name of the existing enum, the desired name of the
new, extended enum, and a list of variants which should be added to the
extended enum.

The macro will add new variants to a new enum definition first, before
wrapping the existing enum in an Existing variant and tagging it with
the `#[serde(untagged)]` annotation.

From is also implemented for ergonomic from/into calls between shared
variants of the existing and extended enums.
2025-02-23 12:16:00 -08:00
alex-ds13
2bbc269b9f feat(wm): add padding per monitor
This commit adds the ability to set container and workspace padding per
monitor.

To do so (and to simplify any future need of changing some value per
monitor), and have it pass through to each workspace a new field was added
to `Workspace` called `globals` which has a new struct called
`WorkspaceGlobals`.

`WorkspaceGlobals` includes any global values that might be needed by
the workspace.

This field is updated by the monitor for all its workspaces whenever the
config is loaded or reloaded. It is also updated on `RetileAll` and on
the function `update_focused_workspace`.

This should make sure that every time a workspace needs to use it's
`update` function, it has all the `globals` up to date!

This also means that now the `update` function from workspaces doesn't
take any argument at all, reducing all the need to get all the
`work_area`, `work_area_offset`, `window_based_work_area_offset` or
`window_based_work_area_offset_limit` simplifying the callers of this
function quite a bit.

Lastly this commit has also (sort of accidentaly) fixed an existing bug
with the `move_workspace_to_monitor` function.

This was previous removing the workspace from a monitor, but wasn't
changing it's `focused_workspace_idx`, meaning that komorebi would get
all messed up after that command. For example, the `border_manager`
would get stuck and the komorebi-bar would crash.

Now, the `remove_focused_workspace` function also focuses the previous
workspace (which in turn will create a new workspace in case the removed
workspace was the last workspace).
2025-02-23 09:52:33 -08:00
alex-ds13
13ee42276d fix(wm): hide/restore floating windows on monocle toggle 2025-02-23 06:51:42 -08:00
alex-ds13
3641ce6b42 fix(wm): take layer into account on ws restore
Previously if a workspace had any floating windows it would always focus
the first one when restoring. Now it only focus the floating window if
the workspace layer is `Floating`.
2025-02-23 06:51:14 -08:00
LGUG2Z
9d41a293f6 feat(wm): add tiling and floating ws layers
This commit introduces an implementation of workspace layers to
komorebi.

Workspace layers change the kinds of windows that certain commands
operate on. This implementation features two variants,
WorkspaceLayer::Tiling and WorkspaceLayer::Floating.

The default behaviour until now has been WorkspaceLayer::Tiling.

When the user sets WorkspaceLayer::Floating, either through the
'toggle-workspace-layer' command or the new bar widget, the 'move',
'focus', 'cycle-focus' and 'resize-axis' commands will operate on
floating windows, if the currently focused window is a floating window.

As I don't have 'cycle-focus' bound to anything, 'focus up' and 'focus
down' double as incrementing and decrementing cycle focus commands,
iterating focus through the floating windows assigned to a workspace.

Floating windows in komorebi belong to specific workspaces, therefore
commands such as 'move' and 'resize-axis' will restrict movement and
resizing to the bounds of their workspace's work area (or more
accurately, the work area of the monitor that the workspace belongs to,
as floating windows are never constrained by workspace-specific work
area restrictions).
2025-02-22 15:57:22 -08:00
LGUG2Z
1756983978 build(cargo): add custom build profiles 2025-02-22 15:57:17 -08:00
alex-ds13
3d327c407c perf(reaper): switch to channel notifications
This commit changes the way the reaper works.

First this commit changed the `known_hwnds` held by the `WindowManager`
to be a HashMap of window handles (isize) to a pair of monitor_idx,
workspace_idx (usize, usize).

This commit then changes the reaper to have a cache of hwnds which is
updated by the `WindowManager` when they change. The reaper has a thread
that is continuously checking this cache to see if there is any window
handle that no longer exists. When it finds them, the thread sends a
notification to a channel which is then received by the reaper on
another thread that actually does the work on the `WindowManager` by
removing said windows.

This means that the reaper no longer tries to access and lock the
`WindowManager` every second like it used to, but instead it only does
it when it actually needs, when a window actually needs to be reaped.
This means that we can make the thread that checks for orphan windows
run much more frequently since it won't influence the rest of komorebi.

Since now the `known_hwnds` have the monitor/workspace index pair of the
window, we can simply get that info from the map and immediately access
that monitor/workspace or use that index info.
2025-02-22 12:29:27 -08:00
alex-ds13
e5fb5390a8 feat(wm): strip unncessary info from state 2025-02-22 12:28:30 -08:00
alex-ds13
6a8e362c21 refactor(wm): make workspace fields public 2025-02-22 12:28:23 -08:00
alex-ds13
1edeb44203 fix(wm): include workspace rules on cached monitor
The `WorkspaceConfig` stored on `Workspace` was changed to not be
serialized, however it needs to be serialized and deserialized when
caching a monitor (after a disconnect), so that when it reconnects it is
able to read all the workspace rules, which include:

- initial_workspace_rules
- workspace_rules
- window_container_behaviour_rules
- layout_rules
- custom_layout_rules

This commit changes the serde skip to only skip if is is `None`.

This means that the `komorebic state` command will have all this
information as well and it will send it when notifying subscribers too,
which isn't good at all, so we need to find another way of excluding it
from the state.
2025-02-22 12:27:55 -08:00
LGUG2Z
8bc04f0610 chore(deps): bump windows-rs from 0.58 to 0.60 2025-02-21 20:16:50 -08:00
LGUG2Z
30c22f51c9 feat(cli): add toggle-window-based-work-area-offset cmd
This commit adds a command to toggle the application of a monitor's
window-based work area offset for the focused workspace.

resolve #1285
2025-02-20 20:38:12 -08:00