Skip to content

fix(xwm): Defer X11 focus release between same-client transitions#2029

Merged
Drakulix merged 2 commits into
Smithay:masterfrom
mayakwd:fix/x11-same-client-focus-transitions
May 13, 2026
Merged

fix(xwm): Defer X11 focus release between same-client transitions#2029
Drakulix merged 2 commits into
Smithay:masterfrom
mayakwd:fix/x11-same-client-focus-transitions

Conversation

@mayakwd
Copy link
Copy Markdown
Contributor

@mayakwd mayakwd commented May 9, 2026

Description

X11Wm set _NET_ACTIVE_WINDOW = NONE for any FocusOut event on a tracked top-level window, ignoring detail field. It breaks X11 clients like Unity Editor.

Mechanism of the flaw:

  • the client gets focus at the top level and...
  • almost instantly switches focus to its sub-window.
  • X-server generates FocusOut(detail=inferior) for the top-level window.
  • code without fix just set _NET_ACTIVE_WINDOW to none, and...
  • there is no one to receive FocusIn.

But that's not the whole issue. X11Surface also synchronously calls set_input_focus(NONE, NONE), producing the intermediate state "focus is nowhere".

Fixes:

Works not only for cosmic-comp but also for other compositors based on Smithay, all of which suffer the same issue.

Symptoms

Symptoms are exactly as described in pop-os/cosmic-comp#2006:

  • Open a project in Unity
  • It's smooth and nice
  • Press Play, and experience a framerate dropped to 10 frames per second (Unity turns on throttling when losing focus, to not waste resources).
  • More than that, the keyboard and mouse are unresponsive inside Game view
  • Alt+tab and back - helps to return focus, but framerate is still down.

And many other related to input-unresponsiveness and sluggish experience, which cannot always be resolved by clicking other panels/alt-tabbing.

Diagnostics

Instrumentation wired into Smithay showed that:

  02:06:02.408  X11Surface::enter                          WINDOW=29360433 MODEL=LocallyActive
  02:06:02.412  xwm Event::FocusIn   WINDOW=29360433 detail=NONLINEAR tracked=true
  02:06:02.412  xwm: _NET_ACTIVE_WINDOW = window           WINDOW=29360433 
  02:06:02.462  xwm Event::FocusOut  WINDOW=29360433 detail=INFERIOR tracked=true
  02:06:02.462  xwm: _NET_ACTIVE_WINDOW = NONE             WINDOW=29360433   <-- issue

Meaning that Unity switched focus from the top level to a sub-window, but _NET_ACTIVE_WINDOW became none.

How the fix works

  • FocusOut now doesn't touch _NET_ACTIVE_WINDOW
  • Losing focus now deferred to the next event loop, leaving a space to cancel focus leaving for X11Wm.

What I've checked

  • Checked locally with cosmic-comp (no code was changed in cosmic-comp, only path to local version of smithay)
  • Unity Editor is no longer throttling
  • Switching between X11<->Wayland apps works as expected
  • No regression issues found

Checklist

The synchronous `set_input_focus(NONE, NONE)` in `X11Surface::leave`
breaks focus transitions between same-client windows. Defer the
focus-out decision to the next event-loop iteration so it can be
cancelled out.
@Drakulix
Copy link
Copy Markdown
Member

Okay this seems pretty okay as a fix, but I am a bit hesitant on merging the deferred __NET_ACTIVE_WINDOW change. Can we not start reading the detail instead and only unset it on NotifyDetailNone or something?

@mayakwd
Copy link
Copy Markdown
Contributor Author

mayakwd commented May 12, 2026

Okay this seems pretty okay as a fix, but I am a bit hesitant on merging the deferred __NET_ACTIVE_WINDOW change. Can we not start reading the detail instead and only unset it on NotifyDetailNone or something?

Well, you are probably right, maybe I jumped the gun a bit.
I'll try to bring unset of __NET_ACTIVE_WINDOW back, and don't smear the logic via deferring, but take a decision in place.

@mayakwd
Copy link
Copy Markdown
Contributor Author

mayakwd commented May 12, 2026

@Drakulix,
I moved back the unset logic and just added a simple guard. It actually works the same way as deferred (in terms of behavior), but now it's not deferred :-)

Yet we still need to preserve the deferred path for set_input_focus to avoid the intermediate (NONE, NONE) state in case a transition occurs within the same client X11->X11.

Copy link
Copy Markdown
Member

@Drakulix Drakulix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still feels a little hacky. At some point we probably should make focus-switches somewhat atomic, so that we can simply infer, if any new target is also an X11Surface or not.

But in the meantime this is a good solution. Thanks!

@Drakulix Drakulix merged commit c0ac123 into Smithay:master May 13, 2026
14 checks passed
@SotoFidel
Copy link
Copy Markdown

SotoFidel commented May 29, 2026

Just tested on Unity 6.4. Looking good 👍🏽
Closing issue 2006 on cosmic-comp.
You all have my thanks!

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.

3 participants