2N/A/* icon_manager.c - gfxmenu icon manager. */
2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (C) 2008,2009 Free Software Foundation, Inc.
2N/A *
2N/A * GRUB is free software: you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation, either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#include <grub/types.h>
2N/A#include <grub/misc.h>
2N/A#include <grub/mm.h>
2N/A#include <grub/err.h>
2N/A#include <grub/gui_string_util.h>
2N/A#include <grub/bitmap.h>
2N/A#include <grub/bitmap_scale.h>
2N/A#include <grub/menu.h>
2N/A#include <grub/icon_manager.h>
2N/A#include <grub/env.h>
2N/A
2N/A/* Currently hard coded to '.png' extension. */
2N/Astatic const char icon_extension[] = ".png";
2N/A
2N/Atypedef struct icon_entry
2N/A{
2N/A char *class_name;
2N/A struct grub_video_bitmap *bitmap;
2N/A struct icon_entry *next;
2N/A} *icon_entry_t;
2N/A
2N/Astruct grub_gfxmenu_icon_manager
2N/A{
2N/A char *theme_path;
2N/A int icon_width;
2N/A int icon_height;
2N/A
2N/A /* Icon cache: linked list w/ dummy head node. */
2N/A struct icon_entry cache;
2N/A};
2N/A
2N/A
2N/A/* Create a new icon manager and return a point to it. */
2N/Agrub_gfxmenu_icon_manager_t
2N/Agrub_gfxmenu_icon_manager_new (void)
2N/A{
2N/A grub_gfxmenu_icon_manager_t mgr;
2N/A mgr = grub_malloc (sizeof (*mgr));
2N/A if (! mgr)
2N/A return 0;
2N/A
2N/A mgr->theme_path = 0;
2N/A mgr->icon_width = 0;
2N/A mgr->icon_height = 0;
2N/A
2N/A /* Initialize the dummy head node. */
2N/A mgr->cache.class_name = 0;
2N/A mgr->cache.bitmap = 0;
2N/A mgr->cache.next = 0;
2N/A
2N/A return mgr;
2N/A}
2N/A
2N/A/* Destroy the icon manager MGR, freeing all resources used by it.
2N/A
2N/ANote: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon()
2N/Aare destroyed and must not be used by the caller after this function
2N/Ais called. */
2N/Avoid
2N/Agrub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr)
2N/A{
2N/A grub_gfxmenu_icon_manager_clear_cache (mgr);
2N/A grub_free (mgr->theme_path);
2N/A grub_free (mgr);
2N/A}
2N/A
2N/A/* Clear the icon cache. */
2N/Avoid
2N/Agrub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr)
2N/A{
2N/A icon_entry_t cur;
2N/A icon_entry_t next;
2N/A for (cur = mgr->cache.next; cur; cur = next)
2N/A {
2N/A next = cur->next;
2N/A grub_free (cur->class_name);
2N/A grub_video_bitmap_destroy (cur->bitmap);
2N/A grub_free (cur);
2N/A }
2N/A mgr->cache.next = 0;
2N/A}
2N/A
2N/A/* Set the theme path. If the theme path is changed, the icon cache
2N/A is cleared. */
2N/Avoid
2N/Agrub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
2N/A const char *path)
2N/A{
2N/A /* Clear the cache if the theme path has changed. */
2N/A if (((mgr->theme_path == 0) != (path == 0))
2N/A || (grub_strcmp (mgr->theme_path, path) != 0))
2N/A grub_gfxmenu_icon_manager_clear_cache (mgr);
2N/A
2N/A grub_free (mgr->theme_path);
2N/A mgr->theme_path = path ? grub_strdup (path) : 0;
2N/A}
2N/A
2N/A/* Set the icon size. When icons are requested from the icon manager,
2N/A they are scaled to this size before being returned. If the size is
2N/A changed, the icon cache is cleared. */
2N/Avoid
2N/Agrub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr,
2N/A int width, int height)
2N/A{
2N/A /* If the width or height is changed, we must clear the cache, since the
2N/A scaled bitmaps are stored in the cache. */
2N/A if (width != mgr->icon_width || height != mgr->icon_height)
2N/A grub_gfxmenu_icon_manager_clear_cache (mgr);
2N/A
2N/A mgr->icon_width = width;
2N/A mgr->icon_height = height;
2N/A}
2N/A
2N/A/* Try to load an icon for the specified CLASS_NAME in the directory DIR.
2N/A Returns 0 if the icon could not be loaded, or returns a pointer to a new
2N/A bitmap if it was successful. */
2N/Astatic struct grub_video_bitmap *
2N/Atry_loading_icon (grub_gfxmenu_icon_manager_t mgr,
2N/A const char *dir, const char *class_name)
2N/A{
2N/A char *path;
2N/A int l;
2N/A
2N/A path = grub_malloc (grub_strlen (dir) + grub_strlen (class_name)
2N/A + grub_strlen (icon_extension) + 3);
2N/A if (! path)
2N/A return 0;
2N/A
2N/A grub_strcpy (path, dir);
2N/A l = grub_strlen (path);
2N/A if (path[l-1] != '/')
2N/A {
2N/A path[l] = '/';
2N/A path[l+1] = 0;
2N/A }
2N/A grub_strcat (path, class_name);
2N/A grub_strcat (path, icon_extension);
2N/A
2N/A struct grub_video_bitmap *raw_bitmap;
2N/A grub_video_bitmap_load (&raw_bitmap, path);
2N/A grub_free (path);
2N/A grub_errno = GRUB_ERR_NONE; /* Critical to clear the error!! */
2N/A if (! raw_bitmap)
2N/A return 0;
2N/A
2N/A struct grub_video_bitmap *scaled_bitmap;
2N/A grub_video_bitmap_create_scaled (&scaled_bitmap,
2N/A mgr->icon_width, mgr->icon_height,
2N/A raw_bitmap,
2N/A GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
2N/A grub_video_bitmap_destroy (raw_bitmap);
2N/A if (! scaled_bitmap)
2N/A {
2N/A grub_error_push ();
2N/A grub_error (grub_errno, "failed to scale icon");
2N/A return 0;
2N/A }
2N/A
2N/A return scaled_bitmap;
2N/A}
2N/A
2N/A/* Get the icon for the specified class CLASS_NAME. If an icon for
2N/A CLASS_NAME already exists in the cache, then a reference to the cached
2N/A bitmap is returned. If it is not cached, then it is loaded and cached.
2N/A If no icon could be could for CLASS_NAME, then 0 is returned. */
2N/Astatic struct grub_video_bitmap *
2N/Aget_icon_by_class (grub_gfxmenu_icon_manager_t mgr, const char *class_name)
2N/A{
2N/A /* First check the icon cache. */
2N/A icon_entry_t entry;
2N/A for (entry = mgr->cache.next; entry; entry = entry->next)
2N/A {
2N/A if (grub_strcmp (entry->class_name, class_name) == 0)
2N/A return entry->bitmap;
2N/A }
2N/A
2N/A if (! mgr->theme_path)
2N/A return 0;
2N/A
2N/A /* Otherwise, we search for an icon to load. */
2N/A char *theme_dir = grub_get_dirname (mgr->theme_path);
2N/A char *icons_dir;
2N/A struct grub_video_bitmap *icon;
2N/A icon = 0;
2N/A /* First try the theme's own icons, from "grub/themes/NAME/icons/" */
2N/A icons_dir = grub_resolve_relative_path (theme_dir, "icons/");
2N/A if (icons_dir)
2N/A {
2N/A icon = try_loading_icon (mgr, icons_dir, class_name);
2N/A grub_free (icons_dir);
2N/A }
2N/A
2N/A grub_free (theme_dir);
2N/A if (! icon)
2N/A {
2N/A const char *icondir;
2N/A
2N/A icondir = grub_env_get ("icondir");
2N/A if (icondir)
2N/A icon = try_loading_icon (mgr, icondir, class_name);
2N/A }
2N/A
2N/A /* No icon was found. */
2N/A /* This should probably be noted in the cache, so that a search is not
2N/A performed each time an icon for CLASS_NAME is requested. */
2N/A if (! icon)
2N/A return 0;
2N/A
2N/A /* Insert a new cache entry for this icon. */
2N/A entry = grub_malloc (sizeof (*entry));
2N/A if (! entry)
2N/A {
2N/A grub_video_bitmap_destroy (icon);
2N/A return 0;
2N/A }
2N/A entry->class_name = grub_strdup (class_name);
2N/A entry->bitmap = icon;
2N/A entry->next = mgr->cache.next;
2N/A mgr->cache.next = entry; /* Link it into the cache. */
2N/A return entry->bitmap;
2N/A}
2N/A
2N/A/* Get the best available icon for ENTRY. Beginning with the first class
2N/A listed in the menu entry and proceeding forward, an icon for each class
2N/A is searched for. The first icon found is returned. The returned icon
2N/A is scaled to the size specified by
2N/A grub_gfxmenu_icon_manager_set_icon_size().
2N/A
2N/A Note: Bitmaps returned by this function are destroyed when the
2N/A icon manager is destroyed.
2N/A */
2N/Astruct grub_video_bitmap *
2N/Agrub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
2N/A grub_menu_entry_t entry)
2N/A{
2N/A struct grub_menu_entry_class *c;
2N/A struct grub_video_bitmap *icon;
2N/A
2N/A /* Try each class in succession. */
2N/A icon = 0;
2N/A for (c = entry->classes; c && ! icon; c = c->next)
2N/A icon = get_icon_by_class (mgr, c->name);
2N/A return icon;
2N/A}