Skip to content

Instantly share code, notes, and snippets.

@gitaarik
Last active March 24, 2024 09:22
Show Gist options
  • Save gitaarik/c28511f0a7a94a2b23d6b64a306584c9 to your computer and use it in GitHub Desktop.
Save gitaarik/c28511f0a7a94a2b23d6b64a306584c9 to your computer and use it in GitHub Desktop.
Awesome WM script that restores floating clients geometry (width / height / position) when switching layouts or unmaximizing
-- Restore last used floating client geometry (with / height / position) when
-- switching layouts and unmaximizing clients.
--
-- When unmaximizing a never-before floating client, it will nicely center the
-- client in the middle of the screen.
--
-- To use, just `require('restore_floating_clients')` in your `awesome/lua.rc`
-- Somewhere where you have your other signal callbacks.
local awful = require("awful")
-- The following variables contain different states of clients, with the
-- `client.window` in the key and the relevant state in the value.
-- Store the floating clients geometries (with / height / position), so we can
-- restore the clients when the layout changes.
local floating_client_geometries = {}
-- Store the previous floating clients geometries. We need this to restore from
-- maximized, because the maximized signal happens after the geometry signal.
-- So the state we want to revert to gets overridden before we can restore it.
-- Therefore we also store the previous floating client geometries.
local prev_floating_client_geometries = {}
-- Store whether a client was NOT maximized in the last used floating layout.
-- We use this to determin whether a client should become maximized or floating
-- when switching to floating layout.
local unmaximized_state = {}
-- Store on which screen a client is. This is needed because when setting the
-- geometry of a client, the client seems to get moved to the first screen
-- regardless of which screen it was on.
local clients_screens = {}
-- When the geometry of a client changes, and the layout is floating, then
-- store the client's maximized state. And if the client is also not maximized,
-- then also store the client's geometry. This will be used to restore the
-- client's geometry when they become floating again.
client.connect_signal("property::geometry", function(c)
clients_screens[c.window] = c.screen
if (
awesome.startup
or awful.layout.get(awful.screen.focused()) ~= awful.layout.suit.floating
) then
-- Don't store anything during startup, or when we're not in a floating
-- layout.
return
end
-- Store the unmaximized state
unmaximized_state[c.window] = not c.maximized
if c.maximized then
-- Don't store geometry for maximized clients
return
end
if floating_client_geometries[c.window] then
-- Store the previous client geometry, before saving new one, for
-- the unmaximize trigger.
prev_floating_client_geometries[c.window] = floating_client_geometries[c.window]
end
-- Store client geometry
floating_client_geometries[c.window] = c:geometry()
end)
-- When the layout changes, then if the layout is floating, restore the
-- geometry of clients that were not maximized the last time the floating
-- layout was used, if there is any previously stored geometry to restore.
tag.connect_signal("property::layout", function(t)
if t.layout == awful.layout.suit.floating then
for k, c in ipairs(t:clients()) do
if (
floating_client_geometries and unmaximized_state
and floating_client_geometries[c.window]
and unmaximized_state[c.window]
) then
-- Restore client geometry
c:geometry(floating_client_geometries[c.window])
else
-- If the client was maximized in the last used floating layout,
-- or if there's no geometry to restore, just set the client to
-- maximized.
c.maximized = true
end
end
else
-- If the layout is not floating, then remove the maximized state from
-- all clients, otherwise those layouts don't work properly.
for k, c in ipairs(t:clients()) do c.maximized = false end
end
end)
-- When changing a maximized client to unmaximized in a floating layout, then
-- restore the floating client geometry to what it was in the previous floating
-- state.
client.connect_signal("property::maximized", function(c)
if (
c.maximized
or awful.layout.get(awful.screen.focused()) ~= awful.layout.suit.floating
) then
-- If the client is maximized, or the layout is not floating, don't
-- restore anything.
return
end
if prev_floating_client_geometries[c.window] then
-- Restore client geometry
c:geometry(prev_floating_client_geometries[c.window])
else
-- If there is no previous geometry state, then center the client nicely in the
-- middle of the screen, so that it's not off bounds or in a corner or
-- extremely small or anything. Then it's easy to move / resize the floating
-- client to your wish.
local g = c.screen.geometry
c:geometry({
x = g.width / 6,
y = g.height / 6,
width = g.width / 1.5,
height = g.height / 1.5
})
-- Restore the client screen, because this seems to get lost due to the
-- `c:geometry()` call. This issue only occurs when you have multiple
-- screens in use.
c.screen = clients_screens[c.window]
end
end)
-- When a new client appears, reset the geometry states. Because this signal
-- comes after the geometry signal, and the geometry signal in this case stores
-- incorrect geometries for a new client. So since this signal comes after the
-- geometry signal, we can reset it, so a new client starts with a blank slate.
client.connect_signal("manage", function (c)
floating_client_geometries[c.window] = nil
prev_floating_client_geometries[c.window] = nil
end)
@guidipolito
Copy link

Thank you 🤝

@gitaarik
Copy link
Author

@guidipolito I'm actually using this now to center a client, which takes into account multi-display setups. At least horizontally, multi-display.

local center_client = function(_client)

    _client.maximized = false
    _client.maximized_horizontal = false
    _client.maximized_vertical = false
    _client.floating = true

    local total_screens_left_width = 0

    for s in screen do

        if s == _client.screen then
            break
        end

        total_screens_left_width = total_screens_left_width + s.geometry.width

    end

    local g = _client.screen.geometry

    _client:geometry({
        x = total_screens_left_width + (g.width / 6),
        y = g.height / 6,
        width = g.width / 1.5,
        height = g.height / 1.5
    })

end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment