diff --git a/lib/awful/titlebar.lua b/lib/awful/titlebar.lua index 9197f032c..aedd9aef6 100644 --- a/lib/awful/titlebar.lua +++ b/lib/awful/titlebar.lua @@ -248,11 +248,8 @@ function titlebar.widget.button(c, name, selector, action) -- We do magic based on whether a client is focused above, so we need to -- connect to the corresponding signal here. - local function focus_func(o) - if o == c then update() end - end - capi.client.connect_signal("focus", focus_func) - capi.client.connect_signal("unfocus", focus_func) + c:connect_signal("focus", update) + c:connect_signal("unfocus", update) return ret end @@ -309,6 +306,10 @@ function titlebar.widget.stickybutton(c) return widget end +client.connect_signal("unmanage", function(c) + all_titlebars[c] = nil +end) + return setmetatable(titlebar, { __call = function(_, ...) return new(...) end}) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/awful/widget/tasklist.lua b/lib/awful/widget/tasklist.lua index a0605834f..ccae7fa16 100644 --- a/lib/awful/widget/tasklist.lua +++ b/lib/awful/widget/tasklist.lua @@ -181,6 +181,9 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg queued_update = true end end + function w._unmanage(c) + data[c] = nil + end if instances == nil then instances = {} local function us(s) @@ -219,7 +222,14 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg capi.client.connect_signal("property::hidden", u) capi.client.connect_signal("tagged", u) capi.client.connect_signal("untagged", u) - capi.client.connect_signal("unmanage", u) + capi.client.connect_signal("unmanage", function(c) + u(c) + for s, i in pairs(instances) do + for _, tlist in pairs(i) do + tlist._unmanage(c) + end + end + end) capi.client.connect_signal("list", u) capi.client.connect_signal("focus", u) capi.client.connect_signal("unfocus", u) diff --git a/tests/test-leak-client.lua b/tests/test-leak-client.lua new file mode 100644 index 000000000..205e48248 --- /dev/null +++ b/tests/test-leak-client.lua @@ -0,0 +1,78 @@ +-- Some memory leak checks involving clients as integration tests. +local awful = require("awful") +local wibox = require("wibox") + +-- "Enable" titlebars (so that the titlebar can prevent garbage collection) +client.connect_signal("manage", function (c) + local buttons = awful.util.table.join( + awful.button({ }, 1, function() + client.focus = c + c:raise() + awful.mouse.client.move(c) + end), + awful.button({ }, 3, function() + client.focus = c + c:raise() + awful.mouse.client.resize(c) + end) + ) + + -- Widgets that are aligned to the left + local left_layout = wibox.layout.fixed.horizontal(awful.titlebar.widget.iconwidget(c)) + left_layout:buttons(buttons) + + -- The title goes in the middle + local title = awful.titlebar.widget.titlewidget(c) + title:set_align("center") + local middle_layout = wibox.layout.flex.horizontal(title) + middle_layout:buttons(buttons) + + awful.titlebar(c):set_widget(wibox.layout.align.horizontal( + left_layout, + middle_layout, + wibox.layout.fixed.horizontal( + awful.titlebar.widget.floatingbutton(c), + awful.titlebar.widget.maximizedbutton(c), + awful.titlebar.widget.stickybutton(c), + awful.titlebar.widget.ontopbutton(c), + awful.titlebar.widget.closebutton(c) + )), { position = "bottom"}) +end) + +-- We tell the garbage collector when to work, disable it +collectgarbage("stop") + +-- We try to get a client representing an already-closed client. +-- The first iteration starts xterm, the second keeps a weak reference to it and +-- closes it and the last one checks that the client object is GC'able. +local objs = nil +local steps = { + function(count) + if count == 1 then + awful.spawn("xterm") + elseif not objs then + local c = client.get()[1] + if c then + objs = setmetatable({ c }, { __mode = "v" }) + c:kill() + end + else + assert(#objs == 1) + + -- Test that we have a client and that it's invalid (tostring() + -- causes an "invalid object" error) + local success, msg = pcall(function() tostring(objs[1]) end) + assert(not success) + assert(msg:find("invalid object"), msg) + + -- Check that it is garbage-collectable + collectgarbage("collect") + assert(#objs == 0) + return true + end + end, +} + +require("_runner").run_steps(steps) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80