Skip to main content

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.

Accessing the Focus History

-- Direct access to the history list
local history = awful.client.focus.history.list

-- Get the previously focused client
local prev = awful.client.focus.history.get(screen, 1, nil)

-- Get previous client on a specific screen
local prev_on_screen = awful.client.focus.history.get(s, 0, function(c)
return c.screen == s
end)

-- Filter by arbitrary criteria
local prev_floating = awful.client.focus.history.get(nil, 0, function(c)
return c.floating
end)

Parameters for focus.history.get()

ParameterDescription
screenFilter to this screen (nil for any)
idxHow many positions back to look (0 = most recent, 1 = second most recent)
filterOptional function (client) -> boolean for custom filtering

Common Use Cases

Alt-Tab Style Switching

Switch between your two most recently focused windows:

awful.key({ "Mod1" }, "Tab", function()
awful.client.focus.history.previous()
end)

Or cycle through the full history:

awful.key({ modkey }, "Tab", function()
awful.client.focus.byidx(1)
end)

Focus Follows Close

This is the default behavior. When a client closes, awful.client.focus.history.previous() is called automatically to focus the next client in history. No configuration needed.

Screen-Aware Focus Fallback

Focus the previous client on the same screen:

client.connect_signal("unmanage", function(c)
if c == client.focus then
local fallback = awful.client.focus.history.get(c.screen, 0, function(cl)
return cl ~= c and cl.screen == c.screen
end)
if fallback then
fallback:emit_signal("request::activate", "history", {raise = true})
end
end
end)

Custom Focus Fallback Logic

Override the default behavior entirely:

-- Replace default focus fallback with your own logic
client.connect_signal("request::unmanage", function(c)
if c == client.focus then
-- Your custom logic here
local next_client = find_my_preferred_client()
if next_client then
next_client:emit_signal("request::activate", "custom", {raise = true})
end
end
end)

Disable Focus History

If you want focus to follow a different pattern (e.g., always focus the master window):

awful.client.focus.history.disable_tracking()

Or selectively exclude clients:

awful.client.focus.filter = function(c)
-- Return false to exclude from focus history
if c.class == "some-app" then
return false
end
return true
end

Debugging Focus Issues

If focus isn't going where you expect when you close a window:

-- Print the current focus history
for i, c in ipairs(awful.client.focus.history.list) do
print(i, c.name, c.class)
end

This shows the complete history in order, so you can see why a particular client got focus.

See Also