Skip to content

fix: suppress cross-PID spurious focus bounce on tag switch#165

Merged
typester merged 4 commits intotypester:mainfrom
ka2u:fix/cross-pid-spurious-focus-bounce
Apr 13, 2026
Merged

fix: suppress cross-PID spurious focus bounce on tag switch#165
typester merged 4 commits intotypester:mainfrom
ka2u:fix/cross-pid-spurious-focus-bounce

Conversation

@ka2u
Copy link
Copy Markdown
Contributor

@ka2u ka2u commented Apr 10, 2026

When switching to a tag containing certain Chromium-based apps (e.g., Dia
browser), macOS sends a spurious FocusedWindowChanged event ~90ms later
pointing back to the previously focused (now hidden) window of a different
app. The auto-tag-switch logic interpreted this as a user-initiated external
focus change and reverted the tag switch.

The existing FocusIntent suppression only handled same-PID focus redirects.
This adds cross-PID suppression: when FocusIntent is active and focus
bounces to a hidden window of a different app, the event is suppressed
without calling refocus_window (which would create an infinite bounce loop).


fixes #164

  When switching to a tag containing certain Chromium-based apps (e.g., Dia
  browser), macOS sends a spurious FocusedWindowChanged event ~90ms later
  pointing back to the previously focused (now hidden) window of a different
  app. The auto-tag-switch logic interpreted this as a user-initiated external
  focus change and reverted the tag switch.

  The existing FocusIntent suppression only handled same-PID focus redirects.
  This adds cross-PID suppression: when FocusIntent is active and focus
  bounces to a hidden window of a different app, the event is suppressed
  without calling refocus_window (which would create an infinite bounce loop).
@typester
Copy link
Copy Markdown
Owner

Hi ka2u-san, long time no see! Hope you're doing well.

Thanks for the PR! The approach looks good to me. There's an ongoing long-running fix task in #161 that touches the same area, so I'd like to take over and incorporate this change on my end.

One question: is the 200ms suppression window sufficient here? I'm wondering if it might not be enough when the machine is under heavy load. In #161, I've been increasing the cross-display suppression window up to 1000ms and observing the results before merging (which is why that branch hasn't been merged yet).

@ka2u
Copy link
Copy Markdown
Contributor Author

ka2u commented Apr 12, 2026

Hi typester-san, Thanks, I'm doing great! Hope you're well.
Thank you for creating the yashiki. I'm using it daily.

The approach of extending the suppression window makes sense. The 200ms was inherited from the existing FocusIntent::SUPPRESSION_DURATION_MS — in the debug logs the bounce happened at ~90ms, so 200ms covered
it. But under heavy load it could definitely exceed that. The logs from testing showed the bounce loop ran for the full 200ms before expiring, so there's no margin.

Using a longer window (like the 500ms or 1000ms being explored in #161) seems safer for cross-PID suppression since the only downside is a slightly longer period where a real user-initiated focus change to a
hidden window would be suppressed — which is an unlikely user action anyway.

Happy to have this incorporated into #161. Thank you for your consideration.

@typester typester force-pushed the fix/cross-pid-spurious-focus-bounce branch from ae29b06 to cded12b Compare April 13, 2026 13:02
@typester
Copy link
Copy Markdown
Owner

@ka2u I made some minor refactoring, but I'll keep the SUPPRESSION_DURATION at 200ms for now. I'm going to merge this as-is. Please let me know if you run into any issues!

@typester typester merged commit 42aaf5d into typester:main Apr 13, 2026
2 checks passed
@github-actions github-actions bot mentioned this pull request Apr 13, 2026
typester pushed a commit that referenced this pull request Apr 13, 2026
## 🤖 New release

* `yashiki-ipc`: 0.12.0 -> 0.12.1
* `yashiki`: 0.12.0 -> 0.12.1
* `yashiki-layout-tatami`: 0.12.0 -> 0.12.1
* `yashiki-layout-byobu`: 0.12.0 -> 0.12.1

<details><summary><i><b>Changelog</b></i></summary><p>

## `yashiki-ipc`

<blockquote>

##
[0.12.0](yashiki-ipc-v0.11.6...yashiki-ipc-v0.12.0)
- 2026-02-25

### Added

- [**breaking**] add keybinding mode system (river-style)
([#157](#157))
</blockquote>

## `yashiki`

<blockquote>

##
[0.12.1](yashiki-v0.12.0...yashiki-v0.12.1)
- 2026-04-13

### Fixed

- suppress cross-PID spurious focus bounce on tag switch
([#165](#165))
- suppress unintended tag switch from macOS auto-activation after app
termination ([#161](#161))
</blockquote>

## `yashiki-layout-tatami`

<blockquote>

##
[0.7.3](yashiki-layout-tatami-v0.7.2...yashiki-layout-tatami-v0.7.3)
- 2026-01-20

### Other

- update Cargo.lock dependencies
</blockquote>

## `yashiki-layout-byobu`

<blockquote>

##
[0.7.3](yashiki-layout-byobu-v0.7.2...yashiki-layout-byobu-v0.7.3)
- 2026-01-20

### Other

- update Cargo.lock dependencies
</blockquote>


</p></details>

---
This PR was generated with
[release-plz](https://github.com/release-plz/release-plz/).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tag switch immediately reverts when switching to Dia browser (cross-PID spurious focus bounce)

2 participants