From d16eabed0679c014dd32827737dc9abbe4b219d0 Mon Sep 17 00:00:00 2001 From: William Marlow Date: Sun, 20 Dec 2020 18:24:14 +0000 Subject: [PATCH] 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. --- Libraries/LibGUI/FileIconProvider.cpp | 80 +++++++++++++++++++++------ 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/Libraries/LibGUI/FileIconProvider.cpp b/Libraries/LibGUI/FileIconProvider.cpp index 1048e1a631a..3994167c96d 100644 --- a/Libraries/LibGUI/FileIconProvider.cpp +++ b/Libraries/LibGUI/FileIconProvider.cpp @@ -25,15 +25,18 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include 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 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(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 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(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)