-- Facility to dump out a file describing all the clients attached to -- awesome, all the tags on all the screens, and to allow that to then -- be re-created on restart. -- -- Essentially to allow restarts of awesome with as close to -- the currently running shape as possible -- module(..., package.seeall) function print(...) local space = "" for _, v in ipairs{...} do io.stderr:write(space, tostring(v)) space = "\t" end io.stderr:write("\n") io.stderr:flush() end local layoutname = { [awful.layout.suit.floating] = "floating", [awful.layout.suit.fair] = "fair", [awful.layout.suit.tile] = "tile", [awful.layout.suit.max] = "max", [awful.layout.suit.spiral] = "spiral", [awful.layout.suit.magnifier] = "magnifier", } local function _dumpstate(f) local space = "" f:write("-- Screens\n\n") local tagmap = {} for i = 1, screen.count() do local scr = screen[i] -- To identify a screen, we can't rely on its index, instead we -- rely on its geometry, initially we consider its width and -- height only and if that is ambiguous then we use its x/y, but -- this is only during restoration anyway. f:write("DefineScreen {\n") local geom = scr.geometry f:write(" geometry = {") space = "" for _, v in ipairs{"x","y","width","height"} do f:write(space, v, " = ", tostring(geom[v])) space = ", " end f:write("},\n") local tags = scr:tags() f:write(" tags = {") space = "\n " for _, tag in ipairs(tags) do tagmap[#tagmap+1] = tag tagmap[tag] = #tagmap f:write(("%s{ id = %d, name = %q,\n props = {\n"):format(space, #tagmap, tag.name)) for k, v in pairs(awful.tag.getdata(tag) or {}) do if k ~= "layout" then if type(v) == "string" then v = ("%q"):format(v) else v = tostring(v) end f:write((" [%q] = %s,\n"):format(k, v)) end end local layout = awful.tag.getproperty(tag, "layout") f:write(" layout = awful.layout.suit.", layoutname[layout] or "floating", "\n }\n") f:write(" }") space = ",\n " end f:write("\n },\n") f:write(" id = ", tostring(i), "\n}\n\n") end f:write("\n\n-- Clients\n\n") local all_clients = client.get(); for _, c in ipairs(all_clients) do f:write("DefineClient {\n") f:write(" -- ", tostring(c.name), " [", tostring(c.class), "/", tostring(c.instance), "]\n") f:write(" screen = ", tostring(c.screen), ",\n") f:write(" tags = { ") space = "" for _, tag in ipairs(c:tags()) do f:write(("%s%d"):format(space, tagmap[tag])) space = ", " end f:write(" },\n") local geom = c:geometry() f:write(" geometry = {") space = "" for _, v in ipairs{"x","y","width","height"} do f:write(space, v, " = ", tostring(geom[v])) space = ", " end f:write("},\n") for _, prop in ipairs{"maximized_vertical", "maximized_horizontal", "minimized"} do -- boolean property f:write(" ", prop, " = ", (c[prop] and "true" or "false"), ",\n") end f:write(" id = ", tostring(c.window), "\n}\n\n") end end local function dump_awesome_state(fname) local f = io.open(fname, "w") local ok, msg = pcall(_dumpstate, f) if not ok then f:write("\n\n-- ", msg, "\n\n") f:close() -- os.execute(("rm -f %q"):format(fname)) return false end f:close() return true end local reloader_filename local function reloader_atexit(is_restart) print "reloader: atexit" if is_restart then print "reloader: restart indicated" dump_awesome_state(reloader_filename) else print "reloader: restart not indicated" end end local client_screen_map = {} local client_tag_map = setmetatable({}, { __mode = "v" }) local client_data_map = {} local function load_restore_state(f) local func = assert(loadfile(f)) print ("reloader: Loaded configuration from " .. f) local screens, clients = {}, {} local function _DefineScreen(s) screens[#screens+1] = s end local function _DefineClient(c) clients[#clients+1] = c end setfenv(func, setmetatable({ DefineScreen = _DefineScreen, DefineClient = _DefineClient }, { __index = _G })) func() print("reloader: Defined", #screens, "screens and ", #clients, "clients") local screenmap = client_screen_map for _, thisscreen in ipairs(screens) do -- Attempt to match this screen to the real screens print("reloader: Attempting to match geometry for incoming screen ", thisscreen.id) local thisgeom = thisscreen.geometry local foundscreen for i = 1, screen.count() do local scr = screen[i] local scrgeom = scr.geometry if thisgeom.x == scrgeom.x and thisgeom.y == scrgeom.y and thisgeom.width == scrgeom.width and thisgeom.height == scrgeom.height then foundscreen = i end end if not foundscreen then for i = 1, screen.count() do local scr = screen[i] local scrgeom = scr.geometry if thisgeom.width == scrgeom.width and thisgeom.height == scrgeom.height then foundscreen = i end end end if not foundscreen then -- If we've not found a candidate then drop it onto screen 1 foundscreen = 1 end print("reloader: Mapping screen", thisscreen.id, "to", foundscreen) screenmap[thisscreen.id] = foundscreen end -- We've mapped the screens, so now we run through, preparing the tags -- We merge tags which were named the same if they fold onto screen 1 local folding_tag_map = {} for i = 1, screen.count() do folding_tag_map[i] = { _byid = {}} end for _, thisscreen in ipairs(screens) do for _, tagtab in ipairs(thisscreen.tags) do tagtab.props.name = tagtab.name local newtag = (folding_tag_map[screenmap[thisscreen.id]][tagtab.name] or shifty.add(tagtab.props)) print("reloader: tag", newtag, "is", tagtab.name, "on", thisscreen.id, "[",screenmap[thisscreen.id], "]") if not folding_tag_map[screenmap[thisscreen.id]][tagtab.name] then folding_tag_map[screenmap[thisscreen.id]][tagtab.name] = newtag local by = folding_tag_map[screenmap[thisscreen.id]]._byid by[#by+1] = newtag print("reloader: that tag is nr", #by, "on my list") end -- The client tag map has weak values which means that -- the result might be nil on read client_tag_map[tagtab.id] = newtag end end -- Tags are folded and prepped, assign them to screens for i = 1, screen.count() do --[[ -- Seems to be bullshit print("reloader: clearing tags for screen", i) local tags_to_del = screen[i]:tags() for i, tag in ipairs(tags_to_del) do print("reloader: dumping tag", tag) shifty.del(tag) end --]] assert(next(folding_tag_map[i]._byid), "No tags for screen " .. tostring(i)) screen[i]:tags(folding_tag_map[i]._byid) awful.tag.viewonly(folding_tag_map[i]._byid[1]) end -- Right now, there's probably no clients (phew) so we'll not -- worry too much about assigning them anywhere. -- Just build the client_data_map for try_place_client later for _, thisclient in ipairs(clients) do print("reloader: defined client", thisclient.id) client_data_map[tonumber(thisclient.id)] = thisclient end -- Aaaand we're done end function prepare_reload_state(fname) -- The filename is where the reloader state will be. reloader_filename = fname local f = io.open(fname, "r") if f then f:close() local ok, msg = pcall(load_restore_state, fname) if not ok then naughty.notify { title = "Failure restoring state", text = msg } print("reloader: Error loading",fname,"::",msg) -- Zero off any client map client_data_map = {} end os.execute(("mv %q %q"):format(fname, fname .. ".old")) end -- Either way, register the atexit print "reloader: connecting atexit" awesome.connect_signal("exit", reloader_atexit) end function try_place_client(c, startup) -- Perform a client placement if we can find hints for it This is -- done *only* during startup management. Startup management is -- done during awesome startup. so it will only happen once we're -- loaded and then reloaded. if not startup then return end print("placement: Attempting to place", c.window) local datamap = client_data_map[tonumber(c.window)] if datamap then client_data_map[c.window] = nil else print("placement: Could not find mapping for", c.window) return end -- Apply the settings from the map to the client. -- First, make sure it's on the right tag and screen c.screen = client_screen_map[datamap.screen] print("placement: Client asked for screen", datamap.screen, "which is really screen", c.screen) local tags, tagset = {}, {} for _, id in ipairs(datamap.tags) do local thistag = client_tag_map[id] print("placement: Client in tag", id, "which is", thistag) if not thistag then -- if we failed to map this tag, we treat it as the first -- tag on the first screen thistag = screen[c.screen]:tags()[1] end if not tagset[thistag] then tags[#tags+1] = thistag tagset[thistag] = true end end if #tags == 0 then print("placement: Could not identify tag, defaulting...") tags[1] = screen[c.screen]:tags()[1] end c:tags(tags) -- Now apply geometry c:geometry(datamap.geometry) -- Now the boolean properties for _, prop in ipairs{"maximized_horizontal", "maximized_vertical", "minimized"} do print("placement: setting",prop,"to",datamap[prop]) c[prop] = datamap[prop] end -- We're done restoring this client. Woop Woop Woop end