Skip to main content
Version: 2.0 (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