Skip to main content
Version: 2.1 (dev)

Focus History

When you close a window, which one gets focus next?

The Focus History List​

The focus history is maintained by awful.client.focus.history. Key facts:

  • Most recently focused client is at the front
  • When a client closes, the next one in history gets focus
  • History has unlimited depth: every client you've ever focused is tracked until it closes

How It Works​

Every time a client receives focus:

  1. It's removed from wherever it was in the history
  2. It's inserted at the front (position 1)
-- Conceptually (simplified from awful/client/focus.lua)
function focus.history.add(c)
focus.history.delete(c) -- Remove if present
table.insert(focus.history.list, 1, c) -- Insert at front
end

This means the history always reflects the order you focused clients, not their stack position or creation order.

Example Walkthrough​

You have 5 clients. You focus them in this order:

ActionFocus History (front → back)
Focus client 1[1]
Focus client 2[2, 1]
Focus client 5[5, 2, 1, ...]
Focus client 4[4, 5, 2, 1, ...]
Close client 4Focus → 5, history becomes [5, 2, 1, ...]
Close client 5Focus → 2, history becomes [2, 1, ...]

Notice that closing client 4 gives focus to client 5 (the previous focus), and closing 5 jumps back to 2, skipping clients 3 and 1 because you never focused them recently.

Focus History vs Client Stack​

These are completely independent systems:

AspectFocus HistoryClient Stack
What it tracksFocus order (timing)Z-order (visual overlap)
What uses itFocus fallback on closeRendering order
How it's orderedMost recent focus firstBack-to-front by raise/lower
Raising a windowNo effectMoves to top
Focusing a windowMoves to frontNo effect

You can have a window at the top of the z-order stack but deep in the focus history (if you raised it but haven't focused it recently). The two systems don't affect each other.

One Restoration Path​

The reason the history exists: something that had focus is always eventually going away. A client closes, a launcher like rofi dismisses (layer-shell surfaces are not clients, but they can hold keyboard focus), the session unlocks, a monitor disconnects. Rather than four focus-restoration mechanisms, the C core funnels every one of these through a single signal: request::focus_restore, emitted on the affected screen.

The default handler, awful.permissions.focus_restore, consults the focus history and activates the most recently focused client still visible on that screen. This is why closing a window "goes back" to where you were, and why dismissing a launcher does too: it is the same decision, made in the same place. And because the C core falls back to the topmost client when no handler focuses anything, a broken handler degrades to sensible behavior instead of stranding focus.

This unified signal is a somewm-specific design. AwesomeWM restores focus from several separate code paths, and has no layer shell at all (it is a Wayland protocol).

See Also​