only operate on surface copies
Working with icons is tricky because their surfaces do not use reference counting correctly. If gears.surface(c.icon) is called multiple time on the same icon, it will cause a double-free error and Awesome will crash.
This commit is contained in:
parent
7b37f7eece
commit
0afab7248e
170
init.lua
170
init.lua
|
@ -1,104 +1,130 @@
|
|||
local gears = require("gears")
|
||||
local awful = require("awful")
|
||||
local theme = require("beautiful")
|
||||
local cairo = require("lgi").cairo
|
||||
|
||||
local module = {}
|
||||
|
||||
-- luacheck: globals client
|
||||
local client = client
|
||||
|
||||
local icons, dynamic_icons, dynamic_classes, delay
|
||||
|
||||
local function len(T)
|
||||
local count = 0
|
||||
for _ in pairs(T) do count = count + 1 end
|
||||
return count
|
||||
local count = 0
|
||||
for _ in pairs(T) do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local function contains(T, V)
|
||||
for _, v in ipairs(T) do
|
||||
if v == V then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
for _, v in ipairs(T) do
|
||||
if v == V then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function icon_copy(icon)
|
||||
if not icon then
|
||||
return nil
|
||||
end
|
||||
local s = gears.surface(icon)
|
||||
local img = cairo.ImageSurface.create(cairo.Format.ARGB32, s:get_width(), s:get_height())
|
||||
local cr = cairo.Context(img)
|
||||
cr:set_source_surface(s, 0, 0)
|
||||
cr:paint()
|
||||
return img
|
||||
end
|
||||
|
||||
local function set_icon(c, icon)
|
||||
if c and c.valid then
|
||||
if not c.icon_backup then
|
||||
c.icon_backup = icons[c.class] and gears.surface(icons[c.class]) or gears.surface(c.icon)
|
||||
end
|
||||
if icon then
|
||||
icon = gears.surface(icon)
|
||||
c.icon = icon and icon._native or nil
|
||||
end
|
||||
end
|
||||
if c and c.valid then
|
||||
if not c.icon_backup then
|
||||
c.icon_backup = icon_copy(icons[c.class]) or icon_copy(c.icon)
|
||||
end
|
||||
icon = icon_copy(icon)
|
||||
if icon then
|
||||
c.icon = icon._native
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function set_dynamic_icon(c)
|
||||
if c.name then
|
||||
for regex, icon in pairs(dynamic_icons) do
|
||||
if string.find(c.name, regex) then
|
||||
set_icon(c, icon)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
if c.name then
|
||||
for regex, icon in pairs(dynamic_icons) do
|
||||
if string.find(c.name, regex) then
|
||||
set_icon(c, icon)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if c.icon_backup then
|
||||
c.icon = c.icon_backup and c.icon_backup._native or nil
|
||||
end
|
||||
if c.icon_backup then
|
||||
c.icon = c.icon_backup._native or nil
|
||||
end
|
||||
end
|
||||
|
||||
local function setup(config)
|
||||
local cfg = config or {}
|
||||
local cfg = config or {}
|
||||
|
||||
icons = cfg.icons or theme.ic_icons or {}
|
||||
dynamic_icons = cfg.dynamic_icons or theme.ic_dynamic_icons or {}
|
||||
dynamic_classes = cfg.dynamic_classes or theme.ic_dynamic_classes or {}
|
||||
delay = cfg.delay or 0.5
|
||||
icons = cfg.icons or theme.ic_icons or {}
|
||||
dynamic_icons = cfg.dynamic_icons or theme.ic_dynamic_icons or {}
|
||||
dynamic_classes = cfg.dynamic_classes or theme.ic_dynamic_classes or {}
|
||||
delay = cfg.delay or 0.5
|
||||
|
||||
if type(icons) ~= 'table' then icons = {} end
|
||||
if type(dynamic_icons) ~= 'table' then dynamic_icons = {} end
|
||||
if type(dynamic_classes) ~= 'table' then dynamic_classes = {} end
|
||||
if type(delay) ~= 'number' then delay = 0.5 end
|
||||
if type(icons) ~= 'table' then
|
||||
icons = {}
|
||||
end
|
||||
if type(dynamic_icons) ~= 'table' then
|
||||
dynamic_icons = {}
|
||||
end
|
||||
if type(dynamic_classes) ~= 'table' then
|
||||
dynamic_classes = {}
|
||||
end
|
||||
if type(delay) ~= 'number' then
|
||||
delay = 0.5
|
||||
end
|
||||
|
||||
client.connect_signal("manage", function(c)
|
||||
-- set icon based on c.class
|
||||
awful.spawn.easy_async_with_shell("sleep " .. delay, function()
|
||||
if c and c.valid and icons[c.class] then
|
||||
set_icon(c, icons[c.class])
|
||||
end
|
||||
end)
|
||||
client.connect_signal("manage", function(c)
|
||||
-- set icon based on c.class
|
||||
awful.spawn.easy_async_with_shell("sleep " .. delay, function()
|
||||
if c and c.valid and icons[c.class] then
|
||||
set_icon(c, icons[c.class])
|
||||
end
|
||||
end)
|
||||
|
||||
if len(dynamic_icons) == 0 then
|
||||
-- user has not defined any dynamic_icons; exit
|
||||
return
|
||||
end
|
||||
if len(dynamic_icons) == 0 then
|
||||
-- user has not defined any dynamic_icons; exit
|
||||
return
|
||||
end
|
||||
|
||||
-- dynamic classes are only being checked if defined explicitly by the user
|
||||
if len(dynamic_classes) > 0 then
|
||||
if not contains(dynamic_classes, c.class) then
|
||||
-- client is not in dynamic_classes; exit
|
||||
return
|
||||
end
|
||||
end
|
||||
-- dynamic classes are only being checked if defined explicitly by the user
|
||||
if len(dynamic_classes) > 0 then
|
||||
if not contains(dynamic_classes, c.class) then
|
||||
-- client is not in dynamic_classes; exit
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- the client name is now being monitored
|
||||
c:connect_signal("property::name", set_dynamic_icon)
|
||||
end)
|
||||
-- the client name is now being monitored
|
||||
c:connect_signal("property::name", set_dynamic_icon)
|
||||
end)
|
||||
end
|
||||
|
||||
return setmetatable(module, { __call = function(_, ...)
|
||||
setup(...)
|
||||
-- we have to update all clients icons manually when the user restarts awesomewm
|
||||
-- since there is no "property::name" signal emitted by already running clients.
|
||||
-- the set delay has to be higher than the regular delay, otherwise a race condition between both signals exists.
|
||||
awful.spawn.easy_async_with_shell("sleep " .. (delay + 0.5), function()
|
||||
for _, c in ipairs(client.get()) do
|
||||
if c and c.valid then
|
||||
c:emit_signal("property::name")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end })
|
||||
return setmetatable(module, {
|
||||
__call = function(_, ...)
|
||||
setup(...)
|
||||
-- we have to update all clients icons manually when the user restarts awesomewm
|
||||
-- since there is no "property::name" signal emitted by already running clients.
|
||||
-- the set delay has to be higher than the regular delay, otherwise a race condition between both signals exists.
|
||||
awful.spawn.easy_async_with_shell("sleep " .. (delay + 0.5), function()
|
||||
for _, c in ipairs(client.get()) do
|
||||
if c and c.valid then
|
||||
c:emit_signal("property::name")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue