naughty: Fix an internal index corruption issue.

Due to some signal callbacks, some notifications could be added twice
to the by-position index.
This commit is contained in:
Emmanuel Lepage Vallee 2020-02-09 04:00:36 -05:00
parent 8190ece3b2
commit 816188163a
1 changed files with 27 additions and 2 deletions

View File

@ -227,6 +227,7 @@ local function remove_from_index(n)
for _, ns in pairs(positions) do for _, ns in pairs(positions) do
for k, n2 in ipairs(ns) do for k, n2 in ipairs(ns) do
if n2 == n then if n2 == n then
assert(ns[k+1] ~= n, "The notification index is corrupted")
table.remove(ns, k) table.remove(ns, k)
return return
end end
@ -237,6 +238,11 @@ end
-- When id or screen are set after the object is created, update the indexing. -- When id or screen are set after the object is created, update the indexing.
local function update_index(n) local function update_index(n)
-- Do things in the right order.
if not n._private.registered then return end
assert(not n._private.is_destroyed, "The notification index is corrupted")
-- Find the only index and remove it (there's an useless loop, but it's small). -- Find the only index and remove it (there's an useless loop, but it's small).
remove_from_index(n) remove_from_index(n)
@ -373,6 +379,9 @@ function naughty.destroy_all_notifications(screens, reason)
for _, scr in pairs(screens) do for _, scr in pairs(screens) do
for _, list in pairs(naughty.notifications[scr]) do for _, list in pairs(naughty.notifications[scr]) do
while #list > 0 do while #list > 0 do
-- Better cause an error than risk an infinite loop.
assert(not list[1]._private.is_destroyed)
ret = ret and list[1]:destroy(reason) ret = ret and list[1]:destroy(reason)
end end
end end
@ -580,14 +589,28 @@ naughty.connect_signal("request::screen", naughty.default_screen_handler)
-- @tparam naughty.action action The action. -- @tparam naughty.action action The action.
-- @tparam string icon_name The icon name. -- @tparam string icon_name The icon name.
--- Emitted when the screen is not defined or being removed.
-- @signal request::screen
-- @tparam table notification The `naughty.notification` object. This is
-- currently either "new" or "removed".
-- @tparam string context Why is the signal sent.
-- Register a new notification object. -- Register a new notification object.
local function register(notification, args) local function register(notification, args)
assert(not notification._private.registered)
-- Add the some more properties -- Add the some more properties
rawset(notification, "get_suspended", get_suspended) rawset(notification, "get_suspended", get_suspended)
local s = get_screen(notification.screen or args.screen local s = get_screen(notification.screen or args.screen
or (notification.preset and notification.preset.screen) or (notification.preset and notification.preset.screen))
or screen.focused())
if not s then
naughty.emit_signal("request::screen", notification, "new", {})
s = notification.screen
end
assert(s)
-- insert the notification to the table -- insert the notification to the table
table.insert(naughty._active, notification) table.insert(naughty._active, notification)
@ -595,6 +618,8 @@ local function register(notification, args)
notification.idx = #naughty.notifications[s][notification.position] notification.idx = #naughty.notifications[s][notification.position]
notification.screen = s notification.screen = s
notification._private.registered = true
if properties.suspended and not args.ignore_suspend then if properties.suspended and not args.ignore_suspend then
notification._private.args = args notification._private.args = args
table.insert(naughty.notifications.suspended, notification) table.insert(naughty.notifications.suspended, notification)