#1749·komorebi

[BUG]: Unfocused-window transparency permanently orphans windows after minimize/restore (WS_EX_LAYERED left set; komorebi restart does not recover them)

Author: MatthewMWRCreated Sep 16, 2026Updated Sep 16, 2026
Labelsbug

Summary

With "transparency": true, any tiled window that is minimized while unfocused (Win+D, Show Desktop, the taskbar, a stray click on the minimize button) is never managed again after it is restored. It comes back floating on top of the layout, komorebic visible-windows no longer lists it, komorebic retile does nothing, and restarting komorebi does not recover it. The only ways back are to close and reopen the application, or to manually clear WS_EX_LAYERED from the window.

This is not an exotic app or a custom rule. I hit it with Notepad and Microsoft Edge within an hour of enabling transparency, on a stock Windows 11 install with the quickstart configuration plus Columns layouts. Minimizing and restoring windows is an everyday action, so in practice transparency cannot be left on.

Steps to reproduce

  1. "transparency": true in komorebi.json (any alpha). No other transparency-related settings.
  2. Open three ordinary windows, e.g. Windows Terminal, Notepad, Edge. All three tile.
  3. Focus Terminal so Notepad and Edge are faded.
  4. Press Win+D (or minimize Notepad and Edge from the taskbar), then restore them.

Expected: Notepad and Edge rejoin the layout, as they do when transparency is off.

Actual: Terminal expands to fill the workspace; Notepad and Edge float above it unmanaged. komorebic visible-windows lists only Terminal. Nothing komorebi offers brings them back.

Root cause (from reading v0.1.41 source)

The transparency feature fades a window by adding WS_EX_LAYERED to its extended style and calling SetLayeredWindowAttributes (window.rs, Window::transparent). The style is only removed by Window::opaque, which the transparency manager calls only for windows it still knows about.

Meanwhile window_is_eligible (window.rs) rejects any window carrying WS_EX_LAYERED unless it is in the layered whitelist or in transparency_manager::known_hwnds(). That known list is cleared and rebuilt on every notification from the windows currently inside tiled containers (transparency_manager.rs, handle_notifications).

So the sequence is:

  1. Window is faded -> WS_EX_LAYERED set, hwnd in known list.
  2. Window is minimized -> removed from its container (correct).
  3. Next notification rebuilds the known list from containers -> hwnd is no longer known. opaque() is never called on it, so WS_EX_LAYERED stays set on the window.
  4. Window is restored -> Show/FocusChange event -> window_is_eligible sees LAYERED, not whitelisted, not known -> rejected. Forever, because nothing will ever make it opaque again.

A komorebi restart cannot fix it: the new instance starts with an empty known list and the window still carries the style. Turning transparency off after the fact also does not fix already-orphaned windows for the same reason.

Evidence

Extended styles of the two orphaned windows, read with GetWindowLong(GWL_EXSTYLE) while they were floating:

Notepad  exstyle=0x00080110  (LAYERED | WINDOWEDGE | ACCEPTFILES)
msedge   exstyle=0x00280100  (LAYERED | NOREDIRECTIONBITMAP | WINDOWEDGE)
Terminal exstyle=0x00200100  (NOREDIRECTIONBITMAP | WINDOWEDGE)   <- stayed focused, stayed tiled

Neither Notepad nor Edge sets WS_EX_LAYERED on its own main window.

Log excerpt: both windows receive SystemMinimizeStart in the same 20 ms window, and then no further events for either hwnd appear in the log, consistent with them being filtered out before the event handler:

18:57:33.705 INFO process_event{event="Minimize" winevent="SystemMinimizeStart" hwnd=197318} ...
18:57:33.721 INFO process_event{event="Minimize" winevent="SystemMinimizeStart" hwnd=590708} ...

Workarounds tested on the orphaned windows:

Action Result
komorebic retile no effect
komorebic stop + komorebic start no effect, both windows still LAYERED and still not listed
Clear WS_EX_LAYERED via SetWindowLong, then minimize + restore the window both windows re-adopted immediately
Close and reopen the app works (fresh window has no LAYERED)

Related reports

  • #1595 (open, labelled apps-behaving-badly): the debug output shows LAYERED in the extended style and should_manage: false; turning transparency off removed LAYERED and the window tiled. I believe this is the same defect.
  • #1490 (closed): transparency + stack-all losing windows. Same mechanism, different way of leaving the container.

Suggested fix

Either of these would close the hole:

  1. When a window leaves management for any reason (minimize, hide, unmanage, workspace move failure, stop), call opaque() on it if it is in the known list before the list is rebuilt, so the style never outlives management.
  2. Keep known_hwnds as a persistent set (add on transparent(), remove on opaque()) instead of rebuilding it from live containers every notification, so a window komorebi made layered stays eligible until komorebi itself un-layers it.

Additionally, on startup komorebi could treat LAYERED windows whose GetLayeredWindowAttributes alpha equals the configured transparency_alpha as its own leftovers and make them opaque, which would also recover users after a crash.

Version Information

OS Name:                       Microsoft Windows 11 Home
OS Version:                    10.0.26200 N/A Build 26200

komorebic 0.1.41
tag:v0.1.41
commit_hash:24c0ce0b
build_time:2026-05-03 20:22:09 +00:00
build_env:rustc 1.95.0 (59807616e 2026-04-14),stable-x86_64-pc-windows-msvc

Display: single laptop panel, 1920x1200 at 125% scaling. ARM64 device running the native aarch64 build from the release MSI (build_env above shows the CI host toolchain, not the target), though nothing here is architecture-specific.

Komorebi Configuration

json
{
  "$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.41/schema.json",
  "app_specific_configuration_path": "$Env:USERPROFILE/applications.json",
  "window_hiding_behaviour": "Cloak",
  "cross_monitor_move_behaviour": "Insert",
  "default_workspace_padding": 0,
  "default_container_padding": 0,
  "border": false,
  "border_width": 1,
  "border_offset": 0,
  "theme": {
    "palette": "Base16",
    "name": "Ashes",
    "unfocused_border": "Base03",
    "bar_accent": "Base0D"
  },
  "stackbar": {
    "mode": "Never",
    "height": 22,
    "label": "Process",
    "tabs": {
      "width": 130,
      "font_size": 24,
      "background": "#1c1c1c",
      "focused_text": "#e0e0e0",
      "unfocused_text": "#8a8a8a"
    }
  },
  "display_index_preferences": {
    "0": "0"
  },
  "monitors": [
    {
      "workspaces": [
        {
          "name": "1",
          "layout": "Columns"
        },
        {
          "name": "2",
          "layout": "Columns"
        },
        {
          "name": "3",
          "layout": "Columns"
        }
      ]
    },
    {
      "workspaces": [
        {
          "name": "1",
          "layout": "Columns"
        },
        {
          "name": "2",
          "layout": "Columns"
        },
        {
          "name": "3",
          "layout": "Columns"
        }
      ]
    },
    {
      "workspaces": [
        {
          "name": "1",
          "layout": "Columns"
        },
        {
          "name": "2",
          "layout": "Columns"
        },
        {
          "name": "3",
          "layout": "Columns"
        }
      ]
    }
  ],
  "border_implementation": "Komorebi",
  "transparency": true,
  "transparency_alpha": 200,
  "mouse_follows_focus": true
}

Hotkey Configuration

The komorebi.ahk sample from the docs (docs/komorebi.ahk.txt), launched via AutoHotkey v2, with the workspace bindings trimmed to 1-3 and two extra bindings for cycle-monitor / cycle-move-to-monitor. Hotkeys are not involved in the reproduction; the bug triggers from Win+D or the taskbar minimize button alone.

Output of komorebic check

No KOMOREBI_CONFIG_HOME detected, defaulting to C:\Users\<user>

Looking for configuration files in C:\Users\<user>

Found komorebi.json; this file can be passed to the start command with the --config flag

Found C:\Users\<user>\.config\whkdrc; key bindings will be loaded from here when whkd is started, and you can start it automatically using the --whkd flag