Reworked a whole lot. New config file and theme config file for easier changes. Did #19. And much more
This commit is contained in:
74
awesome/src/tools/desktop_parser.lua
Normal file
74
awesome/src/tools/desktop_parser.lua
Normal file
@@ -0,0 +1,74 @@
|
||||
local gears = require("gears")
|
||||
local GLib = require("lgi").GLib
|
||||
|
||||
---Will return every $XDG_DATA_DIRS
|
||||
---@return table
|
||||
local function get_paths()
|
||||
local dirs = {}
|
||||
|
||||
local dir
|
||||
for _, value in ipairs(GLib.get_system_data_dirs()) do
|
||||
dir = GLib.build_filenamev({ value, "applications" })
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
end
|
||||
|
||||
dir = GLib.build_filenamev({ GLib.get_user_data_dir(), "applications" })
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
|
||||
return dirs
|
||||
end
|
||||
|
||||
---Returns every .desktop file into a table
|
||||
---@param file table .desktop files
|
||||
---@return table
|
||||
return function(file)
|
||||
|
||||
if not file or file == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local handler = nil
|
||||
|
||||
for _, dir in ipairs(get_paths()) do
|
||||
handler = io.open(dir .. "/" .. file, "r")
|
||||
if handler then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not handler then
|
||||
return
|
||||
end
|
||||
|
||||
local desktop_table = {}
|
||||
while true do
|
||||
local line = handler:read()
|
||||
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
|
||||
if line:match("[Desktop Entry]") then
|
||||
while true do
|
||||
local property = handler:read()
|
||||
if not property then
|
||||
break
|
||||
end
|
||||
|
||||
if property:match("^%[(.+)%]") then
|
||||
return desktop_table
|
||||
end
|
||||
|
||||
if property:match("^Name=") then
|
||||
desktop_table["Name"] = property:match("Name=(.+)")
|
||||
elseif property:match("^Exec") then
|
||||
-- Second match is to remove the %u and %F some applications use to open a URI. It's not needed here
|
||||
desktop_table["Exec"] = property:match("Exec=(.+)"):gsub("%%u", ""):gsub("%%F", "")
|
||||
elseif property:match("^Icon=") then
|
||||
desktop_table["Icon"] = property:match("Icon=(.+)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return desktop_table
|
||||
end
|
||||
@@ -1,95 +0,0 @@
|
||||
-----------------------------------------------------
|
||||
-- Helper to get icons from a program/program name --
|
||||
-----------------------------------------------------
|
||||
local icon_cache = {}
|
||||
|
||||
-- tries to find a matching file name in /usr/share/icons/THEME/RESOLUTION/apps/ and if not found tried with first letter
|
||||
-- as uppercase, this should get almost all icons to work with the papirus theme atleast
|
||||
-- TODO: try with more icon themes
|
||||
function Get_icon(theme, client, program_string, class_string, is_steam)
|
||||
|
||||
client = client or nil
|
||||
program_string = program_string or nil
|
||||
class_string = class_string or nil
|
||||
is_steam = is_steam or nil
|
||||
|
||||
if theme and (client or program_string or class_string) then
|
||||
local clientName
|
||||
if is_steam then
|
||||
clientName = "steam_icon_" .. tostring(client) .. ".svg"
|
||||
elseif client then
|
||||
if client.class then
|
||||
clientName = string.lower(client.class:gsub(" ", "")) .. ".svg"
|
||||
elseif client.name then
|
||||
clientName = string.lower(client.name:gsub(" ", "")) .. ".svg"
|
||||
else
|
||||
if client.icon then
|
||||
return client.icon
|
||||
else
|
||||
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
|
||||
end
|
||||
end
|
||||
else
|
||||
if program_string then
|
||||
clientName = program_string .. ".svg"
|
||||
else
|
||||
clientName = class_string .. ".svg"
|
||||
end
|
||||
end
|
||||
|
||||
for _, icon in ipairs(icon_cache) do
|
||||
if icon:match(clientName) then
|
||||
return icon
|
||||
end
|
||||
end
|
||||
|
||||
local resolutions = { "128x128", "96x96", "64x64", "48x48", "42x42", "32x32", "24x24", "16x16" }
|
||||
for _, res in ipairs(resolutions) do
|
||||
local iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
|
||||
local ioStream = io.open(iconDir .. clientName, "r")
|
||||
if ioStream ~= nil then
|
||||
icon_cache[#icon_cache + 1] = iconDir .. clientName
|
||||
return iconDir .. clientName
|
||||
else
|
||||
clientName = clientName:gsub("^%l", string.upper)
|
||||
iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
|
||||
ioStream = io.open(iconDir .. clientName, "r")
|
||||
if ioStream ~= nil then
|
||||
icon_cache[#icon_cache + 1] = iconDir .. clientName
|
||||
return iconDir .. clientName
|
||||
elseif not class_string then
|
||||
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
|
||||
else
|
||||
clientName = class_string .. ".svg"
|
||||
iconDir = "/usr/share/icons/" .. theme .. "/" .. res .. "/apps/"
|
||||
ioStream = io.open(iconDir .. clientName, "r")
|
||||
if ioStream ~= nil then
|
||||
icon_cache[#icon_cache + 1] = iconDir .. clientName
|
||||
return iconDir .. clientName
|
||||
else
|
||||
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if client then
|
||||
return "/usr/share/icons/Papirus-Dark/128x128/apps/application-default-icon.svg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--———————————No swiches?———————————
|
||||
--⠀⣞⢽⢪⢣⢣⢣⢫⡺⡵⣝⡮⣗⢷⢽⢽⢽⣮⡷⡽⣜⣜⢮⢺⣜⢷⢽⢝⡽⣝
|
||||
--⠸⡸⠜⠕⠕⠁⢁⢇⢏⢽⢺⣪⡳⡝⣎⣏⢯⢞⡿⣟⣷⣳⢯⡷⣽⢽⢯⣳⣫⠇
|
||||
--⠀⠀⢀⢀⢄⢬⢪⡪⡎⣆⡈⠚⠜⠕⠇⠗⠝⢕⢯⢫⣞⣯⣿⣻⡽⣏⢗⣗⠏⠀
|
||||
--⠀⠪⡪⡪⣪⢪⢺⢸⢢⢓⢆⢤⢀⠀⠀⠀⠀⠈⢊⢞⡾⣿⡯⣏⢮⠷⠁⠀⠀
|
||||
--⠀⠀⠀⠈⠊⠆⡃⠕⢕⢇⢇⢇⢇⢇⢏⢎⢎⢆⢄⠀⢑⣽⣿⢝⠲⠉⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⠀⡿⠂⠠⠀⡇⢇⠕⢈⣀⠀⠁⠡⠣⡣⡫⣂⣿⠯⢪⠰⠂⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⡦⡙⡂⢀⢤⢣⠣⡈⣾⡃⠠⠄⠀⡄⢱⣌⣶⢏⢊⠂⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⢝⡲⣜⡮⡏⢎⢌⢂⠙⠢⠐⢀⢘⢵⣽⣿⡿⠁⠁⠀⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⠨⣺⡺⡕⡕⡱⡑⡆⡕⡅⡕⡜⡼⢽⡻⠏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⣼⣳⣫⣾⣵⣗⡵⡱⡡⢣⢑⢕⢜⢕⡝⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⣴⣿⣾⣿⣿⣿⡿⡽⡑⢌⠪⡢⡣⣣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⡟⡾⣿⢿⢿⢵⣽⣾⣼⣘⢸⢸⣞⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
--⠀⠀⠀⠀⠁⠇⠡⠩⡫⢿⣝⡻⡮⣒⢽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
||||
--—————————————————————————————
|
||||
106
awesome/src/tools/theme_index.lua
Normal file
106
awesome/src/tools/theme_index.lua
Normal file
@@ -0,0 +1,106 @@
|
||||
local theme_index = { mt = {} }
|
||||
|
||||
theme_index.new = function(cls, theme, basedirs)
|
||||
local self = {}
|
||||
setmetatable(self, { __index = cls })
|
||||
|
||||
self.icon_theme_name = theme or User_config.icon_theme
|
||||
self.base_dir = nil
|
||||
self["Directories"] = {}
|
||||
self["Inherits"] = {}
|
||||
self.per_directory_keys = {}
|
||||
|
||||
local basedir, handler = nil, nil
|
||||
for _, dir in ipairs(basedirs) do
|
||||
basedir = dir .. "/" .. self.icon_theme_name
|
||||
handler = io.open(basedir .. "/index.theme", "r")
|
||||
if handler then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not handler then
|
||||
return self
|
||||
end
|
||||
self.base_dir = basedir
|
||||
|
||||
while true do
|
||||
local line = handler:read()
|
||||
|
||||
if not line then
|
||||
break
|
||||
end
|
||||
|
||||
local header = line:match("^%[(.+)%]$")
|
||||
if header then
|
||||
if header == "Icon Theme" then
|
||||
while true do
|
||||
local property = handler:read()
|
||||
|
||||
if not property then
|
||||
break
|
||||
end
|
||||
|
||||
if property:match("^%[(.+)%]$") then
|
||||
handler:seek("cur", -string.len(property) - 1)
|
||||
break
|
||||
end
|
||||
|
||||
local key, value = property:match("^(%w+)=(.*)$")
|
||||
if key == "Directories" or key == "Inherits" then
|
||||
string.gsub(value, "([^,]+),?", function(match)
|
||||
table.insert(self[key], match)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
local keys = {}
|
||||
|
||||
while true do
|
||||
local property = handler:read()
|
||||
if not property then
|
||||
break
|
||||
end
|
||||
|
||||
if property:match("^%[(.+)%]$") then
|
||||
handler:seek("cur", -string.len(property) - 1)
|
||||
break
|
||||
end
|
||||
|
||||
local key, value = property:match("^(%w+)=(%w+)$")
|
||||
if key == "Size" or key == "MinSize" or key == "MaxSize" or key == "Threshold" then
|
||||
keys[key] = tonumber(value)
|
||||
elseif key == "Type" then
|
||||
keys[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
if keys["Size"] then
|
||||
if not keys["Type"] then keys["Type"] = "Threshold" end
|
||||
if not keys["MinSize"] then keys["MinSize"] = keys["Size"] end
|
||||
if not keys["MaxSize"] then keys["MaxSize"] = keys["Size"] end
|
||||
if not keys["Threshold"] then keys["Threshold"] = 2 end
|
||||
self.per_directory_keys[header] = keys
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
handler:close()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
theme_index.get_subdirectories = function(self)
|
||||
return self["Directories"]
|
||||
end
|
||||
|
||||
theme_index.get_inherits = function(self)
|
||||
return self["Inherits"]
|
||||
end
|
||||
|
||||
theme_index.mt.__call = function(cls, theme, basedirs)
|
||||
return theme_index.new(cls, theme, basedirs)
|
||||
end
|
||||
|
||||
return setmetatable(theme_index, theme_index.mt)
|
||||
236
awesome/src/tools/xdg_icon_lookup.lua
Normal file
236
awesome/src/tools/xdg_icon_lookup.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
-----------------------------------------------------
|
||||
-- Helper to get icons from a program/program name --
|
||||
-----------------------------------------------------
|
||||
|
||||
local gears = require("gears")
|
||||
local GLib = require("lgi").GLib
|
||||
|
||||
local theme_index = require("src.tools.theme_index")
|
||||
|
||||
local function get_basedir()
|
||||
local dirs = {}
|
||||
|
||||
local dir = GLib.build_filenamev({ GLib.get_home_dir(), ".icons" })
|
||||
if gears.filesystem.dir_readable(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
|
||||
dir = GLib.build_filenamev({ GLib.get_user_data_dir(), "icons" })
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
|
||||
for _, value in ipairs(GLib.get_system_data_dirs()) do
|
||||
dir = GLib.build_filenamev({ value, ".icons" })
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
end
|
||||
|
||||
local need_usr_share_pixmaps = true
|
||||
for _, value in ipairs(GLib.get_system_data_dirs()) do
|
||||
dir = GLib.build_filenamev({ value, ".icons" })
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
if dir == "/usr/share/pixmaps" then
|
||||
need_usr_share_pixmaps = false
|
||||
end
|
||||
end
|
||||
|
||||
dir = "/usr/share/pixmaps"
|
||||
if gears.filesystem.dir_readable(dir) then table.insert(dirs, dir) end
|
||||
if need_usr_share_pixmaps and gears.filesystem.dir_readable(dir) then
|
||||
table.insert(dirs, dir)
|
||||
end
|
||||
|
||||
table.insert(dirs, "/usr/share/icons")
|
||||
|
||||
return dirs
|
||||
end
|
||||
|
||||
local xdg_icon_lookup = { mt = {} }
|
||||
|
||||
local icon_cache = {}
|
||||
|
||||
xdg_icon_lookup.new = function(theme, base_dirs)
|
||||
local self = {}
|
||||
|
||||
self.icon_theme = theme or User_config.icon_theme
|
||||
self.base_directories = base_dirs or get_basedir()
|
||||
self.file_extension = { "svg", "png", "xpm" }
|
||||
|
||||
if not icon_cache[self.icon_theme] then
|
||||
icon_cache[self.icon_theme] = {}
|
||||
end
|
||||
|
||||
local cache_key = table.concat(self.base_directories, ":")
|
||||
if not icon_cache[self.icon_theme][cache_key] then
|
||||
icon_cache[self.icon_theme][cache_key] = theme_index(self.icon_theme, self.base_directories)
|
||||
end
|
||||
self.theme_index = icon_cache[self.icon_theme][cache_key]
|
||||
|
||||
return setmetatable(self, { __index = xdg_icon_lookup })
|
||||
end
|
||||
|
||||
---Look for an fallback icon
|
||||
---@param iconname any
|
||||
---@return string
|
||||
local function lookup_fallback_icon(self, iconname)
|
||||
for _, dir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.file_extension) do
|
||||
local filename = string.format("%s/%s.%s", dir, iconname, ext)
|
||||
if gears.filesystem.file_readable(filename) then
|
||||
return filename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Checkes if the size equals the actual icon size
|
||||
---@param subdir any
|
||||
---@param iconsize any
|
||||
---@return boolean
|
||||
local function directory_matches_size(self, subdir, iconsize)
|
||||
local type, size, min_size, max_size, threshold = self.theme_index.per_directory_keys[subdir]["Type"],
|
||||
self.theme_index.per_directory_keys[subdir]["Size"], self.theme_index.per_directory_keys[subdir]["MinSize"],
|
||||
self.theme_index.per_directory_keys[subdir]["MaxSize"], self.theme_index.per_directory_keys[subdir]["Threshold"]
|
||||
|
||||
if type == "Fixed" then
|
||||
return iconsize == size
|
||||
elseif type == "Scalable" then
|
||||
return iconsize >= min_size and iconsize <= max_size
|
||||
elseif type == "Threshold" then
|
||||
return iconsize >= size - threshold and iconsize <= size + threshold
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---Returns how far off the size is from the actual icon size
|
||||
---@param subdir table
|
||||
---@param iconsize number
|
||||
---@return number
|
||||
local function directory_size_distance(self, subdir, iconsize)
|
||||
local type, size, min_size, max_size, threshold = self.theme_index.per_directory_keys[subdir]["Type"],
|
||||
self.theme_index.per_directory_keys[subdir]["Size"], self.theme_index.per_directory_keys[subdir]["MinSize"],
|
||||
self.theme_index.per_directory_keys[subdir]["MaxSize"], self.theme_index.per_directory_keys[subdir]["Threshold"]
|
||||
|
||||
if type and min_size and max_size and threshold then
|
||||
if type == "Fixed" then
|
||||
return math.abs(size - iconsize)
|
||||
elseif type == "Scalable" then
|
||||
if iconsize < min_size then
|
||||
return min_size - iconsize
|
||||
elseif iconsize > max_size then
|
||||
return iconsize - max_size
|
||||
end
|
||||
return 0
|
||||
elseif type == "Threshold" then
|
||||
if iconsize < size - threshold then
|
||||
return min_size - iconsize
|
||||
elseif iconsize > size + threshold then
|
||||
return iconsize - max_size
|
||||
end
|
||||
return 0
|
||||
end
|
||||
end
|
||||
return 0xffffffff
|
||||
end
|
||||
|
||||
---Checks each and every sub directory for an icon
|
||||
---@param iconname any
|
||||
---@param size any
|
||||
---@return string path_to_icon
|
||||
local function lookup_icon(self, iconname, size)
|
||||
local already_checked = {}
|
||||
for _, subdir in ipairs(self.theme_index:get_subdirectories()) do
|
||||
for _, dir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.file_extension) do
|
||||
if directory_matches_size(self, subdir, size) then
|
||||
local filename = string.format("%s/%s/%s/%s.%s", dir, self.icon_theme, subdir, iconname, ext)
|
||||
if gears.filesystem.file_readable(filename) then
|
||||
return filename
|
||||
else
|
||||
already_checked[filename] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local min_size = 0xffffffff
|
||||
local closest_filename = nil
|
||||
for _, subdir in ipairs(self.theme_index:get_subdirectories()) do
|
||||
local dist = directory_size_distance(self, subdir, size)
|
||||
if dist < min_size then
|
||||
for _, dir in ipairs(self.base_directories) do
|
||||
for _, ext in ipairs(self.file_extension) do
|
||||
local filename = string.format("%s/%s/%s/%s.%s", dir, self.icon_theme, subdir, iconname, ext)
|
||||
if not already_checked[filename] then
|
||||
if gears.filesystem.file_readable(filename) then
|
||||
closest_filename = filename
|
||||
min_size = dist
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return closest_filename or nil
|
||||
end
|
||||
|
||||
---Check if the icon inherits from another icon theme and search that for an icon
|
||||
---@param icon any
|
||||
---@param size any
|
||||
---@param self any
|
||||
---@return string path_to_icon
|
||||
local function find_icon_helper(self, icon, size)
|
||||
local filename = lookup_icon(self, icon, size)
|
||||
if filename then return filename end
|
||||
|
||||
filename = lookup_icon(self, icon:lower(), size)
|
||||
if filename then return filename end
|
||||
|
||||
for _, parent in ipairs(self.theme_index:get_inherits()) do
|
||||
filename = find_icon_helper(xdg_icon_lookup(parent, self.base_directories), icon, size)
|
||||
if filename then return filename end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local iconcache = {}
|
||||
---Takes an icon and its props and theme to search for it inside the theme
|
||||
---@param icon any
|
||||
---@param size any
|
||||
---@return string path_to_icon
|
||||
function xdg_icon_lookup:find_icon(icon, size)
|
||||
if iconcache[icon] then
|
||||
return iconcache[icon]
|
||||
end
|
||||
size = size or 64
|
||||
if not icon or icon == "" then return nil end
|
||||
|
||||
local filename = find_icon_helper(self, icon, size)
|
||||
if filename then
|
||||
iconcache[icon] = filename
|
||||
return filename
|
||||
end
|
||||
|
||||
filename = find_icon_helper(xdg_icon_lookup("hicolor", self.base_directories), icon, size)
|
||||
if filename then
|
||||
iconcache[icon] = filename
|
||||
return filename
|
||||
end
|
||||
|
||||
filename = lookup_fallback_icon(self, icon)
|
||||
if filename then
|
||||
iconcache[icon] = filename
|
||||
return filename
|
||||
end
|
||||
|
||||
local fallback = gears.color.recolor_image(require("awful").util.getdir("config") .. "src/assets/icons/fallback.svg",
|
||||
"#ffffff")
|
||||
iconcache[icon] = fallback
|
||||
return fallback
|
||||
end
|
||||
|
||||
xdg_icon_lookup.mt.__call = function(_, ...)
|
||||
return xdg_icon_lookup.new(...)
|
||||
end
|
||||
|
||||
return setmetatable(xdg_icon_lookup, xdg_icon_lookup.mt)
|
||||
Reference in New Issue
Block a user