2022-08-31 12:20:58 +00:00
-- This file is part of Reno desktop.
--
-- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
--
-- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see <https://www.gnu.org/licenses/>.
2022-07-23 19:10:02 +00:00
-- Asynchronous XDG data aggregator
2023-01-16 12:47:51 +00:00
local start_human = os.time ( )
local start_computer = os.clock ( )
2022-07-23 19:10:02 +00:00
local menu_utils = require ( " menubar.utils " )
local menu_gen = require ( " menubar.menu_gen " )
local awful = require ( " awful " )
local gears = require ( " gears " )
2023-01-16 12:47:51 +00:00
local json = require ( " dkjson " )
2022-07-23 19:10:02 +00:00
menu_utils.wm_name = " "
-- Directories to scan for .desktop files
2023-05-06 08:56:25 +00:00
local desktop_dirs = { os.getenv ( " HOME " ) .. " /Desktop " }
2022-07-23 19:10:02 +00:00
local desktop_dirs_complete = 0
2023-01-16 12:47:51 +00:00
local _ = ( ( table.concat ( gears.filesystem . get_xdg_data_dirs ( ) , " : " ) or
" /usr/share:/usr/local/share " ) .. " : " .. os.getenv ( " HOME " ) .. " /.local/share " ) : gsub ( " [^:]* " , function ( path )
2022-07-23 19:10:02 +00:00
if gears.filesystem . dir_readable ( path .. " /applications " ) then
table.insert ( desktop_dirs , path .. " /applications " )
end
end )
2023-01-16 12:47:51 +00:00
-- Global xdg data struct
_G.xdg = {
directory_integrity = { } ,
directory_listings = { } ,
2022-07-23 19:10:02 +00:00
apps = { } ,
categories = {
Other = {
icon = " applications-other " ,
apps = { }
} ,
Wine = {
icon = " wine " ,
apps = { }
}
}
}
2023-01-16 12:47:51 +00:00
-- Load cached applications
local cache_file = io.open ( gears.filesystem . get_xdg_cache_home ( ) .. " .reno_xdg_cache.json " , " r " )
local cache
if cache_file then
cache = json.decode ( cache_file : read ( " *a " ) )
cache_file : close ( )
end
-- Add missing category entries as defined by awesome
for _ , v in pairs ( menu_gen.all_categories ) do
2022-07-23 19:10:02 +00:00
xdg.categories [ v.app_type ] = {
name = v.name ,
icon = v.icon_name ,
apps = { }
}
end
-- Asynchronous scanning process
2023-01-16 12:47:51 +00:00
for _ , v in pairs ( desktop_dirs ) do
xdg.directory_listings [ v ] = { }
awful.spawn . with_line_callback ( " find " .. tostring ( v ) .. " -maxdepth 1 -name *.desktop " , {
stdout = function ( line )
-- Assume the cache is valid for a listed file
if cache and cache.directory_listings [ v ] [ line ] then
xdg.directory_listings [ v ] [ line ] = true
xdg.apps [ line ] = cache.apps [ line ]
xdg.categories [ cache.apps [ line ] . category ] . apps [ line ] = cache.apps [ line ]
return
2022-07-23 19:10:02 +00:00
end
2023-01-16 12:47:51 +00:00
local data = menu_utils.parse_desktop_file ( line )
if data.NoDisplay then
return
end
local appdata = {
name = data.Name ,
category = " Other " ,
exec = data.Exec ,
icon = ( data.Icon and menu_utils.lookup_icon ( data.Icon ) ) ,
description = data.Comment
}
-- Match first available cateogry for sorting
for _ , vv in pairs ( data.Categories or { " Other " } ) do
if xdg.categories [ vv ] then
appdata.category = vv
break
end
-- Oh how do I love those Wine applications and their categories
if vv : match ( " ^Wine " ) then
appdata.category = " Wine "
break
end
end
-- Open terminal apps in the terminal (duh)
if data.Terminal then
appdata.exec = global.terminal .. " -e " .. appdata.exec
end
-- Just for you, Wine - special case because you're being a shit
if ( appdata.exec and appdata.exec : find ( " %W?wine " ) ) then
2022-07-23 19:10:02 +00:00
appdata.category = " Wine "
end
2023-01-16 12:47:51 +00:00
xdg.apps [ line ] = appdata
xdg.categories [ appdata.category ] . apps [ line ] = appdata
-- Add the file to the listing of cached ones
xdg.directory_listings [ v ] [ line ] = true
end ,
output_done = function ( )
-- Save directory listing hash
desktop_dirs_complete = desktop_dirs_complete + 1
-- Call a global signal
awesome.emit_signal ( " xdg::dir_finished " , v )
2022-07-23 19:10:02 +00:00
end
2023-01-16 12:47:51 +00:00
} )
end
local count = function ( t )
local n = 0
for _ , _ in pairs ( t ) do
n = n + 1
end
return n
2022-07-23 19:10:02 +00:00
end
2023-01-16 12:47:51 +00:00
2022-07-23 19:10:02 +00:00
awesome.connect_signal ( " xdg::dir_finished " , function ( dir )
2023-01-16 12:47:51 +00:00
-- We only send the all_finished signal when all directories finished processing
2022-07-23 19:10:02 +00:00
if desktop_dirs_complete == # desktop_dirs then
-- Clean up empty categories
for k , v in pairs ( xdg.categories ) do
2023-01-16 12:47:51 +00:00
if count ( v.apps ) == 0 then
2022-07-23 19:10:02 +00:00
xdg.categories [ k ] = nil
end
end
2023-01-16 12:47:51 +00:00
-- Save the cache if it doesn't exist yet
io.open ( gears.filesystem . get_xdg_cache_home ( ) .. " .reno_xdg_cache.json " , " w " ) : write ( json.encode ( xdg ) ) : close ( )
-- Finally, call the all_finished signal
awesome.emit_signal ( " xdg::all_finished " )
2022-07-23 19:10:02 +00:00
end
end )
2023-01-19 15:23:50 +00:00
-- Before exiting, save all xdg cache
awesome.connect_signal ( " exit " , function ( )
io.open ( gears.filesystem . get_xdg_cache_home ( ) .. " .reno_xdg_cache.json " , " w " ) : write ( json.encode ( xdg ) ) : close ( )
end )