Focusing workspaces quickly moves windows between workspaces #91

Closed
opened 2026-01-05 14:48:15 +01:00 by adam · 8 comments
Owner

Originally created by @tgharib on GitHub (Jun 3, 2022).

I have Windows+Shift+1 bound to focus workspace 1 and Windows+Shift+2 to focus workspace 2. If I hold, Windows+Shift and spam 1 and 2 as quickly as possible, windows in workspace 2 will move to workspace 1 or vice-versa. While this procedure is extreme to reproduce the bug, this bug has happened to me during legitimate usage too.

I had the same issue when I was using Alt+1 and Alt+2 for my workspace keybinds so the bug is not specific to the key binds.

Originally created by @tgharib on GitHub (Jun 3, 2022). I have `Windows+Shift+1` bound to focus workspace 1 and `Windows+Shift+2` to focus workspace 2. If I hold, `Windows+Shift` and spam 1 and 2 as quickly as possible, windows in workspace 2 will move to workspace 1 or vice-versa. While this procedure is extreme to reproduce the bug, this bug has happened to me during legitimate usage too. I had the same issue when I was using `Alt+1` and `Alt+2` for my workspace keybinds so the bug is not specific to the key binds.
adam closed this issue 2026-01-05 14:48:15 +01:00
Author
Owner

@LGUG2Z commented on GitHub (Jun 3, 2022):

Are you running komorebi with window-hiding-behaviour set to hide or minimize? I know that hide can occasionally result in the sort of behaviour that you are describing but I haven't experienced this with minimize (yet).

hide is still the default as I'm reluctant to make a major version bump just for this one change, but I think that in the future this entire option should just be removed and the behaviour should default to minimize.

@LGUG2Z commented on GitHub (Jun 3, 2022): Are you running `komorebi` with `window-hiding-behaviour` set to `hide` or `minimize`? I know that `hide` can occasionally result in the sort of behaviour that you are describing but I haven't experienced this with `minimize` (yet). `hide` is still the default as I'm reluctant to make a major version bump just for this one change, but I think that in the future this entire option should just be removed and the behaviour should default to `minimize`.
Author
Owner

@tgharib commented on GitHub (Jun 3, 2022):

window-hiding-behaviour is set to minimize. I am using v0.1.9 which appears to default to minimize. When I switch workspaces, the windows for other workspaces are minimized - not hidden.

I went ahead an attached a video reproducing this behavior. I have a notepad in workspace 4 and another notepad in workspace 5 and I am simply focusing workspaces 4 and 5 rapidly. I know the bug appears extreme but I encounter this bug when for example, I focus workspace 2 and then immediately change my mind, to focus workspace 3.

I believe this bug occurs because of this timeline:

  1. I focus workspace 5 from workspace 4. Notepad 5 is shown and notepad 4 is in the middle of being minimized.
  2. I focus back to workspace 4 before notepad 4 is minimized and thus interrupted. Now komorebi believes that both notepad 4 and notepad 5 belong to workspace 5. After focusing workspace 4, all windows are minimized.
  3. I focus workspace 5 and komobrei shows both notepad 4 and 5.

So just guessing (I didn't look at any code), I think this bug is a consequence of the slowness from the win32 windows api. I imagine fixing this would involve a painful architecture change, to use an internal tree of windows rather relying on the win32 state of minimized/shown windows.

@tgharib commented on GitHub (Jun 3, 2022): `window-hiding-behaviour` is set to minimize. I am using `v0.1.9` which appears to default to minimize. When I switch workspaces, the windows for other workspaces are minimized - not hidden. I went ahead an attached a [video](https://github.com/LGUG2Z/komorebi/files/8835102/2022-06-03.15-01-27.zip) reproducing this behavior. I have a notepad in workspace 4 and another notepad in workspace 5 and I am simply focusing workspaces 4 and 5 rapidly. I know the bug appears extreme but I encounter this bug when for example, I focus workspace 2 and then immediately change my mind, to focus workspace 3. I believe this bug occurs because of this timeline: 1. I focus workspace 5 from workspace 4. Notepad 5 is shown and notepad 4 is in the middle of being minimized. 2. I focus back to workspace 4 before notepad 4 is minimized and thus interrupted. Now komorebi believes that both notepad 4 and notepad 5 belong to workspace 5. After focusing workspace 4, all windows are minimized. 3. I focus workspace 5 and komobrei shows both notepad 4 and 5. So just guessing (I didn't look at any code), I think this bug is a consequence of the slowness from the win32 windows api. I imagine fixing this would involve a painful architecture change, to use an internal tree of windows rather relying on the win32 state of minimized/shown windows.
Author
Owner

@LGUG2Z commented on GitHub (Jun 3, 2022):

Looks like I've been working on this so long that I no longer know what I'm talking about. 😅

I think that this is indeed most likely due to a timing issue like you have mentioned between the application's internal state and the events being emitted by the Windows API.

It should be possible to wrap calls that update the currently focused workspace with a debouncer which can be personalized by the user with a CLI command. I will look into this when I have some free time over the next few weeks.

@LGUG2Z commented on GitHub (Jun 3, 2022): Looks like I've been working on this so long that I no longer know what I'm talking about. 😅 I think that this is indeed most likely due to a timing issue like you have mentioned between the application's internal state and the events being emitted by the Windows API. It should be possible to wrap calls that update the currently focused workspace with a debouncer which can be personalized by the user with a CLI command. I will look into this when I have some free time over the next few weeks.
Author
Owner

@LGUG2Z commented on GitHub (Jun 7, 2022):

Dumping some logs here from the moment when I was able to trigger this on my machine:

2022-06-06T22:00:06.674828Z  INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.675438Z  INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}: komorebi::window_manager: focusing workspace
2022-06-06T22:00:06.675873Z  INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}:focus_workspace{idx=0}: komorebi::monitor: focusing workspace
2022-06-06T22:00:06.689844Z  INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating
2022-06-06T22:00:06.691534Z  INFO read_commands:process_command{FocusWorkspaceNumber(0)}: komorebi::process_command: processed
2022-06-06T22:00:06.711443Z  INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.712118Z  INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_window{idx=0}: komorebi::container: focusing window
2022-06-06T22:00:06.712507Z  INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_container{idx=0}: komorebi::workspace: focusing container
2022-06-06T22:00:06.713615Z  INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame)
2022-06-06T22:00:06.720491Z  INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.720911Z  INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}: komorebi::window_manager: focusing workspace
2022-06-06T22:00:06.721240Z  INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}:focus_workspace{idx=1}: komorebi::monitor: focusing workspace
2022-06-06T22:00:06.750470Z  INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating
2022-06-06T22:00:06.755325Z  INFO read_commands:process_command{FocusWorkspaceNumber(1)}: komorebi::process_command: processed
2022-06-06T22:00:06.756630Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.757139Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.757501Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}: komorebi::window_manager: focusing workspace
2022-06-06T22:00:06.757863Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}:focus_workspace{idx=0}: komorebi::monitor: focusing workspace
2022-06-06T22:00:06.775542Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating
2022-06-06T22:00:06.777997Z  INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 395786 })}: komorebi::process_event: processed: (hwnd: 395786, title: PowerShell, exe: WindowsTerminal.exe, class: CASCADIA_HOSTING_WINDOW_CLASS)
2022-06-06T22:00:06.778830Z  INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 722816 })}:update_focused_workspace{follow_focus=false}: komorebi::window_manager: updating
2022-06-06T22:00:06.780247Z  INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame)
2022-06-06T22:00:06.780978Z  INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.783281Z ERROR komorebi::process_event: there is no container/window
2022-06-06T22:00:06.789206Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.789838Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.790275Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}: komorebi::window_manager: focusing workspace
2022-06-06T22:00:06.790603Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}:focus_workspace{idx=1}: komorebi::monitor: focusing workspace
2022-06-06T22:00:06.811352Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating
2022-06-06T22:00:06.815243Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor
2022-06-06T22:00:06.815791Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_window{idx=0}: komorebi::container: focusing window
2022-06-06T22:00:06.816159Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_container{idx=1}: komorebi::workspace: focusing container
2022-06-06T22:00:06.816507Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:update_focused_workspace{follow_focus=false}: komorebi::window_manager: updating
2022-06-06T22:00:06.826723Z  INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame)
@LGUG2Z commented on GitHub (Jun 7, 2022): Dumping some logs here from the moment when I was able to trigger this on my machine: ``` 2022-06-06T22:00:06.674828Z INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.675438Z INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}: komorebi::window_manager: focusing workspace 2022-06-06T22:00:06.675873Z INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}:focus_workspace{idx=0}: komorebi::monitor: focusing workspace 2022-06-06T22:00:06.689844Z INFO read_commands:process_command{FocusWorkspaceNumber(0)}:focus_workspace{idx=0}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating 2022-06-06T22:00:06.691534Z INFO read_commands:process_command{FocusWorkspaceNumber(0)}: komorebi::process_command: processed 2022-06-06T22:00:06.711443Z INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.712118Z INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_window{idx=0}: komorebi::container: focusing window 2022-06-06T22:00:06.712507Z INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}:focus_container{idx=0}: komorebi::workspace: focusing container 2022-06-06T22:00:06.713615Z INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame) 2022-06-06T22:00:06.720491Z INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.720911Z INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}: komorebi::window_manager: focusing workspace 2022-06-06T22:00:06.721240Z INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}:focus_workspace{idx=1}: komorebi::monitor: focusing workspace 2022-06-06T22:00:06.750470Z INFO read_commands:process_command{FocusWorkspaceNumber(1)}:focus_workspace{idx=1}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating 2022-06-06T22:00:06.755325Z INFO read_commands:process_command{FocusWorkspaceNumber(1)}: komorebi::process_command: processed 2022-06-06T22:00:06.756630Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.757139Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.757501Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}: komorebi::window_manager: focusing workspace 2022-06-06T22:00:06.757863Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}:focus_workspace{idx=0}: komorebi::monitor: focusing workspace 2022-06-06T22:00:06.775542Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_workspace{idx=0}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating 2022-06-06T22:00:06.777997Z INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 395786 })}: komorebi::process_event: processed: (hwnd: 395786, title: PowerShell, exe: WindowsTerminal.exe, class: CASCADIA_HOSTING_WINDOW_CLASS) 2022-06-06T22:00:06.778830Z INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 722816 })}:update_focused_workspace{follow_focus=false}: komorebi::window_manager: updating 2022-06-06T22:00:06.780247Z INFO process_event{event=Minimize(SystemMinimizeStart, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame) 2022-06-06T22:00:06.780978Z INFO process_event{event=FocusChange(SystemForeground, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.783281Z ERROR komorebi::process_event: there is no container/window 2022-06-06T22:00:06.789206Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.789838Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.790275Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}: komorebi::window_manager: focusing workspace 2022-06-06T22:00:06.790603Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}:focus_workspace{idx=1}: komorebi::monitor: focusing workspace 2022-06-06T22:00:06.811352Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 395786 })}:focus_workspace{idx=1}:update_focused_workspace{follow_focus=true}: komorebi::window_manager: updating 2022-06-06T22:00:06.815243Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_monitor{idx=0}: komorebi::window_manager: focusing monitor 2022-06-06T22:00:06.815791Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_window{idx=0}: komorebi::container: focusing window 2022-06-06T22:00:06.816159Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:focus_container{idx=1}: komorebi::workspace: focusing container 2022-06-06T22:00:06.816507Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}:update_focused_workspace{follow_focus=false}: komorebi::window_manager: updating 2022-06-06T22:00:06.826723Z INFO process_event{event=Show(SystemMinimizeEnd, Window { hwnd: 722816 })}: komorebi::process_event: processed: (hwnd: 722816, title: komorebi – process_event.rs, exe: idea64.exe, class: SunAwtFrame) ```
Author
Owner

@LGUG2Z commented on GitHub (Jun 7, 2022):

It looks like SystemMinimizeEnd (associated with the received command FocusWorkspaceNumber(0)) fires after FocusWorkspaceNumber(1) has been received, which results in this window restoration happening on workspace 1 instead of workspace 0.

I tried blocking the hide and restore calls until it's possible to confirm via another API call that the window has indeed been minimized or restored, but I am still able to reproduce this behaviour:

    pub fn hide(self) {
        let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
        if !programmatically_hidden_hwnds.contains(&self.hwnd) {
            programmatically_hidden_hwnds.push(self.hwnd);
        }

        let hiding_behaviour = HIDING_BEHAVIOUR.lock();
        match *hiding_behaviour {
            HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd()),
            HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd()),
        }

        while !WindowsApi::is_iconic(self.hwnd()) {}
    }

    pub fn restore(self) {
        let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock();
        if let Some(idx) = programmatically_hidden_hwnds
            .iter()
            .position(|&hwnd| hwnd == self.hwnd)
        {
            programmatically_hidden_hwnds.remove(idx);
        }

        WindowsApi::restore_window(self.hwnd());
        while !WindowsApi::is_window_visible(self.hwnd()) {}
    }
@LGUG2Z commented on GitHub (Jun 7, 2022): It looks like `SystemMinimizeEnd` (associated with the received command `FocusWorkspaceNumber(0)`) fires after `FocusWorkspaceNumber(1)` has been received, which results in this window restoration happening on workspace 1 instead of workspace 0. I tried blocking the `hide` and `restore` calls until it's possible to confirm via another API call that the window has indeed been minimized or restored, but I am still able to reproduce this behaviour: ```rust pub fn hide(self) { let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if !programmatically_hidden_hwnds.contains(&self.hwnd) { programmatically_hidden_hwnds.push(self.hwnd); } let hiding_behaviour = HIDING_BEHAVIOUR.lock(); match *hiding_behaviour { HidingBehaviour::Hide => WindowsApi::hide_window(self.hwnd()), HidingBehaviour::Minimize => WindowsApi::minimize_window(self.hwnd()), } while !WindowsApi::is_iconic(self.hwnd()) {} } pub fn restore(self) { let mut programmatically_hidden_hwnds = HIDDEN_HWNDS.lock(); if let Some(idx) = programmatically_hidden_hwnds .iter() .position(|&hwnd| hwnd == self.hwnd) { programmatically_hidden_hwnds.remove(idx); } WindowsApi::restore_window(self.hwnd()); while !WindowsApi::is_window_visible(self.hwnd()) {} } ```
Author
Owner

@LGUG2Z commented on GitHub (Feb 12, 2023):

@tgharib Check out this commit, just set window-hiding-behaviour to cloak: 80c98596dd

@LGUG2Z commented on GitHub (Feb 12, 2023): @tgharib Check out this commit, just set `window-hiding-behaviour` to `cloak`: https://github.com/LGUG2Z/komorebi/commit/80c98596dd2cb4e28666e49c753ffc7e5137e09e
Author
Owner

@tgharib commented on GitHub (Feb 13, 2023):

Sweet, I cannot reproduce this bug when window-hiding-behaviour is cloak. It is still reproducible with minimize though.

@tgharib commented on GitHub (Feb 13, 2023): Sweet, I cannot reproduce this bug when `window-hiding-behaviour` is `cloak`. It is still reproducible with `minimize` though.
Author
Owner

@LGUG2Z commented on GitHub (Feb 13, 2023):

Ultimately I think I will keep the hide and minimize options around to avoid breaking peoples' configurations, but for future releases I will set the default to cloak and encourage its use as this is the same (hidden) mechanism used by Microsoft when switching between virtual desktops and naturally it avoids entire classes of bugs like this one.

@LGUG2Z commented on GitHub (Feb 13, 2023): Ultimately I think I will keep the `hide` and `minimize` options around to avoid breaking peoples' configurations, but for future releases I will set the default to `cloak` and encourage its use as this is the same (hidden) mechanism used by Microsoft when switching between virtual desktops and naturally it avoids entire classes of bugs like this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/komorebi#91