LibGUI: Get executable file icons from PNGs stored in ELF sections.

If an ELF application contains sections called "serenity_icon_s"
or "serenity_icon_m" then parse these as PNG images and use them
for the 16x16 and 32x32 executable file icons respectively.

If the application is not an ELF binary, the sections do not
exist, the sections are not valid PNGs, or the file cannot be read
then the default application icon will be used.
This commit is contained in:
William Marlow 2020-12-20 18:24:14 +00:00 committed by Andreas Kling
parent 765936ebae
commit d16eabed06
Notes: sideshowbarker 2024-07-19 17:31:01 +09:00

View file

@ -25,15 +25,18 @@
*/
#include <AK/LexicalPath.h>
#include <AK/MappedFile.h>
#include <AK/String.h>
#include <LibCore/ConfigFile.h>
#include <LibCore/DirIterator.h>
#include <LibCore/File.h>
#include <LibCore/StandardPaths.h>
#include <LibELF/Image.h>
#include <LibGUI/FileIconProvider.h>
#include <LibGUI/Icon.h>
#include <LibGUI/Painter.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/PNGLoader.h>
#include <sys/stat.h>
namespace GUI {
@ -126,28 +129,71 @@ Icon FileIconProvider::icon_for_path(const String& path)
static Icon icon_for_executable(const String& path)
{
// FIXME: This is a huge hack and it would be much nicer if executables had icons embedded in them somehow.
static HashMap<String, Icon> app_icon_cache;
if (app_icon_cache.is_empty()) {
Core::DirIterator dt("/res/apps");
while (dt.has_next()) {
auto app_file = Core::ConfigFile::open(dt.next_full_path());
Icon app_icon;
auto icon16_path = app_file->read_entry("Icons", "16x16");
auto icon32_path = app_file->read_entry("Icons", "32x32");
if (auto icon16 = Gfx::Bitmap::load_from_file(icon16_path))
app_icon.set_bitmap_for_size(16, move(icon16));
if (auto icon32 = Gfx::Bitmap::load_from_file(icon32_path))
app_icon.set_bitmap_for_size(32, move(icon32));
app_icon_cache.set(app_file->read_entry("App", "Executable"), move(app_icon));
}
}
if (auto it = app_icon_cache.find(path); it != app_icon_cache.end())
return it->value;
return s_executable_icon;
// If the icon for an app isn't in the cache we attempt to load the file as an ELF image and extract
// the serenity_app_icon_* sections which should contain the icons as raw PNG data. In the future it would
// be better if the binary signalled the image format being used or we deduced it, e.g. using magic bytes.
auto mapped_file = make<MappedFile>(path);
if (!mapped_file->is_valid()) {
app_icon_cache.set(path, s_executable_icon);
return s_executable_icon;
}
if (mapped_file->size() < SELFMAG) {
app_icon_cache.set(path, s_executable_icon);
return s_executable_icon;
}
if (memcmp(mapped_file->data(), ELFMAG, SELFMAG) != 0) {
app_icon_cache.set(path, s_executable_icon);
return s_executable_icon;
}
auto image = ELF::Image((const u8*)mapped_file->data(), mapped_file->size());
if (!image.is_valid()) {
app_icon_cache.set(path, s_executable_icon);
return s_executable_icon;
}
// If any of the required sections are missing then use the defaults
Icon icon;
struct IconSection {
const char* section_name;
int image_size;
};
static const IconSection icon_sections[] = { { .section_name = "serenity_icon_s", .image_size = 16 }, { .section_name = "serenity_icon_m", .image_size = 32 } };
bool had_error = false;
for (const auto& icon_section : icon_sections) {
auto section = image.lookup_section(icon_section.section_name);
RefPtr<Gfx::Bitmap> bitmap;
if (section.is_undefined()) {
bitmap = s_executable_icon.bitmap_for_size(icon_section.image_size)->clone();
} else {
bitmap = Gfx::load_png_from_memory(reinterpret_cast<const u8*>(section.raw_data()), section.size());
}
if (!bitmap) {
dbgln("Failed to find embedded icon and failed to clone default icon for application {} at icon size {}", path, icon_section.image_size);
had_error = true;
continue;
}
icon.set_bitmap_for_size(icon_section.image_size, std::move(bitmap));
}
if (had_error) {
app_icon_cache.set(path, s_executable_icon);
return s_executable_icon;
}
app_icon_cache.set(path, icon);
return icon;
}
Icon FileIconProvider::icon_for_path(const String& path, mode_t mode)