[PR #1306] [CLOSED] feat(border): cache borders #1313

Closed
opened 2026-01-05 14:54:51 +01:00 by adam · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/LGUG2Z/komorebi/pull/1306
Author: @alex-ds13
Created: 2/28/2025
Status: Closed

Base: masterHead: feat(border)/cache-borders


📝 Commits (2)

  • b0b4292 fix(border): update border when moving from admin windows
  • 38325f9 feat(border): cache borders on all workspaces

📊 Changes

11 files changed (+250 additions, -205 deletions)

View changed files

📝 komorebi-client/src/lib.rs (+1 -0)
📝 komorebi/src/border_manager/border.rs (+21 -19)
📝 komorebi/src/border_manager/mod.rs (+178 -171)
📝 komorebi/src/container.rs (+11 -0)
📝 komorebi/src/core/mod.rs (+2 -0)
📝 komorebi/src/stackbar_manager/stackbar.rs (+2 -2)
📝 komorebi/src/window.rs (+21 -6)
📝 komorebi/src/window_manager.rs (+2 -2)
📝 komorebi/src/windows_api.rs (+1 -1)
📝 komorebi/src/windows_callbacks.rs (+8 -4)
📝 komorebi/src/workspace.rs (+3 -0)

📄 Description

1st Commit:

  • Bug fix: focusing an admin window like the TaskManager, makes the focused container have the unfocused border (as it should since it no longer has focus), however if you focused that same container the border would still show as unfocused, since for komorebi nothing changed at all. This commit fixes that and makes the border update correctly.

2nd Commit:

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 drop 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. (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 we 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.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/LGUG2Z/komorebi/pull/1306 **Author:** [@alex-ds13](https://github.com/alex-ds13) **Created:** 2/28/2025 **Status:** ❌ Closed **Base:** `master` ← **Head:** `feat(border)/cache-borders` --- ### 📝 Commits (2) - [`b0b4292`](https://github.com/LGUG2Z/komorebi/commit/b0b4292ad81d9f5c8fd82cfd2397f5591875b884) fix(border): update border when moving from admin windows - [`38325f9`](https://github.com/LGUG2Z/komorebi/commit/38325f93c6fae4c8b5bc3a38ad5c5aebe9eb2a77) feat(border): cache borders on all workspaces ### 📊 Changes **11 files changed** (+250 additions, -205 deletions) <details> <summary>View changed files</summary> 📝 `komorebi-client/src/lib.rs` (+1 -0) 📝 `komorebi/src/border_manager/border.rs` (+21 -19) 📝 `komorebi/src/border_manager/mod.rs` (+178 -171) 📝 `komorebi/src/container.rs` (+11 -0) 📝 `komorebi/src/core/mod.rs` (+2 -0) 📝 `komorebi/src/stackbar_manager/stackbar.rs` (+2 -2) 📝 `komorebi/src/window.rs` (+21 -6) 📝 `komorebi/src/window_manager.rs` (+2 -2) 📝 `komorebi/src/windows_api.rs` (+1 -1) 📝 `komorebi/src/windows_callbacks.rs` (+8 -4) 📝 `komorebi/src/workspace.rs` (+3 -0) </details> ### 📄 Description ### 1st Commit: - Bug fix: focusing an admin window like the TaskManager, makes the focused container have the unfocused border (as it should since it no longer has focus), however if you focused that same container the border would still show as unfocused, since for komorebi nothing changed at all. This commit fixes that and makes the border update correctly. ### 2nd Commit: 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 drop 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. (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 we 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. <!-- Please follow the Conventional Commits specification. If you need to update your PR with changes from `master`, please run `git rebase master`. By opening this PR, you confirm that you have read and understood this project's `CONTRIBUTING.md`. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
adam added the pull-request label 2026-01-05 14:54:51 +01:00
adam closed this issue 2026-01-05 14:54:51 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/komorebi#1313