Hi everyone,
I have been plagued by a specific Alt+Tab behavior for a long time: occasionally, when cycling between two windows (A and B), pressing Alt+Tab unexpectedly switches to a third window (C) instead of the previous one.
This issue breaks muscle memory and is incredibly disruptive to the user experience. I found an existing discussion about this here:
Alt-tab intermittently switches to the wrong window, but the root cause remained elusive.
I decided to dig into the KWin source code, apply some instrumentation, and debug the issue. I believe I have found the mechanism causing this (might not be the only one though). I’m posting this here instead of https://bugs.kde.org because I’m not sure if this is a bug.
The Root Cause
The issue arises from an interaction between Window Rules (specifically those matching titles) and the Focus Chain.
Prerequisites:
- You have at least 3 windows open (A, B, C).
- There is a Window Rule configured that matches Window B’s class/type/role (unimportant match is considered a match as well), AND this rule includes a non-trivial Title property match (e.g., Exact Match, Substring Match, etc.).
- Note: The rule doesn’t need to have certain settings; the key is that KWin is monitoring the title for rule evaluation.
Reproduction Steps:
- Focus Window A, then Focus Window B.
- Switch back to Window A (B is now the “previous” window).
- The Trigger: While Window B is in the background, its title changes.
- Example: Window B is a terminal. You run
sleep 2and switch to Window A. When the command finishes, the terminal updates its title.
- Example: Window B is a terminal. You run
- Press Alt+Tab.
- Result: KWin switches to Window C (the 3rd window) instead of Window B.
Code Analysis
Here is what happens internally when the background window (B) updates its title:
- Because there is a window rule involving title matching, KWin connects the title change signal to
Window::evaluateWindowRules(this is done insideRules::match). evaluateWindowRulescallsWindow::applyWindowRules.- Inside
applyWindowRules, it callssetOnActivities(activities()). - This triggers
updateActivities(false). - Finally, this calls
Workspace::self()->focusChain()->update(this, FocusChain::MakeFirst).
The Consequence:
Even though Window B is in the background, the rule re-evaluation silently promotes it to the “First” position in the Focus Chain.
- Original Chain: A (Active) → B (Last Used) → C
- After Title Update: B (Moved to First) → A → C
- Result: Since you are currently on A, the “next” window in the MRU (Most Recently Used) list is now effectively C, because B has been artificially injected before A.
Conclusion & Next Steps
This explains why the bug feels random—it often depends on background processes (like a compiler finishing in a terminal or a browser tab updating) changing a window title while a specific window rule exists.
I am not sure if updateActivities implies a necessity to update the focus chain to MakeFirst during a rule re-evaluation, or if this is an unintended side effect.
I am very willing to contribute a fix for this. However, I would appreciate some guidance from the KWin maintainers: what is the preferred way to handle this? Should we prevent the focus chain update when rules are just being re-evaluated for title changes?
Thanks!