All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] 5/5 Graphical menu (gfxmenu)
@ 2009-01-31 20:59 Colin D Bennett
  2009-01-31 21:39 ` Vesa Jääskeläinen
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Colin D Bennett @ 2009-01-31 20:59 UTC (permalink / raw)
  To: The development of GRUB 2


[-- Attachment #1.1: Type: text/plain, Size: 1414 bytes --]

This long-awaited patch adds graphical menu support to GRUB 2.  It is
largely the result of my work during the Google Summer of Code 2008.

The graphical menu system supports image-based themes, of which I have
created several sample themes[1].

There are still a few important things that need to be done on gfxmenu
before it is completely ready for end-users (off the top of my head):

- Need a good way to switch themes at runtime.  I want to add a popup
  menu that lists the available themes and lets the user choose one.
  (Currently the keys 1, 2, 3, and 4 are hardcoded to switch between my
  sample themes...)

- gfxmenu interacts badly with gfxterm.  Running commands from the
  terminal withing gfxmenu that switch the video mode
  ('videotest basic', for instance) cause a crash when the command
  returns.  I think this has something to do with the interaction of
  video mode-switching and the GRUB output terminal.

The default theme is set in grub.cfg like so:

  set theme="/boot/grub/themes/proto/theme.txt"

Also, pressing 't' currently switches between graphical and text mode
menus, but by adding the following menu entries, it can be done from
the menu:

  menuentry "Switch to text menu" {
    set menuviewer="terminal"
  }
  menuentry "Switch to graphical menu" {
    set menuviewer="gfxmenu"
  }

Regards,
Colin

[1] <http://grub.gibibit.com/Themes>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: 05_gfxmenu.patch --]
[-- Type: text/x-patch, Size: 229841 bytes --]

=== modified file 'conf/common.rmk'
--- conf/common.rmk	2009-01-31 20:26:35 +0000
+++ conf/common.rmk	2009-01-31 20:43:30 +0000
@@ -329,6 +329,7 @@
 	cmp.mod cat.mod help.mod search.mod					\
 	loopback.mod fs_uuid.mod configfile.mod echo.mod	\
 	terminfo.mod test.mod blocklist.mod hexdump.mod		\
+	gfxmenu.mod                                             \
 	read.mod sleep.mod loadenv.mod crc.mod
 
 # For hello.mod.
@@ -336,6 +337,27 @@
 hello_mod_CFLAGS = $(COMMON_CFLAGS)
 hello_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For gfxmenu.mod.
+gfxmenu_mod_SOURCES = \
+	gfxmenu/gfxmenu.c \
+	gfxmenu/model.c \
+	gfxmenu/view.c \
+	gfxmenu/icon_manager.c \
+	gfxmenu/theme_loader.c \
+	gfxmenu/widget-box.c \
+	gfxmenu/gui_canvas.c \
+	gfxmenu/gui_circular_progress.c \
+	gfxmenu/gui_box.c \
+	gfxmenu/gui_label.c \
+	gfxmenu/gui_list.c \
+	gfxmenu/gui_image.c \
+	gfxmenu/gui_progress_bar.c \
+	gfxmenu/gui_util.c \
+	gfxmenu/gui_string_util.c \
+	gfxmenu/named_colors.c
+gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS)
+gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # For boot.mod.
 boot_mod_SOURCES = commands/boot.c
 boot_mod_CFLAGS = $(COMMON_CFLAGS)
@@ -436,7 +458,7 @@
 	png.mod	font.mod gfxterm.mod
 
 # For video.mod.
-video_mod_SOURCES = video/video.c
+video_mod_SOURCES = video/video.c video/setmode.c
 video_mod_CFLAGS = $(COMMON_CFLAGS)
 video_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
@@ -476,7 +498,7 @@
 gfxterm_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # Misc.
-pkglib_MODULES += gzio.mod bufio.mod elf.mod
+pkglib_MODULES += gzio.mod bufio.mod elf.mod trig.mod
 
 # For elf.mod.
 elf_mod_SOURCES = kern/elf.c
@@ -492,3 +514,8 @@
 bufio_mod_SOURCES = io/bufio.c
 bufio_mod_CFLAGS = $(COMMON_CFLAGS)
 bufio_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
+# For trig.mod.
+trig_mod_SOURCES = lib/trig.c
+trig_mod_CFLAGS = $(COMMON_CFLAGS)
+trig_mod_LDFLAGS = $(COMMON_LDFLAGS)

=== modified file 'conf/sparc64-ieee1275.rmk'
--- conf/sparc64-ieee1275.rmk	2009-01-31 16:50:07 +0000
+++ conf/sparc64-ieee1275.rmk	2009-01-31 20:43:30 +0000
@@ -83,7 +83,7 @@
 # Modules.
 #_linux.mod linux.mod
 pkglib_MODULES = fat.mod ufs.mod ext2.mod minix.mod \
-	hfs.mod jfs.mod normal.mod hello.mod font.mod ls.mod \
+	hfs.mod jfs.mod normal.mod hello.mod ls.mod \
 	boot.mod cmp.mod cat.mod terminal.mod fshelp.mod amiga.mod apple.mod \
 	pc.mod suspend.mod loopback.mod help.mod reboot.mod halt.mod sun.mod \
 	configfile.mod search.mod gzio.mod xfs.mod \
@@ -204,11 +204,6 @@
 cat_mod_CFLAGS = $(COMMON_CFLAGS)
 cat_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
-# For font.mod.
-font_mod_SOURCES = font/manager.c
-font_mod_CFLAGS = $(COMMON_CFLAGS)
-font_mod_LDFLAGS = $(COMMON_LDFLAGS)
-
 # For amiga.mod
 amiga_mod_SOURCES = partmap/amiga.c
 amiga_mod_CFLAGS = $(COMMON_CFLAGS)

=== added file 'docs/gfxmenu-theme-example.txt'
--- docs/gfxmenu-theme-example.txt	1970-01-01 00:00:00 +0000
+++ docs/gfxmenu-theme-example.txt	2009-01-31 20:43:30 +0000
@@ -0,0 +1,128 @@
+# GRUB gfxmenu theme "winter".
+# Uses background image from:
+# http://www.cyberpunkcafe.com/e107_plugins/autogallery/autogallery.php?show=1.Open%20Source%20Wallpaper
+# "without-leaves.png" was called "Without Leafs in Winter.png"
+
+lua-script: "winter.lua"
+title-text: ""
+title-font: "Helvetica Bold 18"
+status-font: "Helvetica 8"
+terminal-font: "Fixed 9"
+title-color: "40, 40, 40"
+status-color: "#FFF"
+status-bg-color: "0, 166, 183, 128"
+desktop-image: "without-leaves.png"
+desktop-color: "0, 154, 183"
+terminal-box: "terminal_*.png"
+
++ boot_menu {
+   position = (120, 60)
+   preferred_size = (400, -1)
+   item_font = "Helvetica Bold 14"
+   selected_item_font = "Helvetica Bold 14"
+   item_color = "0, 0, 0"
+   selected_item_color = "203, 251, 255"
+   menu_pixmap_style = "menu_*.png"
+   selected_item_pixmap_style = "select_*.png"
+   icon_width = 44
+   icon_height = 44
+   item_height = 32
+   item_padding = 0
+   item_icon_space = 3
+   item_spacing = 11
+}
+
+# You can add text at arbitrary locations on the screen.
+# The specification within the "+label {...}" block is free-form,
+# so you can use as much or as little white space as you like.
+
++ label {
+   position = (170, 50)
+   font = "smoothansi 13"
+   color = "0,0,128"
+   text = "This is the Winter theme ... brought to you by GRUB!"
+}
+
+# Show the text alignment supported by labels.
++ vbox {
+   position = (220, 347)
+   preferred_size = (200, -1)     # A preferred size of -1 means automatic.
+   + label { text="Text alignment demo" align="center" font="aqui 11" }
+   + label { text="Left" align="left" font="cure 11" }
+   + label { text="Center" align="center" font="cure 11" }
+   + label { text="Right" align="right" font="cure 11" }
+}
+
++ vbox {
+   position = (580, 10)
+   + label { text="GNU" font="gelly 11" color="0, 0, 0" }
+   + label { text="GRUB" font="aqui 11" color="0, 0, 0" }
+   + label { text="boot loader" font="cure 11" color="0, 0, 0" }
+}
+
++ hbox {
+   position = (80, 10)
+   + label { text="GNU" font="gelly 11" color="0, 0, 0" }
+   + label { text="GRUB" font="aqui 11" color="0, 0, 0" }
+   + label { text="boot loader" font="cure 11" color="0, 0, 0" }
+}
+
+# Demonstration of a compound layout: boxes within boxes.
++ hbox
+{
+   position = (480, 3)
+
+   + vbox
+   {
+      # Note: We can't just use 'size' to set the image's size,
+      #       since the vbox will resize the component according to its
+      #       preferred size, which for images is the native image size.
+
+      + image { file="/boot/grub/themes/icons/ubuntu.png"
+                preferred_size = (20, 20) }
+      + image { file="/boot/grub/themes/icons/gentoo.png"
+                preferred_size = (20, 20) }
+   }
+
+   + vbox
+   {
+      + label { text="GRand" font="cure 11" color=#99F }
+      + label { text="Unified" font="cure 11" color=#BBF }
+      + label { text="Bootloader" font="cure 11" color=#DDF }
+   }
+}
+
+# By defining a 'progress_bar' type component with an ID of '__timeout__',
+# the progress bar will be used to display the time remaining before an
+# the default entry is automatically booted.
++ progress_bar
+{
+   id = "__timeout__"
+   position = (80, 393)
+   preferred_size = (500, 24)
+   font = "cure 11"
+   text_color = #000
+   fg_color = #CCF
+   bg_color = #66B
+   border_color = #006
+   show_text = false
+}
+
+# Although the progress_bar component is normally used to indicate the
+# time remaining, it's also possible to create other components with an ID
+# of '__timeout__'.  All components with and ID of 'timeout_bar' will have
+# the following properties set based on the timeout value:
+#   text, value, start, end, visible.
+# In this case, we have set 'show_text=false' on the progress bar, and use
+# the following label's 'text' property to display the message.
++ label
+{
+   id = "__timeout__"
+   position = (80, 420)
+   preferred_size = (500, 24)
+   font = "lime 11"
+   color = #117
+   align = "center"
+}
+
+

=== added file 'gentrigtables.py'
--- gentrigtables.py	1970-01-01 00:00:00 +0000
+++ gentrigtables.py	2009-01-31 20:43:30 +0000
@@ -0,0 +1,58 @@
+#!/usr/bin/python
+# Script to generate trigonometric function tables.
+#
+#  GRUB  --  GRand Unified Bootloader
+#  Copyright (C) 2008  Free Software Foundation, Inc.
+#
+#  GRUB 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.
+#
+#  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+from math import *
+from sys import stdout
+
+def write(x):
+    stdout.write(x)
+
+def writeTable(arr, name):
+    indent = ' ' * 4
+    write("short ")
+    write(name)
+    write("[] =\n{\n")
+    write(indent)
+    for i in range(len(arr)):
+        if i != 0:
+            write(",")
+            if i % 10 == 0:
+                write("\n")
+                write(indent)
+        write("%d" % arr[i])
+    write("\n};\n")
+
+def main():
+    sintab = []
+    costab = []
+    for i in range(256):
+        # Convert to an angle in 1/256 of a circle.
+        x = i * 2 * pi / 256
+        sintab.append(int(round(sin(x) * 16384)))
+        costab.append(int(round(cos(x) * 16384)))
+
+    write("#define TRIG_ANGLE_MAX 256\n")
+    write("#define TRIG_FRACTION_SCALE 16384\n")
+    writeTable(sintab, "sintab")
+    writeTable(costab, "costab")
+
+if __name__ == "__main__":
+    main()
+
+# vim:ai et sw=4 ts=4

=== added directory 'gfxmenu'
=== added file 'gfxmenu/gfxmenu.c'
--- gfxmenu/gfxmenu.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gfxmenu.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,231 @@
+/* gfxmenu.c - Graphical menu interface controller. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/video.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/term.h>
+#include <grub/gfxwidgets.h>
+#include <grub/menu.h>
+#include <grub/menu_viewer.h>
+#include <grub/gfxmenu_model.h>
+#include <grub/gfxmenu_view.h>
+
+static void switch_to_text_menu (void)
+{
+  grub_env_set ("menuviewer", "terminal");
+}
+
+static void
+process_key_press (int c,
+                   grub_gfxmenu_model_t model,
+                   grub_gfxmenu_view_t view,
+                   int nested,
+                   int *should_exit)
+{
+  /* When a key is pressed, stop the timeout.  */
+  grub_gfxmenu_model_clear_timeout (model);
+
+  if (c == 'j' || c == GRUB_TERM_DOWN)
+    {
+      int i = grub_gfxmenu_model_get_selected_index (model);
+      int num_items = grub_gfxmenu_model_get_num_entries (model);
+      if (i < num_items - 1)
+        {
+          i++;
+          grub_gfxmenu_model_set_selected_index (model, i);
+        }
+    }
+  else if (c == 'k' || c == GRUB_TERM_UP)
+    {
+      int i = grub_gfxmenu_model_get_selected_index (model);
+      if (i > 0)
+        {
+          i--;
+          grub_gfxmenu_model_set_selected_index (model, i);
+        }
+    }
+  else if (c == '\r' || c == '\n' || c == GRUB_TERM_RIGHT)
+    {
+      int selected = grub_gfxmenu_model_get_selected_index (model);
+      int num_entries = grub_gfxmenu_model_get_num_entries (model);
+      if (selected >= 0 && selected < num_entries)
+        {
+          grub_menu_entry_t entry =
+            grub_gfxmenu_model_get_entry (model, selected);
+          grub_gfxmenu_view_execute_entry (view, entry);
+        }
+    }
+  else if (c == 'c')
+    {
+      grub_gfxmenu_view_run_terminal (view);
+      if (grub_errno != GRUB_ERR_NONE)
+        *should_exit = 1;
+    }
+  else if (c == 't')
+    {
+      /* The write hook for 'menuviewer' will cause
+       * grub_menu_viewer_should_return to return nonzero. */
+      switch_to_text_menu ();
+      *should_exit = 1;
+    }
+  else if (c == '1')
+    {
+      grub_gfxmenu_view_load_theme (view,
+                                    "/boot/grub/themes/proto/theme.txt");
+    }
+  else if (c == '2')
+    {
+      grub_gfxmenu_view_load_theme (view,
+                                    "/boot/grub/themes/winter/theme.txt");
+    }
+  else if (c == '3')
+    {
+      grub_gfxmenu_view_load_theme (view,
+                                    "/boot/grub/themes/ubuntu1/theme.txt");
+    }
+  else if (c == '4')
+    {
+      grub_gfxmenu_view_load_theme (view,
+                                    "/boot/grub/themes/ubuntu2/theme.txt");
+    }
+  else if (nested && c == GRUB_TERM_ESC)
+    {
+      *should_exit = 1;
+    }
+}
+
+static void
+handle_key_events (grub_gfxmenu_model_t model,
+                   grub_gfxmenu_view_t view,
+                   int nested,
+                   int *should_exit)
+{
+  while (!*should_exit && grub_checkkey () != -1)
+    {
+      int key = grub_getkey ();
+      int c = GRUB_TERM_ASCII_CHAR (key);
+      process_key_press (c, model, view, nested, should_exit);
+    }
+}
+
+static grub_err_t
+show_menu (grub_menu_t menu, int nested)
+{
+  grub_gfxmenu_model_t model;
+
+  model = grub_gfxmenu_model_new (menu);
+  if (! model)
+    {
+      grub_print_error ();
+      grub_printf ("Initializing menu data for graphical menu failed;\n"
+                   "falling back to terminal based menu.\n");
+      grub_wait_after_message ();
+      switch_to_text_menu ();
+      return grub_errno;
+    }
+
+  grub_gfxmenu_view_t view;
+
+  /* Create the view.  */
+  const char *theme_path = grub_env_get ("theme");
+  if (! theme_path)
+    theme_path = "/boot/grub/themes/proto/theme.txt";
+
+  view = grub_gfxmenu_view_new (theme_path, model);
+  if (! view)
+    {
+      grub_print_error ();
+      grub_printf ("Starting graphical menu failed;\n"
+                   "falling back to terminal based menu.\n");
+      grub_wait_after_message ();
+      grub_gfxmenu_model_destroy (model);
+      switch_to_text_menu ();
+      return grub_errno;
+    }
+
+  /* Initially select the default menu entry.  */
+  int default_index = grub_menu_get_default_entry_index (menu);
+  grub_gfxmenu_model_set_selected_index (model, default_index);
+
+  /* Start the timer to execute the default entry.  */
+  grub_gfxmenu_model_set_timeout (model);
+
+  /* Main event loop.  */
+  int exit_requested = 0;
+  while (!exit_requested && !grub_menu_viewer_should_return ())
+    {
+      if (grub_gfxmenu_model_timeout_expired (model))
+        {
+          grub_gfxmenu_model_clear_timeout (model);
+          int i = grub_gfxmenu_model_get_selected_index (model);
+          grub_menu_entry_t e = grub_gfxmenu_model_get_entry (model, i);
+          grub_gfxmenu_view_execute_with_fallback (view, e);
+          continue;
+        }
+
+      grub_gfxmenu_view_draw (view);
+      grub_video_swap_buffers ();
+      handle_key_events (model, view, nested, &exit_requested);
+    }
+
+  grub_gfxmenu_view_destroy (view);
+  grub_gfxmenu_model_destroy (model);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_cmd_gfxmenu (struct grub_arg_list *state __attribute__ ((unused)),
+                  int argc __attribute__ ((unused)),
+                  char **args __attribute__ ((unused)))
+{
+  grub_menu_t menu = grub_env_get_data_slot ("menu");
+  if (!menu)
+    return grub_error (GRUB_ERR_MENU, "No menu context");
+
+  return show_menu (menu, 1);
+}
+
+static struct grub_menu_viewer menu_viewer =
+{
+  .name = "gfxmenu",
+  .show_menu = show_menu
+};
+
+GRUB_MOD_INIT (gfxmenu)
+{
+  (void) mod;                   /* To stop warning. */
+  grub_menu_viewer_register (&menu_viewer);
+  grub_register_command ("gfxmenu",
+                         grub_cmd_gfxmenu, GRUB_COMMAND_FLAG_BOTH,
+                         "gfxmenu", "Show graphical menu interface", 0);
+}
+
+GRUB_MOD_FINI (gfxmenu)
+{
+  grub_unregister_command ("gfxmenu");
+}

=== added file 'gfxmenu/gui_box.c'
--- gfxmenu/gui_box.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_box.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,367 @@
+/* gui_box.c - GUI container that stack components. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+struct component_node
+{
+  grub_gui_component_t component;
+  struct component_node *next;
+  struct component_node *prev;
+};
+
+typedef struct grub_gui_box *grub_gui_box_t;
+
+typedef void (*layout_func_t) (grub_gui_box_t self, int modify_layout,
+                               int *width, int *height);
+
+struct grub_gui_box
+{
+  struct grub_gui_container_ops *container;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+
+  /* Doubly linked list of components with dummy head & tail nodes.  */
+  struct component_node chead;
+  struct component_node ctail;
+
+  /* The layout function: differs for vertical and horizontal boxes.  */
+  layout_func_t layout_func;
+};
+
+static void
+box_destroy (void *vself)
+{
+  grub_gui_box_t self = vself;
+  struct component_node *cur;
+  struct component_node *next;
+  for (cur = self->chead.next; cur != &self->ctail; cur = next)
+    {
+      /* Copy the 'next' pointer, since we need it for the next iteration,
+         and we're going to free the memory it is stored in.  */
+      next = cur->next;
+      /* Destroy the child component.  */
+      cur->component->ops->destroy (cur->component);
+      /* Free the linked list node.  */
+      grub_free (cur);
+    }
+  grub_free (self);
+}
+
+static const char *
+box_get_id (void *vself)
+{
+  grub_gui_box_t self = vself;
+  return self->id;
+}
+
+static int
+box_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return (grub_strcmp (type, "component") == 0
+          || grub_strcmp (type, "container") == 0);
+}
+
+static void
+layout_horizontally (grub_gui_box_t self, int modify_layout,
+                     int *width, int *height)
+{
+  /* Start at the left (chead) and set the x coordinates as we go right.  */
+  /* All components have their width set to the box's width.  */
+
+  struct component_node *cur;
+  int x = 0;
+  if (height)
+    *height = 0;
+  for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+    {
+      grub_gui_component_t c = cur->component;
+      grub_video_rect_t r;
+
+      c->ops->get_preferred_size (c, &r.width, &r.height);
+
+      /* Check and possibly update the maximum width, if non-null.  */
+      if (height && r.height > *height)
+        *height = r.height;
+
+      /* Set the component's bounds, if the flag is set.  */
+      if (modify_layout)
+        {
+          r.x = x;
+          r.y = 0;
+          /* Width comes from the component's preferred size.  */
+          r.height = self->bounds.height;
+          c->ops->set_bounds (c, &r);
+        }
+
+      x += r.width;
+    }
+
+  /* Return the sum of the children's preferred widths.  */
+  if (width)
+    *width = x;
+}
+
+static void
+layout_vertically (grub_gui_box_t self, int modify_layout,
+                   int *width, int *height)
+{
+  /* Start at the top (chead) and set the y coordinates as we go down.  */
+  /* All components have their width set to the vbox's width.  */
+
+  struct component_node *cur;
+  int y = 0;
+  if (width)
+    *width = 0;
+  for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+    {
+      grub_gui_component_t c = cur->component;
+      grub_video_rect_t r;
+
+      c->ops->get_preferred_size (c, &r.width, &r.height);
+
+      /* Check and possibly update the maximum width, if non-null.  */
+      if (width && r.width > *width)
+        *width = r.width;
+
+      /* Set the component's bounds, if the flag is set.  */
+      if (modify_layout)
+        {
+          r.x = 0;
+          r.y = y;
+          r.width = self->bounds.width;
+          /* Height comes from the component's preferred size.  */
+          c->ops->set_bounds (c, &r);
+        }
+
+      y += r.height;
+    }
+
+  /* Return the sum of the children's preferred heights.  */
+  if (height)
+    *height = y;
+}
+
+static void
+box_paint (void *vself)
+{
+  grub_gui_box_t self = vself;
+  struct component_node *cur;
+  grub_video_rect_t vpsave;
+
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+  for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+    {
+      grub_gui_component_t comp = cur->component;
+      comp->ops->paint (comp);
+    }
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+box_set_parent (void *vself, grub_gui_container_t parent)
+{
+  grub_gui_box_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+box_get_parent (void *vself)
+{
+  grub_gui_box_t self = vself;
+  return self->parent;
+}
+
+static void
+box_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  grub_gui_box_t self = vself;
+  self->bounds = *bounds;
+  self->layout_func (self, 1, 0, 0);   /* Relayout the children.  */
+}
+
+static void
+box_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  grub_gui_box_t self = vself;
+  *bounds = self->bounds;
+}
+
+/* The box's preferred size is based on the preferred sizes
+   of its children.  */
+static void
+box_get_preferred_size (void *vself, int *width, int *height)
+{
+  grub_gui_box_t self = vself;
+  self->layout_func (self, 0, width, height);   /* Just calculate the size.  */
+
+  /* Allow preferred dimensions to override the computed dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+box_set_property (void *vself, const char *name, const char *value)
+{
+  grub_gui_box_t self = vself;
+  if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+}
+
+static void
+box_add (void *vself, grub_gui_component_t comp)
+{
+  grub_gui_box_t self = vself;
+  struct component_node *node;
+  node = grub_malloc (sizeof (*node));
+  if (! node)
+    return;   /* Note: probably should handle the error.  */
+  node->component = comp;
+  /* Insert the node before the tail.  */
+  node->prev = self->ctail.prev;
+  node->prev->next = node;
+  node->next = &self->ctail;
+  node->next->prev = node;
+
+  comp->ops->set_parent (comp, (grub_gui_container_t) self);
+  self->layout_func (self, 1, 0, 0);   /* Relayout the children.  */
+}
+
+static void
+box_remove (void *vself, grub_gui_component_t comp)
+{
+  grub_gui_box_t self = vself;
+  struct component_node *cur;
+  for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+    {
+      if (cur->component == comp)
+        {
+          /* Unlink 'cur' from the list.  */
+          cur->prev->next = cur->next;
+          cur->next->prev = cur->prev;
+          /* Free the node's memory (but don't destroy the component).  */
+          grub_free (cur);
+          /* Must not loop again, since 'cur' would be dereferenced!  */
+          return;
+        }
+    }
+}
+
+static void
+box_iterate_children (void *vself,
+                      grub_gui_component_callback cb, void *userdata)
+{
+  grub_gui_box_t self = vself;
+  struct component_node *cur;
+  for (cur = self->chead.next; cur != &self->ctail; cur = cur->next)
+    cb (cur->component, userdata);
+}
+
+static struct grub_gui_container_ops box_ops =
+{
+  .component =
+    {
+      .destroy = box_destroy,
+      .get_id = box_get_id,
+      .is_instance = box_is_instance,
+      .paint = box_paint,
+      .set_parent = box_set_parent,
+      .get_parent = box_get_parent,
+      .set_bounds = box_set_bounds,
+      .get_bounds = box_get_bounds,
+      .get_preferred_size = box_get_preferred_size,
+      .set_property = box_set_property
+    },
+  .add = box_add,
+  .remove = box_remove,
+  .iterate_children = box_iterate_children
+};
+
+/* Box constructor.  Specify the appropriate layout function to create
+   a horizontal or vertical stacking box.  */
+static grub_gui_box_t
+box_new (layout_func_t layout_func)
+{
+  grub_gui_box_t box;
+  box = grub_malloc (sizeof (*box));
+  if (! box)
+    return 0;
+  box->container = &box_ops;
+  box->parent = 0;
+  box->bounds.x = 0;
+  box->bounds.y = 0;
+  box->bounds.width = 0;
+  box->bounds.height = 0;
+  box->id = 0;
+  box->preferred_width = -1;
+  box->preferred_height = -1;
+  box->chead.component = 0;
+  box->chead.prev = 0;
+  box->chead.next = &box->ctail;
+  box->ctail.component = 0;
+  box->ctail.prev = &box->chead;
+  box->ctail.next = 0;
+  box->layout_func = layout_func;
+  return box;
+}
+
+/* Create a new container that stacks its child components horizontally,
+   from left to right.  Each child get a width corresponding to its
+   preferred width.  The height of each child is set the maximum of the
+   preferred heights of all children.  */
+grub_gui_container_t
+grub_gui_hbox_new (void)
+{
+  return (grub_gui_container_t) box_new (layout_horizontally);
+}
+
+/* Create a new container that stacks its child components verticallyj,
+   from top to bottom.  Each child get a height corresponding to its
+   preferred height.  The width of each child is set the maximum of the
+   preferred widths of all children.  */
+grub_gui_container_t
+grub_gui_vbox_new (void)
+{
+  return (grub_gui_container_t) box_new (layout_vertically);
+}

=== added file 'gfxmenu/gui_canvas.c'
--- gfxmenu/gui_canvas.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_canvas.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,264 @@
+/* gui_canvas.c - GUI container allowing manually placed components. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+// TODO Add layering so that components can be properly overlaid.
+
+struct component_node
+{
+  grub_gui_component_t component;
+  struct component_node *next;
+};
+
+struct grub_gui_canvas
+{
+  struct grub_gui_container_ops *container;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  /* Component list (dummy head node).  */
+  struct component_node components;
+};
+
+typedef struct grub_gui_canvas *grub_gui_canvas_t;
+
+static void
+canvas_destroy (void *vself)
+{
+  grub_gui_canvas_t self = vself;
+  struct component_node *cur;
+  struct component_node *next;
+  for (cur = self->components.next; cur; cur = next)
+    {
+      /* Copy the 'next' pointer, since we need it for the next iteration,
+         and we're going to free the memory it is stored in.  */
+      next = cur->next;
+      /* Destroy the child component.  */
+      cur->component->ops->destroy (cur->component);
+      /* Free the linked list node.  */
+      grub_free (cur);
+    }
+  grub_free (self);
+}
+
+static const char *
+canvas_get_id (void *vself)
+{
+  grub_gui_canvas_t self = vself;
+  return self->id;
+}
+
+static int
+canvas_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return (grub_strcmp (type, "component") == 0
+          || grub_strcmp (type, "container") == 0);
+}
+
+static void
+canvas_paint (void *vself)
+{
+  grub_gui_canvas_t self = vself;
+  struct component_node *cur;
+  grub_video_rect_t vpsave;
+
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+  for (cur = self->components.next; cur; cur = cur->next)
+    {
+      int pw;
+      int ph;
+      grub_video_rect_t r;
+      grub_gui_component_t comp;
+
+      comp = cur->component;
+
+      /* Give the child its preferred size.  */
+      comp->ops->get_preferred_size (comp, &pw, &ph);
+      comp->ops->get_bounds (comp, &r);
+      if (r.width != pw || r.height != ph)
+        {
+          r.width = pw;
+          r.height = ph;
+          comp->ops->set_bounds (comp, &r);
+        }
+
+      /* Paint the child.  */
+      comp->ops->paint (comp);
+    }
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+canvas_set_parent (void *vself, grub_gui_container_t parent)
+{
+  grub_gui_canvas_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+canvas_get_parent (void *vself)
+{
+  grub_gui_canvas_t self = vself;
+  return self->parent;
+}
+
+static void
+canvas_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  grub_gui_canvas_t self = vself;
+  self->bounds = *bounds;
+}
+
+static void
+canvas_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  grub_gui_canvas_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+canvas_get_preferred_size (void *vself, int *width, int *height)
+{
+  grub_gui_canvas_t self = vself;
+  *width = 0;
+  *height = 0;
+
+  /* Allow preferred dimensions to override the empty dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+canvas_set_property (void *vself, const char *name, const char *value)
+{
+  grub_gui_canvas_t self = vself;
+  if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+}
+
+static void
+canvas_add (void *vself, grub_gui_component_t comp)
+{
+  grub_gui_canvas_t self = vself;
+  struct component_node *node;
+  node = grub_malloc (sizeof (*node));
+  if (! node)
+    return;   /* Note: probably should handle the error.  */
+  node->component = comp;
+  node->next = self->components.next;
+  self->components.next = node;
+  comp->ops->set_parent (comp, (grub_gui_container_t) self);
+}
+
+static void
+canvas_remove (void *vself, grub_gui_component_t comp)
+{
+  grub_gui_canvas_t self = vself;
+  struct component_node *cur;
+  struct component_node *prev;
+  prev = &self->components;
+  for (cur = self->components.next; cur; prev = cur, cur = cur->next)
+    {
+      if (cur->component == comp)
+        {
+          /* Unlink 'cur' from the list.  */
+          prev->next = cur->next;
+          /* Free the node's memory (but don't destroy the component).  */
+          grub_free (cur);
+          /* Must not loop again, since 'cur' would be dereferenced!  */
+          return;
+        }
+    }
+}
+
+static void
+canvas_iterate_children (void *vself,
+                         grub_gui_component_callback cb, void *userdata)
+{
+  grub_gui_canvas_t self = vself;
+  struct component_node *cur;
+  for (cur = self->components.next; cur; cur = cur->next)
+    cb (cur->component, userdata);
+}
+
+static struct grub_gui_container_ops canvas_ops =
+{
+  .component =
+    {
+      .destroy = canvas_destroy,
+      .get_id = canvas_get_id,
+      .is_instance = canvas_is_instance,
+      .paint = canvas_paint,
+      .set_parent = canvas_set_parent,
+      .get_parent = canvas_get_parent,
+      .set_bounds = canvas_set_bounds,
+      .get_bounds = canvas_get_bounds,
+      .get_preferred_size = canvas_get_preferred_size,
+      .set_property = canvas_set_property
+    },
+  .add = canvas_add,
+  .remove = canvas_remove,
+  .iterate_children = canvas_iterate_children
+};
+
+grub_gui_container_t
+grub_gui_canvas_new (void)
+{
+  grub_gui_canvas_t canvas;
+  canvas = grub_malloc (sizeof (*canvas));
+  if (! canvas)
+    return 0;
+  canvas->container = &canvas_ops;
+  canvas->parent = 0;
+  canvas->bounds.x = 0;
+  canvas->bounds.y = 0;
+  canvas->bounds.width = 0;
+  canvas->bounds.height = 0;
+  canvas->id = 0;
+  canvas->preferred_width = -1;
+  canvas->preferred_height = -1;
+  canvas->components.component = 0;
+  canvas->components.next = 0;
+  return (grub_gui_container_t) canvas;
+}

=== added file 'gfxmenu/gui_circular_progress.c'
--- gfxmenu/gui_circular_progress.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_circular_progress.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,339 @@
+/* gui_circular_process.c - GUI circular progress indicator component.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+#include <grub/trig.h>
+
+struct grub_gui_circular_progress
+{
+  struct grub_gui_component_ops *circprog_ops;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  int visible;
+  int start;
+  int end;
+  int value;
+  int num_ticks;
+  int start_angle;
+  int ticks_disappear;
+  char *theme_dir;
+  int need_to_load_pixmaps;
+  char *center_file;
+  char *tick_file;
+  struct grub_video_bitmap *center_bitmap;
+  struct grub_video_bitmap *tick_bitmap;
+};
+
+typedef struct grub_gui_circular_progress *circular_progress_t;
+
+static void
+circprog_destroy (void *vself)
+{
+  circular_progress_t self = vself;
+  grub_free (self);
+}
+
+static const char *
+circprog_get_id (void *vself)
+{
+  circular_progress_t self = vself;
+  return self->id;
+}
+
+static int
+circprog_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return grub_strcmp (type, "component") == 0;
+}
+
+static struct grub_video_bitmap *
+load_bitmap (const char *dir, const char *file)
+{
+  struct grub_video_bitmap *bitmap;
+  char *abspath;
+
+  /* Check arguments.  */
+  if (! dir || ! file)
+    return 0;
+
+  /* Resolve to an absolute path.  */
+  abspath = grub_resolve_relative_path (dir, file);
+  if (! abspath)
+    return 0;
+
+  /* Load the image.  */
+  grub_errno = GRUB_ERR_NONE;
+  grub_video_bitmap_load (&bitmap, abspath);
+  grub_errno = GRUB_ERR_NONE;
+
+  grub_free (abspath);
+  return bitmap;
+}
+
+static int
+check_pixmaps (circular_progress_t self)
+{
+  if (self->need_to_load_pixmaps)
+    {
+      if (self->center_bitmap)
+        grub_video_bitmap_destroy (self->center_bitmap);
+      self->center_bitmap = load_bitmap (self->theme_dir, self->center_file);
+      self->tick_bitmap = load_bitmap (self->theme_dir, self->tick_file);
+      self->need_to_load_pixmaps = 0;
+    }
+
+  return (self->center_bitmap != 0 && self->tick_bitmap != 0);
+}
+
+static void
+circprog_paint (void *vself)
+{
+  circular_progress_t self = vself;
+
+  if (! self->visible)
+    return;
+  if (! check_pixmaps (self))
+    return;
+
+  grub_video_rect_t vpsave;
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+
+  int width = self->bounds.width;
+  int height = self->bounds.height;
+  int center_width = grub_video_bitmap_get_width (self->center_bitmap);
+  int center_height = grub_video_bitmap_get_height (self->center_bitmap);
+  int tick_width = grub_video_bitmap_get_width (self->tick_bitmap);
+  int tick_height = grub_video_bitmap_get_height (self->tick_bitmap);
+  grub_video_blit_bitmap (self->center_bitmap, GRUB_VIDEO_BLIT_BLEND,
+                          (width - center_width) / 2,
+                          (height - center_height) / 2, 0, 0,
+                          center_width, center_height);
+
+  int radius = width / 2 - tick_width / 2 - 1;
+  int nticks = (self->num_ticks
+                * (self->value - self->start)
+                / (self->end - self->start));
+  int tick_begin;
+  int tick_end;
+  /* Do ticks appear or disappear as the value approached the end?  */
+  if (self->ticks_disappear)
+    {
+      tick_begin = nticks;
+      tick_end = self->num_ticks - 1;
+    }
+  else
+    {
+      tick_begin = 0;
+      tick_end = nticks - 1;
+    }
+
+  int i;
+  for (i = tick_begin; i < tick_end; i++)
+    {
+       int x;
+       int y;
+       int angle;
+
+       /* Calculate the location of the tick.  */
+       angle = self->start_angle + i * GRUB_TRIG_ANGLE_MAX / self->num_ticks;
+       x = width / 2 + (grub_cos (angle) * radius / GRUB_TRIG_FRACTION_SCALE);
+       y = height / 2 + (grub_sin (angle) * radius / GRUB_TRIG_FRACTION_SCALE);
+
+       /* Adjust (x,y) so the tick is centered.  */
+       x -= tick_width / 2;
+       y -= tick_height / 2;
+
+       /* Draw the tick.  */
+       grub_video_blit_bitmap (self->tick_bitmap, GRUB_VIDEO_BLIT_BLEND,
+                               x, y, 0, 0, tick_width, tick_height);
+    }
+
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+circprog_set_parent (void *vself, grub_gui_container_t parent)
+{
+  circular_progress_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+circprog_get_parent (void *vself)
+{
+  circular_progress_t self = vself;
+  return self->parent;
+}
+
+static void
+circprog_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  circular_progress_t self = vself;
+  self->bounds = *bounds;
+}
+
+static void
+circprog_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  circular_progress_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+circprog_get_preferred_size (void *vself, int *width, int *height)
+{
+  circular_progress_t self = vself;
+
+  *width = 0;
+  *height = 0;
+
+  /* Allow preferred dimensions to override the circprog dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+circprog_set_property (void *vself, const char *name, const char *value)
+{
+  circular_progress_t self = vself;
+  if (grub_strcmp (name, "value") == 0)
+    {
+      self->value = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "start") == 0)
+    {
+      self->start = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "end") == 0)
+    {
+      self->end = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "num_ticks") == 0)
+    {
+      self->num_ticks = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "start_angle") == 0)
+    {
+      self->start_angle = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "ticks_disappear") == 0)
+    {
+      self->ticks_disappear = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "center_bitmap") == 0)
+    {
+      self->need_to_load_pixmaps = 1;
+      grub_free (self->center_file);
+      self->center_file = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "tick_bitmap") == 0)
+    {
+      self->need_to_load_pixmaps = 1;
+      grub_free (self->tick_file);
+      self->tick_file = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "theme_dir") == 0)
+    {
+      self->need_to_load_pixmaps = 1;
+      grub_free (self->theme_dir);
+      self->theme_dir = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+  else if (grub_strcmp (name, "visible") == 0)
+    {
+      self->visible = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+}
+
+static struct grub_gui_component_ops circprog_ops =
+{
+  .destroy = circprog_destroy,
+  .get_id = circprog_get_id,
+  .is_instance = circprog_is_instance,
+  .paint = circprog_paint,
+  .set_parent = circprog_set_parent,
+  .get_parent = circprog_get_parent,
+  .set_bounds = circprog_set_bounds,
+  .get_bounds = circprog_get_bounds,
+  .get_preferred_size = circprog_get_preferred_size,
+  .set_property = circprog_set_property
+};
+
+grub_gui_component_t
+grub_gui_circular_progress_new (void)
+{
+  circular_progress_t self;
+  self = grub_malloc (sizeof (*self));
+  if (! self)
+    return 0;
+  self->circprog_ops = &circprog_ops;
+  self->parent = 0;
+  self->bounds.x = 0;
+  self->bounds.y = 0;
+  self->bounds.width = 0;
+  self->bounds.height = 0;
+  self->id = 0;
+  self->preferred_width = -1;
+  self->preferred_height = -1;
+  self->visible = 1;
+  self->start = 0;
+  self->end = 0;
+  self->value = 0;
+  self->num_ticks = 64;
+  self->start_angle = -64;
+  self->ticks_disappear = 0;
+
+  self->theme_dir = 0;
+  self->need_to_load_pixmaps = 0;
+  self->center_file = 0;
+  self->tick_file = 0;
+  self->center_bitmap = 0;
+  self->tick_bitmap = 0;
+
+  return (grub_gui_component_t) self;
+}

=== added file 'gfxmenu/gui_image.c'
--- gfxmenu/gui_image.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_image.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,262 @@
+/* gui_image.c - GUI component to display an image.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+
+struct grub_gui_image
+{
+  struct grub_gui_component_ops *image;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  struct grub_video_bitmap *raw_bitmap;
+  struct grub_video_bitmap *bitmap;
+};
+
+typedef struct grub_gui_image *grub_gui_image_t;
+
+static void
+image_destroy (void *vself)
+{
+  grub_gui_image_t self = vself;
+
+  /* Free the scaled bitmap, unless it's a reference to the raw bitmap.  */
+  if (self->bitmap && (self->bitmap != self->raw_bitmap))
+    grub_video_bitmap_destroy (self->bitmap);
+  if (self->raw_bitmap)
+    grub_video_bitmap_destroy (self->raw_bitmap);
+
+  grub_free (self);
+}
+
+static const char *
+image_get_id (void *vself)
+{
+  grub_gui_image_t self = vself;
+  return self->id;
+}
+
+static int
+image_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return grub_strcmp (type, "component") == 0;
+}
+
+static void
+image_paint (void *vself)
+{
+  grub_gui_image_t self = vself;
+  if (! self->bitmap)
+    return;
+  grub_video_rect_t vpsave;
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+  grub_video_blit_bitmap (self->bitmap, GRUB_VIDEO_BLIT_BLEND,
+                          0, 0, 0, 0,
+                          grub_video_bitmap_get_width (self->bitmap),
+                          grub_video_bitmap_get_height (self->bitmap));
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+image_set_parent (void *vself, grub_gui_container_t parent)
+{
+  grub_gui_image_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+image_get_parent (void *vself)
+{
+  grub_gui_image_t self = vself;
+  return self->parent;
+}
+
+static void
+rescale_image (grub_gui_image_t self)
+{
+  if (! self->raw_bitmap)
+    {
+      if (self->bitmap)
+        {
+          grub_video_bitmap_destroy (self->bitmap);
+          self->bitmap = 0;
+        }
+      return;
+    }
+
+  unsigned width = self->bounds.width;
+  unsigned height = self->bounds.height;
+
+  if (self->bitmap
+      && (grub_video_bitmap_get_width (self->bitmap) == width)
+      && (grub_video_bitmap_get_height (self->bitmap) == height))
+    return;   /* Nothing to do; already the right size.  */
+
+  /* Free any old scaled bitmap,
+     unless it's a reference to the raw bitmap.  */
+  if (self->bitmap && (self->bitmap != self->raw_bitmap))
+    grub_video_bitmap_destroy (self->bitmap);
+
+  /* Create a scaled bitmap, unless the requested size is the same
+     as the raw size -- in that case a reference is made.  */
+  if (grub_video_bitmap_get_width (self->raw_bitmap) == width
+      && grub_video_bitmap_get_height (self->raw_bitmap) == height)
+    {
+      self->bitmap = self->raw_bitmap;
+      return;
+    }
+
+  /* Create the scaled bitmap.  */
+  grub_video_bitmap_create_scaled (&self->bitmap,
+                                   width,
+                                   height,
+                                   self->raw_bitmap,
+                                   GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+  grub_errno = GRUB_ERR_NONE;  /* Catch any errors.  */
+}
+
+static void
+image_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  grub_gui_image_t self = vself;
+  self->bounds = *bounds;
+  rescale_image (self);
+}
+
+static void
+image_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  grub_gui_image_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+image_get_preferred_size (void *vself, int *width, int *height)
+{
+  grub_gui_image_t self = vself;
+
+  if (self->raw_bitmap)
+    {
+      *width = grub_video_bitmap_get_width (self->raw_bitmap);
+      *height = grub_video_bitmap_get_height (self->raw_bitmap);
+    }
+  else
+    {
+      *width = 0;
+      *height = 0;
+    }
+
+  /* Allow preferred dimensions to override the image dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+load_image (grub_gui_image_t self, const char *path)
+{
+  struct grub_video_bitmap *bitmap;
+  grub_errno = GRUB_ERR_NONE;
+  if (grub_video_bitmap_load (&bitmap, path) != GRUB_ERR_NONE)
+    {
+      /* We should log the error somehow.  */
+      grub_errno = GRUB_ERR_NONE;
+      return;
+    }
+  if (self->bitmap && (self->bitmap != self->raw_bitmap))
+    grub_video_bitmap_destroy (self->bitmap);
+  if (self->raw_bitmap)
+    grub_video_bitmap_destroy (self->raw_bitmap);
+
+  self->raw_bitmap = bitmap;
+  rescale_image (self);
+}
+
+static void
+image_set_property (void *vself, const char *name, const char *value)
+{
+  grub_gui_image_t self = vself;
+  if (grub_strcmp (name, "file") == 0)
+    {
+      load_image (self, value);
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+  else if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+}
+
+static struct grub_gui_component_ops image_ops =
+{
+  .destroy = image_destroy,
+  .get_id = image_get_id,
+  .is_instance = image_is_instance,
+  .paint = image_paint,
+  .set_parent = image_set_parent,
+  .get_parent = image_get_parent,
+  .set_bounds = image_set_bounds,
+  .get_bounds = image_get_bounds,
+  .get_preferred_size = image_get_preferred_size,
+  .set_property = image_set_property
+};
+
+grub_gui_component_t
+grub_gui_image_new (void)
+{
+  grub_gui_image_t image;
+  image = grub_malloc (sizeof (*image));
+  if (! image)
+    return 0;
+  image->image = &image_ops;
+  image->parent = 0;
+  image->bounds.x = 0;
+  image->bounds.y = 0;
+  image->bounds.width = 0;
+  image->bounds.height = 0;
+  image->id = 0;
+  image->preferred_width = -1;
+  image->preferred_height = -1;
+  image->raw_bitmap = 0;
+  image->bitmap = 0;
+  return (grub_gui_component_t) image;
+}
+

=== added file 'gfxmenu/gui_label.c'
--- gfxmenu/gui_label.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_label.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,248 @@
+/* gui_label.c - GUI component to display a line of text.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+
+static const char *align_options[] =
+{
+  "left",
+  "center",
+  "right",
+  0
+};
+
+enum align_mode {
+  align_left,
+  align_center,
+  align_right
+};
+
+struct grub_gui_label
+{
+  struct grub_gui_component_ops *label;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  int visible;
+  char *text;
+  grub_font_t font;
+  grub_gui_color_t color;
+  enum align_mode align;
+};
+
+typedef struct grub_gui_label *grub_gui_label_t;
+
+static void
+label_destroy (void *vself)
+{
+  grub_gui_label_t self = vself;
+  grub_free (self->text);
+  grub_free (self);
+}
+
+static const char *
+label_get_id (void *vself)
+{
+  grub_gui_label_t self = vself;
+  return self->id;
+}
+
+static int
+label_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return grub_strcmp (type, "component") == 0;
+}
+
+static void
+label_paint (void *vself)
+{
+  grub_gui_label_t self = vself;
+
+  if (! self->visible)
+    return;
+
+  /* Calculate the starting x coordinate.  */
+  int left_x;
+  if (self->align == align_left)
+    left_x = 0;
+  else if (self->align == align_center)
+    left_x = ((self->bounds.width
+               - grub_font_get_string_width (self->font, self->text))
+             ) / 2;
+  else if (self->align == align_right)
+    left_x = (self->bounds.width
+              - grub_font_get_string_width (self->font, self->text));
+  else
+    return;   /* Invalid alignment.  */
+
+  grub_video_rect_t vpsave;
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+  grub_font_draw_string (self->text,
+                         self->font,
+                         grub_gui_map_color (self->color),
+                         left_x,
+                         grub_font_get_ascent (self->font));
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+label_set_parent (void *vself, grub_gui_container_t parent)
+{
+  grub_gui_label_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+label_get_parent (void *vself)
+{
+  grub_gui_label_t self = vself;
+  return self->parent;
+}
+
+static void
+label_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  grub_gui_label_t self = vself;
+  self->bounds = *bounds;
+}
+
+static void
+label_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  grub_gui_label_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+label_get_preferred_size (void *vself, int *width, int *height)
+{
+  grub_gui_label_t self = vself;
+  *width = grub_font_get_string_width (self->font, self->text);
+  *height = (grub_font_get_ascent (self->font)
+             + grub_font_get_descent (self->font));
+
+  /* Allow preferred dimensions to override the computed dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+label_set_property (void *vself, const char *name, const char *value)
+{
+  grub_gui_label_t self = vself;
+  if (grub_strcmp (name, "text") == 0)
+    {
+      grub_free (self->text);
+      if (! value)
+        value = "";
+      self->text = grub_strdup (value);
+    }
+  else if (grub_strcmp (name, "font") == 0)
+    {
+      self->font = grub_font_get (value);
+    }
+  else if (grub_strcmp (name, "color") == 0)
+    {
+      self->color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "align") == 0)
+    {
+      int i;
+      for (i = 0; align_options[i]; i++)
+        {
+          if (grub_strcmp (align_options[i], value) == 0)
+            {
+              self->align = i;   /* Set the alignment mode.  */
+              break;
+            }
+        }
+    }
+  else if (grub_strcmp (name, "visible") == 0)
+    {
+      self->visible = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+  else if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+}
+
+static struct grub_gui_component_ops label_ops =
+{
+  .destroy = label_destroy,
+  .get_id = label_get_id,
+  .is_instance = label_is_instance,
+  .paint = label_paint,
+  .set_parent = label_set_parent,
+  .get_parent = label_get_parent,
+  .set_bounds = label_set_bounds,
+  .get_bounds = label_get_bounds,
+  .get_preferred_size = label_get_preferred_size,
+  .set_property = label_set_property
+};
+
+grub_gui_component_t
+grub_gui_label_new (void)
+{
+  grub_gui_label_t label;
+  label = grub_malloc (sizeof (*label));
+  if (! label)
+    return 0;
+  label->label = &label_ops;
+  label->parent = 0;
+  label->bounds.x = 0;
+  label->bounds.y = 0;
+  label->bounds.width = 0;
+  label->bounds.height = 0;
+  label->id = 0;
+  label->preferred_width = -1;
+  label->preferred_height = -1;
+  label->visible = 1;
+  label->text = grub_strdup ("");
+  label->font = grub_font_get ("Helvetica 10");
+  label->color.red = 0;
+  label->color.green = 0;
+  label->color.blue = 0;
+  label->color.alpha = 255;
+  label->align = align_left;
+  return (grub_gui_component_t) label;
+}

=== added file 'gfxmenu/gui_list.c'
--- gfxmenu/gui_list.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_list.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,624 @@
+/* gui_list.c - GUI component to display a selectable list of items.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+
+struct grub_gui_list_impl
+{
+  struct grub_gui_list_ops *list_ops;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  int visible;
+
+  int icon_width;
+  int icon_height;
+  int item_height;
+  int item_padding;
+  int item_icon_space;
+  int item_spacing;
+  grub_font_t item_font;
+  grub_font_t selected_item_font;
+  grub_gui_color_t item_color;
+  int selected_item_color_set;
+  grub_gui_color_t selected_item_color;
+
+  int draw_scrollbar;
+  int need_to_recreate_scrollbar;
+  char *scrollbar_frame_pattern;
+  char *scrollbar_thumb_pattern;
+  grub_gfxmenu_box_t scrollbar_frame;
+  grub_gfxmenu_box_t scrollbar_thumb;
+  int scrollbar_width;
+
+  int min_items_shown;
+  int max_items_shown;
+  int first_shown_index;
+
+  int need_to_recreate_boxes;
+  char *theme_dir;
+  char *menu_box_pattern;
+  char *selected_item_box_pattern;
+  grub_gfxmenu_box_t menu_box;
+  grub_gfxmenu_box_t selected_item_box;
+
+  grub_gfxmenu_icon_manager_t icon_manager;
+  grub_gfxmenu_model_t menu;
+};
+
+typedef struct grub_gui_list_impl *list_impl_t;
+
+static void
+list_destroy (void *vself)
+{
+  list_impl_t self = vself;
+
+  grub_free (self->theme_dir);
+  grub_free (self->menu_box_pattern);
+  grub_free (self->selected_item_box_pattern);
+  if (self->menu_box)
+    self->menu_box->destroy (self->menu_box);
+  if (self->selected_item_box)
+    self->selected_item_box->destroy (self->selected_item_box);
+  if (self->icon_manager)
+    grub_gfxmenu_icon_manager_destroy (self->icon_manager);
+
+  grub_free (self);
+}
+
+static int
+get_num_shown_items (list_impl_t self)
+{
+  int n = grub_gfxmenu_model_get_num_entries (self->menu);
+  if (self->min_items_shown != -1 && n < self->min_items_shown)
+    n = self->min_items_shown;
+  if (self->max_items_shown != -1 && n > self->max_items_shown)
+    n = self->max_items_shown;
+  return n;
+}
+
+static int
+check_boxes (list_impl_t self)
+{
+  if (self->need_to_recreate_boxes)
+    {
+      grub_gui_recreate_box (&self->menu_box,
+                             self->menu_box_pattern,
+                             self->theme_dir);
+
+      grub_gui_recreate_box (&self->selected_item_box,
+                             self->selected_item_box_pattern,
+                             self->theme_dir);
+
+      self->need_to_recreate_boxes = 0;
+    }
+
+  return (self->menu_box != 0 && self->selected_item_box != 0);
+}
+
+static int
+check_scrollbar (list_impl_t self)
+{
+  if (self->need_to_recreate_scrollbar)
+    {
+      grub_gui_recreate_box (&self->scrollbar_frame,
+                             self->scrollbar_frame_pattern,
+                             self->theme_dir);
+
+      grub_gui_recreate_box (&self->scrollbar_thumb,
+                             self->scrollbar_thumb_pattern,
+                             self->theme_dir);
+
+      self->need_to_recreate_scrollbar = 0;
+    }
+
+  return (self->scrollbar_frame != 0 && self->scrollbar_thumb != 0);
+}
+
+static const char *
+list_get_id (void *vself)
+{
+  list_impl_t self = vself;
+  return self->id;
+}
+
+static int
+list_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return (grub_strcmp (type, "component") == 0
+          || grub_strcmp (type, "list") == 0);
+}
+
+static struct grub_video_bitmap *
+get_item_icon (list_impl_t self, int item_index)
+{
+  grub_menu_entry_t entry;
+  entry = grub_gfxmenu_model_get_entry (self->menu, item_index);
+  if (! entry)
+    return 0;
+
+  return grub_gfxmenu_icon_manager_get_icon (self->icon_manager, entry);
+}
+
+static void
+make_selected_item_visible (list_impl_t self)
+{
+  int selected_index = grub_gfxmenu_model_get_selected_index (self->menu);
+  if (selected_index < 0)
+    return;   /* No item is selected.  */
+  int num_shown_items = get_num_shown_items (self);
+  int last_shown_index = self->first_shown_index + (num_shown_items - 1);
+  if (selected_index < self->first_shown_index)
+    self->first_shown_index = selected_index;
+  else if (selected_index > last_shown_index)
+    self->first_shown_index = selected_index - (num_shown_items - 1);
+}
+
+/* Draw a scrollbar on the menu.  */
+static void
+draw_scrollbar (list_impl_t self,
+                int value, int extent, int min, int max,
+                int rightx, int topy, int height)
+{
+  grub_gfxmenu_box_t frame = self->scrollbar_frame;
+  grub_gfxmenu_box_t thumb = self->scrollbar_thumb;
+  int frame_vertical_pad = (frame->get_top_pad (frame)
+                            + frame->get_bottom_pad (frame));
+  int frame_horizontal_pad = (frame->get_left_pad (frame)
+                              + frame->get_right_pad (frame));
+  int tracktop = topy + frame->get_top_pad (frame);
+  int tracklen = height - frame_vertical_pad;
+  frame->set_content_size (frame, self->scrollbar_width, tracklen);
+  int thumby = tracktop + tracklen * (value - min) / (max - min);
+  int thumbheight = tracklen * extent / (max - min) + 1;
+  thumb->set_content_size (thumb,
+                           self->scrollbar_width - frame_horizontal_pad,
+                           thumbheight - (thumb->get_top_pad (thumb)
+                                          + thumb->get_bottom_pad (thumb)));
+  frame->draw (frame,
+               rightx - (self->scrollbar_width + frame_horizontal_pad),
+               topy);
+  thumb->draw (thumb,
+               rightx - (self->scrollbar_width - frame->get_right_pad (frame)),
+               thumby);
+}
+
+/* Draw the list of items.  */
+static void
+draw_menu (list_impl_t self)
+{
+  if (! self->menu_box || ! self->selected_item_box)
+    return;
+
+  int boxpad = self->item_padding;
+  int icon_text_space = self->item_icon_space;
+  int item_vspace = self->item_spacing;
+
+  int ascent = grub_font_get_ascent (self->item_font);
+  int descent = grub_font_get_descent (self->item_font);
+  int item_height = self->item_height;
+
+  int total_num_items = grub_gfxmenu_model_get_num_entries (self->menu);
+  int num_shown_items = get_num_shown_items (self);
+  grub_gfxmenu_box_t box = self->menu_box;
+  int width = self->bounds.width;
+  int height = self->bounds.height;
+
+  int box_left_pad = box->get_left_pad (box);
+  int box_top_pad = box->get_top_pad (box);
+  int box_right_pad = box->get_right_pad (box);
+  int box_bottom_pad = box->get_bottom_pad (box);
+
+  box->set_content_size (box,
+                         width - box_left_pad - box_right_pad,
+                         height - box_top_pad - box_bottom_pad);
+
+  box->draw (box, 0, 0);
+
+  make_selected_item_visible (self);
+
+  int drawing_scrollbar = (self->draw_scrollbar
+                           && (num_shown_items < total_num_items)
+                           && check_scrollbar (self));
+
+  int scrollbar_h_space = drawing_scrollbar ? self->scrollbar_width : 0;
+
+  int item_top = box_top_pad + boxpad;
+  int item_left = box_left_pad + boxpad;
+  int menu_index;
+  int visible_index;
+
+  for (visible_index = 0, menu_index = self->first_shown_index;
+       visible_index < num_shown_items && menu_index < total_num_items;
+       visible_index++, menu_index++)
+    {
+      int is_selected =
+        (menu_index == grub_gfxmenu_model_get_selected_index (self->menu));
+
+      if (is_selected)
+        {
+          grub_gfxmenu_box_t selbox = self->selected_item_box;
+          int sel_leftpad = selbox->get_left_pad (selbox);
+          int sel_toppad = selbox->get_top_pad (selbox);
+          selbox->set_content_size (selbox,
+                                    (width - 2 * boxpad
+                                     - box_left_pad - box_right_pad
+                                     - scrollbar_h_space),
+                                    item_height);
+          selbox->draw (selbox,
+                        item_left - sel_leftpad,
+                        item_top - sel_toppad);
+        }
+
+      struct grub_video_bitmap *icon;
+      if ((icon = get_item_icon (self, menu_index)) != 0)
+        grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
+                                item_left,
+                                item_top + (item_height - self->icon_height) / 2,
+                                0, 0, self->icon_width, self->icon_height);
+
+      const char *item_title =
+        grub_gfxmenu_model_get_entry_title (self->menu, menu_index);
+      grub_font_t font =
+        (is_selected && self->selected_item_font
+         ? self->selected_item_font
+         : self->item_font);
+      grub_gui_color_t text_color =
+        ((is_selected && self->selected_item_color_set)
+         ? self->selected_item_color
+         : self->item_color);
+      grub_font_draw_string (item_title,
+                             font,
+                             grub_gui_map_color (text_color),
+                             item_left + self->icon_width + icon_text_space,
+                             (item_top + (item_height - (ascent + descent))
+                              / 2 + ascent));
+
+      item_top += item_height + item_vspace;
+    }
+
+  if (drawing_scrollbar)
+    draw_scrollbar (self,
+                    self->first_shown_index, num_shown_items,
+                    0, total_num_items,
+                    width - box_right_pad + self->scrollbar_width,
+                    box_top_pad + boxpad,
+                    height - box_top_pad - box_bottom_pad);
+}
+
+static void
+list_paint (void *vself)
+{
+  list_impl_t self = vself;
+
+  if (! self->visible)
+    return;
+
+  check_boxes (self);
+
+  grub_video_rect_t vpsave;
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+  draw_menu (self);
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+list_set_parent (void *vself, grub_gui_container_t parent)
+{
+  list_impl_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+list_get_parent (void *vself)
+{
+  list_impl_t self = vself;
+  return self->parent;
+}
+
+static void
+list_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  list_impl_t self = vself;
+  self->bounds = *bounds;
+}
+
+static void
+list_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  list_impl_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+list_get_preferred_size (void *vself, int *width, int *height)
+{
+  list_impl_t self = vself;
+
+  if (check_boxes (self))
+    {
+      int boxpad = self->item_padding;
+      int item_vspace = self->item_spacing;
+      int item_height = self->item_height;
+      int num_items = get_num_shown_items (self);
+
+      grub_gfxmenu_box_t box = self->menu_box;
+      int box_left_pad = box->get_left_pad (box);
+      int box_top_pad = box->get_top_pad (box);
+      int box_right_pad = box->get_right_pad (box);
+      int box_bottom_pad = box->get_bottom_pad (box);
+
+      *width = 400 + 2 * boxpad + box_left_pad + box_right_pad;
+
+      /* Set the menu box height to fit the items.  */
+      *height = (item_height * num_items
+                 + item_vspace * (num_items - 1)
+                 + 2 * boxpad
+                 + box_top_pad + box_bottom_pad);
+    }
+  else
+    {
+      *width = 0;
+      *height = 0;
+    }
+
+  /* Allow preferred dimensions to override the computed dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+list_set_property (void *vself, const char *name, const char *value)
+{
+  list_impl_t self = vself;
+  if (grub_strcmp (name, "item_font") == 0)
+    {
+      self->item_font = grub_font_get (value);
+    }
+  else if (grub_strcmp (name, "selected_item_font") == 0)
+    {
+      if (! value || grub_strcmp (value, "inherit") == 0)
+        self->selected_item_font = 0;
+      else
+        self->selected_item_font = grub_font_get (value);
+    }
+  else if (grub_strcmp (name, "item_color") == 0)
+    {
+      self->item_color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "selected_item_color") == 0)
+    {
+      if (! value || grub_strcmp (value, "inherit") == 0)
+        {
+          self->selected_item_color_set = 0;
+        }
+      else
+        {
+          self->selected_item_color_set = 1;
+          self->selected_item_color = grub_gui_parse_color (value);
+        }
+    }
+  else if (grub_strcmp (name, "icon_width") == 0)
+    {
+      self->icon_width = grub_strtol (value, 0, 10);
+      grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+                                               self->icon_width,
+                                               self->icon_height);
+    }
+  else if (grub_strcmp (name, "icon_height") == 0)
+    {
+      self->icon_height = grub_strtol (value, 0, 10);
+      grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+                                               self->icon_width,
+                                               self->icon_height);
+    }
+  else if (grub_strcmp (name, "item_height") == 0)
+    {
+      self->item_height = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "item_padding") == 0)
+    {
+      self->item_padding = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "item_icon_space") == 0)
+    {
+      self->item_icon_space = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "item_spacing") == 0)
+    {
+      self->item_spacing = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "visible") == 0)
+    {
+      self->visible = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "menu_pixmap_style") == 0)
+    {
+      self->need_to_recreate_boxes = 1;
+      grub_free (self->menu_box_pattern);
+      self->menu_box_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "selected_item_pixmap_style") == 0)
+    {
+      self->need_to_recreate_boxes = 1;
+      grub_free (self->selected_item_box_pattern);
+      self->selected_item_box_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "scrollbar_frame") == 0)
+    {
+      self->need_to_recreate_scrollbar = 1;
+      grub_free (self->scrollbar_frame_pattern);
+      self->scrollbar_frame_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "scrollbar_thumb") == 0)
+    {
+      self->need_to_recreate_scrollbar = 1;
+      grub_free (self->scrollbar_thumb_pattern);
+      self->scrollbar_thumb_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "scrollbar_width") == 0)
+    {
+      self->scrollbar_width = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "scrollbar") == 0)
+    {
+      self->draw_scrollbar = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "min_items_shown") == 0)
+    {
+      self->min_items_shown = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "max_items_shown") == 0)
+    {
+      self->max_items_shown = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "theme_dir") == 0)
+    {
+      self->need_to_recreate_boxes = 1;
+      grub_free (self->theme_dir);
+      self->theme_dir = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+  else if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+}
+
+/* Set necessary information that the gfxmenu view provides.  */
+static void
+list_set_view_info (void *vself,
+                    const char *theme_path,
+                    grub_gfxmenu_model_t menu)
+{
+  list_impl_t self = vself;
+  grub_gfxmenu_icon_manager_set_theme_path (self->icon_manager, theme_path);
+  self->menu = menu;
+}
+
+static struct grub_gui_list_ops list_ops =
+{
+  .component_ops =
+    {
+      .destroy = list_destroy,
+      .get_id = list_get_id,
+      .is_instance = list_is_instance,
+      .paint = list_paint,
+      .set_parent = list_set_parent,
+      .get_parent = list_get_parent,
+      .set_bounds = list_set_bounds,
+      .get_bounds = list_get_bounds,
+      .get_preferred_size = list_get_preferred_size,
+      .set_property = list_set_property
+    },
+  .set_view_info = list_set_view_info
+};
+
+grub_gui_component_t
+grub_gui_list_new (void)
+{
+  list_impl_t self;
+  grub_font_t default_font;
+  grub_gui_color_t default_fg_color;
+  grub_gui_color_t default_bg_color;
+
+  self = grub_malloc (sizeof (*self));
+  if (! self)
+    return 0;
+
+  self->list_ops = &list_ops;
+  self->parent = 0;
+  self->bounds.x = 0;
+  self->bounds.y = 0;
+  self->bounds.width = 0;
+  self->bounds.height = 0;
+  self->id = 0;
+  self->preferred_width = -1;
+  self->preferred_height = -1;
+  self->visible = 1;
+
+  default_font = grub_font_get ("Helvetica 12");
+  default_fg_color = grub_gui_color_rgb (0, 0, 0);
+  default_bg_color = grub_gui_color_rgb (255, 255, 255);
+
+  self->icon_width = 32;
+  self->icon_height = 32;
+  self->item_height = 42;
+  self->item_padding = 14;
+  self->item_icon_space = 4;
+  self->item_spacing = 16;
+  self->item_font = default_font;
+  self->selected_item_font = 0;    /* Default to using the item_font.  */
+  self->item_color = default_fg_color;
+  self->selected_item_color_set = 0;  /* Default to using the item_color.  */
+  self->selected_item_color = default_fg_color;
+
+  self->draw_scrollbar = 1;
+  self->need_to_recreate_scrollbar = 1;
+  self->scrollbar_frame = 0;
+  self->scrollbar_thumb = 0;
+  self->scrollbar_frame_pattern = 0;
+  self->scrollbar_thumb_pattern = 0;
+  self->scrollbar_width = 16;
+
+  self->min_items_shown = -1;
+  self->max_items_shown = -1;
+  self->first_shown_index = 0;
+
+  self->need_to_recreate_boxes = 0;
+  self->theme_dir = 0;
+  self->menu_box_pattern = 0;
+  self->selected_item_box_pattern = 0;
+  self->menu_box = grub_gfxmenu_create_box (0, 0);
+  self->selected_item_box = grub_gfxmenu_create_box (0, 0);
+
+  self->icon_manager = grub_gfxmenu_icon_manager_new ();
+  if (! self->icon_manager)
+    {
+      self->list_ops->component_ops.destroy (self);
+      return 0;
+    }
+  grub_gfxmenu_icon_manager_set_icon_size (self->icon_manager,
+                                           self->icon_width,
+                                           self->icon_height);
+  return (grub_gui_component_t) self;
+}

=== added file 'gfxmenu/gui_progress_bar.c'
--- gfxmenu/gui_progress_bar.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_progress_bar.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,378 @@
+/* gui_progress_bar.c - GUI progress bar component.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/font.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gfxwidgets.h>
+
+struct grub_gui_progress_bar
+{
+  struct grub_gui_component_ops *progress_bar;
+
+  grub_gui_container_t parent;
+  grub_video_rect_t bounds;
+  char *id;
+  int preferred_width;
+  int preferred_height;
+  int visible;
+  int start;
+  int end;
+  int value;
+  int show_text;
+  char *text;
+  grub_font_t font;
+  grub_gui_color_t text_color;
+  grub_gui_color_t border_color;
+  grub_gui_color_t bg_color;
+  grub_gui_color_t fg_color;
+
+  char *theme_dir;
+  int need_to_recreate_pixmaps;
+  char *bar_pattern;
+  char *highlight_pattern;
+  grub_gfxmenu_box_t bar_box;
+  grub_gfxmenu_box_t highlight_box;
+};
+
+typedef struct grub_gui_progress_bar *grub_gui_progress_bar_t;
+
+static void
+progress_bar_destroy (void *vself)
+{
+  grub_gui_progress_bar_t self = vself;
+  grub_free (self);
+}
+
+static const char *
+progress_bar_get_id (void *vself)
+{
+  grub_gui_progress_bar_t self = vself;
+  return self->id;
+}
+
+static int
+progress_bar_is_instance (void *vself __attribute__((unused)), const char *type)
+{
+  return grub_strcmp (type, "component") == 0;
+}
+
+static int
+check_pixmaps (grub_gui_progress_bar_t self)
+{
+  if (self->need_to_recreate_pixmaps)
+    {
+      grub_gui_recreate_box (&self->bar_box,
+                             self->bar_pattern,
+                             self->theme_dir);
+
+      grub_gui_recreate_box (&self->highlight_box,
+                             self->highlight_pattern,
+                             self->theme_dir);
+
+      self->need_to_recreate_pixmaps = 0;
+    }
+
+  return (self->bar_box != 0 && self->highlight_box != 0);
+}
+
+static void
+draw_filled_rect_bar (grub_gui_progress_bar_t self)
+{
+  /* Set the progress bar's frame.  */
+  grub_video_rect_t f;
+  f.x = 1;
+  f.y = 1;
+  f.width = self->bounds.width - 2;
+  f.height = self->bounds.height - 2;
+
+  /* Border.  */
+  grub_video_fill_rect (grub_gui_map_color (self->border_color),
+                        f.x - 1, f.y - 1,
+                        f.width + 2, f.height + 2);
+
+  /* Bar background.  */
+  int barwidth = (f.width
+                  * (self->value - self->start)
+                  / (self->end - self->start));
+  grub_video_fill_rect (grub_gui_map_color (self->bg_color),
+                        f.x + barwidth, f.y,
+                        f.width - barwidth, f.height);
+
+  /* Bar foreground.  */
+  grub_video_fill_rect (grub_gui_map_color (self->fg_color),
+                        f.x, f.y,
+                        barwidth, f.height);
+}
+
+static void
+draw_pixmap_bar (grub_gui_progress_bar_t self)
+{
+  grub_gfxmenu_box_t bar = self->bar_box;
+  grub_gfxmenu_box_t hl = self->highlight_box;
+  int w = self->bounds.width;
+  int h = self->bounds.height;
+  int bar_l_pad = bar->get_left_pad (bar);
+  int bar_r_pad = bar->get_right_pad (bar);
+  int bar_t_pad = bar->get_top_pad (bar);
+  int bar_b_pad = bar->get_bottom_pad (bar);
+  int bar_h_pad = bar_l_pad + bar_r_pad;
+  int bar_v_pad = bar_t_pad + bar_b_pad;
+  int tracklen = w - bar_h_pad;
+  int trackheight = h - bar_v_pad;
+  bar->set_content_size (bar, tracklen, trackheight);
+
+  int barwidth = (tracklen
+                  * (self->value - self->start)
+                  / (self->end - self->start));
+  hl->set_content_size (hl, barwidth, h - bar_v_pad);
+
+  bar->draw (bar, 0, 0);
+  hl->draw (hl, bar_l_pad, bar_t_pad);
+}
+
+static void
+draw_text (grub_gui_progress_bar_t self)
+{
+  const char *text = self->text;
+  if (text && self->show_text)
+    {
+      grub_font_t font = self->font;
+      grub_video_color_t text_color = grub_gui_map_color (self->text_color);
+      int width = self->bounds.width;
+      int height = self->bounds.height;
+
+      /* Center the text. */
+      int text_width = grub_font_get_string_width (font, text);
+      int x = (width - text_width) / 2;
+      int y = ((height - grub_font_get_descent (font)) / 2
+               + grub_font_get_ascent (font) / 2);
+      grub_font_draw_string (text, font, text_color, x, y);
+    }
+}
+
+static void
+progress_bar_paint (void *vself)
+{
+  grub_gui_progress_bar_t self = vself;
+  if (! self->visible)
+    return;
+
+  grub_video_rect_t vpsave;
+  grub_gui_set_viewport (&self->bounds, &vpsave);
+
+  if (check_pixmaps (self))
+    draw_pixmap_bar (self);
+  else
+    draw_filled_rect_bar (self);
+
+  draw_text (self);
+
+  grub_gui_restore_viewport (&vpsave);
+}
+
+static void
+progress_bar_set_parent (void *vself, grub_gui_container_t parent)
+{
+  grub_gui_progress_bar_t self = vself;
+  self->parent = parent;
+}
+
+static grub_gui_container_t
+progress_bar_get_parent (void *vself)
+{
+  grub_gui_progress_bar_t self = vself;
+  return self->parent;
+}
+
+static void
+progress_bar_set_bounds (void *vself, const grub_video_rect_t *bounds)
+{
+  grub_gui_progress_bar_t self = vself;
+  self->bounds = *bounds;
+}
+
+static void
+progress_bar_get_bounds (void *vself, grub_video_rect_t *bounds)
+{
+  grub_gui_progress_bar_t self = vself;
+  *bounds = self->bounds;
+}
+
+static void
+progress_bar_get_preferred_size (void *vself, int *width, int *height)
+{
+  grub_gui_progress_bar_t self = vself;
+
+  *width = 200;
+  *height = 28;
+
+  /* Allow preferred dimensions to override the progress_bar dimensions.  */
+  if (self->preferred_width >= 0)
+    *width = self->preferred_width;
+  if (self->preferred_height >= 0)
+    *height = self->preferred_height;
+}
+
+static void
+progress_bar_set_property (void *vself, const char *name, const char *value)
+{
+  grub_gui_progress_bar_t self = vself;
+  if (grub_strcmp (name, "value") == 0)
+    {
+      self->value = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "start") == 0)
+    {
+      self->start = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "end") == 0)
+    {
+      self->end = grub_strtol (value, 0, 10);
+    }
+  else if (grub_strcmp (name, "text") == 0)
+    {
+      grub_free (self->text);
+      if (! value)
+        value = "";
+      self->text = grub_strdup (value);
+    }
+  else if (grub_strcmp (name, "font") == 0)
+    {
+      self->font = grub_font_get (value);
+    }
+  else if (grub_strcmp (name, "text_color") == 0)
+    {
+      self->text_color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "border_color") == 0)
+    {
+      self->border_color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "bg_color") == 0)
+    {
+      self->bg_color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "fg_color") == 0)
+    {
+      self->fg_color = grub_gui_parse_color (value);
+    }
+  else if (grub_strcmp (name, "bar_style") == 0)
+    {
+      self->need_to_recreate_pixmaps = 1;
+      grub_free (self->bar_pattern);
+      self->bar_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "highlight_style") == 0)
+    {
+      self->need_to_recreate_pixmaps = 1;
+      grub_free (self->highlight_pattern);
+      self->highlight_pattern = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "theme_dir") == 0)
+    {
+      self->need_to_recreate_pixmaps = 1;
+      grub_free (self->theme_dir);
+      self->theme_dir = value ? grub_strdup (value) : 0;
+    }
+  else if (grub_strcmp (name, "preferred_size") == 0)
+    {
+      int w;
+      int h;
+      if (grub_gui_parse_2_tuple (value, &w, &h))
+        {
+          self->preferred_width = w;
+          self->preferred_height = h;
+        }
+    }
+  else if (grub_strcmp (name, "visible") == 0)
+    {
+      self->visible = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "show_text") == 0)
+    {
+      self->show_text = grub_strcmp (value, "false") != 0;
+    }
+  else if (grub_strcmp (name, "id") == 0)
+    {
+      grub_free (self->id);
+      if (value)
+        self->id = grub_strdup (value);
+      else
+        self->id = 0;
+    }
+}
+
+static struct grub_gui_component_ops progress_bar_ops =
+{
+  .destroy = progress_bar_destroy,
+  .get_id = progress_bar_get_id,
+  .is_instance = progress_bar_is_instance,
+  .paint = progress_bar_paint,
+  .set_parent = progress_bar_set_parent,
+  .get_parent = progress_bar_get_parent,
+  .set_bounds = progress_bar_set_bounds,
+  .get_bounds = progress_bar_get_bounds,
+  .get_preferred_size = progress_bar_get_preferred_size,
+  .set_property = progress_bar_set_property
+};
+
+grub_gui_component_t
+grub_gui_progress_bar_new (void)
+{
+  grub_gui_progress_bar_t self;
+  self = grub_malloc (sizeof (*self));
+  if (! self)
+    return 0;
+  self->progress_bar = &progress_bar_ops;
+  self->parent = 0;
+  self->bounds.x = 0;
+  self->bounds.y = 0;
+  self->bounds.width = 0;
+  self->bounds.height = 0;
+  self->id = 0;
+  self->preferred_width = -1;
+  self->preferred_height = -1;
+  self->visible = 1;
+  self->start = 0;
+  self->end = 0;
+  self->value = 0;
+  self->show_text = 1;
+  self->text = grub_strdup ("");
+  self->font = grub_font_get ("Helvetica 10");
+  grub_gui_color_t black = { .red = 0, .green = 0, .blue = 0, .alpha = 255 };
+  grub_gui_color_t gray = { .red = 128, .green = 128, .blue = 128, .alpha = 255 };
+  grub_gui_color_t lightgray = { .red = 200, .green = 200, .blue = 200, .alpha = 255 };
+  self->text_color = black;
+  self->border_color = black;
+  self->bg_color = gray;
+  self->fg_color = lightgray;
+
+  self->theme_dir = 0;
+  self->need_to_recreate_pixmaps = 0;
+  self->bar_pattern = 0;
+  self->highlight_pattern = 0;
+  self->bar_box = 0;
+  self->highlight_box = 0;
+
+  return (grub_gui_component_t) self;
+}

=== added file 'gfxmenu/gui_string_util.c'
--- gfxmenu/gui_string_util.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_string_util.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,348 @@
+/* gui_string_util.c - String utilities used by the GUI system.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/gui_string_util.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+
+/* Create a new NUL-terminated string on the heap as a substring of BUF.
+   The range of buf included is the half-open interval [START,END).
+   The index START is inclusive, END is exclusive.  */
+char *
+grub_new_substring (const char *buf,
+                    grub_size_t start, grub_size_t end)
+{
+  if (end < start)
+    return 0;
+  grub_size_t len = end - start;
+  char *s = grub_malloc (len + 1);
+  if (! s)
+    return 0;
+  grub_memcpy (s, buf + start, len);
+  s[len] = '\0';
+  return s;
+}
+
+/* Eliminate "." and ".." path elements from PATH.  A new heap-allocated
+   string is returned.  */
+static char *
+canonicalize_path (const char *path)
+{
+  int i;
+  const char *p;
+  char *newpath = 0;
+
+  /* Count the path components in path.  */
+  int components = 1;
+  for (p = path; *p; p++)
+    if (*p == '/')
+      components++;
+
+  char **path_array = grub_malloc (components * sizeof (*path_array));
+  if (! path_array)
+    return 0;
+
+  /* Initialize array elements to NULL pointers; in case once of the
+     allocations fails, the cleanup code can just call grub_free() for all
+     pointers in the array.  */
+  for (i = 0; i < components; i++)
+    path_array[i] = 0;
+
+  /* Parse the path into path_array.  */
+  p = path;
+  for (i = 0; i < components && p; i++)
+    {
+      /* Find the end of the path element.  */
+      const char *end = grub_strchr (p, '/');
+      if (!end)
+        end = p + grub_strlen (p);
+
+      /* Copy the element.  */
+      path_array[i] = grub_new_substring (p, 0, end - p);
+      if (!path_array[i])
+        goto cleanup;
+
+      /* Advance p to point to the start of the next element, or NULL.  */
+      if (*end)
+        p = end + 1;
+      else
+        p = 0;
+    }
+
+  /* Eliminate '.' and '..' elements from the path array.  */
+  int newpath_length = 0;
+  for (i = components - 1; i >= 0; --i)
+    {
+      if (! grub_strcmp (path_array[i], "."))
+        {
+          grub_free (path_array[i]);
+          path_array[i] = 0;
+        }
+      else if (! grub_strcmp (path_array[i], "..")
+               && i > 0)
+        {
+          /* Delete the '..' and the prior path element.  */
+          grub_free (path_array[i]);
+          path_array[i] = 0;
+          --i;
+          grub_free (path_array[i]);
+          path_array[i] = 0;
+        }
+      else
+        {
+          newpath_length += grub_strlen (path_array[i]) + 1;
+        }
+    }
+
+  /* Construct a new path string.  */
+  newpath = grub_malloc (newpath_length + 1);
+  if (! newpath)
+    goto cleanup;
+
+  newpath[0] = '\0';
+  char *newpath_end = newpath;
+  int first = 1;
+  for (i = 0; i < components; i++)
+    {
+      char *element = path_array[i];
+      if (element)
+        {
+          /* For all components but the first, prefix with a slash.  */
+          if (! first)
+            newpath_end = grub_stpcpy (newpath_end, "/");
+          newpath_end = grub_stpcpy (newpath_end, element);
+          first = 0;
+        }
+    }
+
+cleanup:
+  for (i = 0; i < components; i++)
+    grub_free (path_array[i]);
+  grub_free (path_array);
+
+  return newpath;
+}
+
+/* Return a new heap-allocated string representing to absolute path
+   to the file referred to by PATH.  If PATH is an absolute path, then
+   the returned path is a copy of PATH.  If PATH is a relative path, then
+   BASE is with PATH used to construct the absolute path.  */
+char *
+grub_resolve_relative_path (const char *base, const char *path)
+{
+  char *abspath;
+  char *canonpath;
+  char *p;
+
+  /* If PATH is an absolute path, then just use it as is.  */
+  if (path[0] == '/' || path[0] == '(')
+    return canonicalize_path (path);
+
+  abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 1);
+  if (!abspath)
+    return 0;
+
+  /* Concatenate BASE and PATH.
+     Note that BASE is expected to have a trailing slash.  */
+  p = grub_stpcpy (abspath, base);
+  grub_stpcpy (p, path);
+
+  canonpath = canonicalize_path (abspath);
+  if (!canonpath)
+    return abspath;
+
+  grub_free (abspath);
+  return canonpath;
+}
+
+/* Get the path of the directory where the file at FILE_PATH is located.
+   FILE_PATH should refer to a file, not a directory.  The returned path
+   includes a trailing slash.
+   This does not handle GRUB "(hd0,0)" paths properly yet since it only
+   looks at slashes.  */
+char *
+grub_get_dirname (const char *file_path)
+{
+  int i;
+  int last_slash;
+
+  last_slash = -1;
+  for (i = grub_strlen (file_path) - 1; i >= 0; --i)
+    {
+      if (file_path[i] == '/')
+        {
+          last_slash = i;
+          break;
+        }
+    }
+  if (last_slash == -1)
+    return grub_strdup ("/");
+
+  return grub_new_substring (file_path, 0, last_slash + 1);
+}
+
+static __inline int
+isxdigit (char c)
+{
+  return ((c >= '0' && c <= '9')
+          || (c >= 'a' && c <= 'f')
+          || (c >= 'A' && c <= 'F'));
+}
+
+static int
+parse_hex_color_component (const char *s, unsigned start, unsigned end)
+{
+  unsigned len;
+  char buf[3];
+
+  len = end - start;
+  /* Check the limits so we don't overrun the buffer.  */
+  if (len < 1 || len > 2)
+    return 0;
+
+  if (len == 1)
+    {
+      buf[0] = s[start];   /* Get the first and only hex digit.  */
+      buf[1] = buf[0];     /* Duplicate the hex digit.  */
+    }
+  else if (len == 2)
+    {
+      buf[0] = s[start];
+      buf[1] = s[start + 1];
+    }
+
+  buf[2] = '\0';
+
+  return grub_strtoul (buf, 0, 16);
+}
+
+/* Parse a color string of the form "r, g, b", "#RGB", "#RGBA",
+   "#RRGGBB", or "#RRGGBBAA".  */
+grub_gui_color_t
+grub_gui_parse_color (const char *s)
+{
+  grub_gui_color_t c;
+
+  /* Skip whitespace.  */
+  while (*s && grub_isspace (*s))
+    s++;
+
+  if (*s == '#')
+    {
+      /* HTML-style.  Number if hex digits:
+         [6] #RRGGBB     [3] #RGB
+         [8] #RRGGBBAA   [4] #RGBA  */
+
+      s++;  /* Skip the '#'.  */
+      /* Count the hexits to determine the format.  */
+      int hexits = 0;
+      const char *end = s;
+      while (isxdigit (*end))
+        {
+          end++;
+          hexits++;
+        }
+
+      /* Parse the color components based on the format.  */
+      if (hexits == 3 || hexits == 4)
+        {
+          c.red = parse_hex_color_component (s, 0, 1);
+          c.green = parse_hex_color_component (s, 1, 2);
+          c.blue = parse_hex_color_component (s, 2, 3);
+          if (hexits == 4)
+            c.alpha = parse_hex_color_component (s, 3, 4);
+          else
+            c.alpha = 255;
+        }
+      else if (hexits == 6 || hexits == 8)
+        {
+          c.red = parse_hex_color_component (s, 0, 2);
+          c.green = parse_hex_color_component (s, 2, 4);
+          c.blue = parse_hex_color_component (s, 4, 6);
+          if (hexits == 8)
+            c.alpha = parse_hex_color_component (s, 6, 8);
+          else
+            c.alpha = 255;
+        }
+      else
+        goto fail;
+    }
+  else if (grub_isdigit (*s))
+    {
+      /* Comma separated decimal values.  */
+      c.red = grub_strtoul (s, 0, 0);
+      if ((s = grub_strchr (s, ',')) == 0)
+        goto fail;
+      s++;
+      c.green = grub_strtoul (s, 0, 0);
+      if ((s = grub_strchr (s, ',')) == 0)
+        goto fail;
+      s++;
+      c.blue = grub_strtoul (s, 0, 0);
+      if ((s = grub_strchr (s, ',')) == 0)
+        c.alpha = 255;
+      else
+        {
+          s++;
+          c.alpha = grub_strtoul (s, 0, 0);
+        }
+    }
+  else
+    {
+      if (! grub_gui_get_named_color (s, &c))
+        goto fail;
+    }
+
+  return c;
+
+fail:
+  c.red = 0;
+  c.green = 0;
+  c.blue = 0;
+  c.alpha = 255;
+  return c;
+}
+
+/* Parse a value in the form "(x, y)", storing the first element (x) into
+ *PX and the second element (y) into *PY.
+ Returns 1 if successful, 0 if failed to parse.  */
+int
+grub_gui_parse_2_tuple (const char *s, int *px, int *py)
+{
+  int x;
+  int y;
+
+  while (*s && grub_isspace (*s))
+    s++;
+  if (*s != '(')
+    return 0;
+  s++;  /* Skip the opening parentheses.  */
+  if (! *s)
+    return 0;
+  x = grub_strtol (s, 0, 10);
+  if ((s = grub_strchr (s, ',')) == 0)
+    return 0;
+  s++;  /* Skip the element separator (the comma).  */
+  y = grub_strtol (s, 0, 10);
+
+  *px = x;
+  *py = y;
+  return 1;
+}

=== added file 'gfxmenu/gui_util.c'
--- gfxmenu/gui_util.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/gui_util.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,101 @@
+/* gui_util.c - GUI utility functions.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+
+
+struct find_by_id_state
+{
+  const char *match_id;
+  grub_gui_component_callback match_callback;
+  void *match_userdata;
+};
+
+static void
+find_by_id_recursively (grub_gui_component_t component, void *userdata)
+{
+  struct find_by_id_state *state;
+  const char *id;
+
+  state = (struct find_by_id_state *) userdata;
+  id = component->ops->get_id (component);
+  if (id && grub_strcmp (id, state->match_id) == 0)
+    state->match_callback (component, state->match_userdata);
+
+  if (component->ops->is_instance (component, "container"))
+    {
+      grub_gui_container_t container;
+      container = (grub_gui_container_t) component;
+      container->ops->iterate_children (container,
+                                        find_by_id_recursively,
+                                        state);
+    }
+}
+
+void
+grub_gui_find_by_id (grub_gui_component_t root,
+                     const char *id,
+                     grub_gui_component_callback cb,
+                     void *userdata)
+{
+  struct find_by_id_state state;
+  state.match_id = id;
+  state.match_callback = cb;
+  state.match_userdata = userdata;
+  find_by_id_recursively (root, &state);
+}
+
+
+struct iterate_recursively_state
+{
+  grub_gui_component_callback callback;
+  void *userdata;
+};
+
+static
+void iterate_recursively_cb (grub_gui_component_t component, void *userdata)
+{
+  struct iterate_recursively_state *state;
+
+  state = (struct iterate_recursively_state *) userdata;
+  state->callback (component, state->userdata);
+
+  if (component->ops->is_instance (component, "container"))
+    {
+      grub_gui_container_t container;
+      container = (grub_gui_container_t) component;
+      container->ops->iterate_children (container,
+                                        iterate_recursively_cb,
+                                        state);
+    }
+}
+
+void
+grub_gui_iterate_recursively (grub_gui_component_t root,
+                              grub_gui_component_callback cb,
+                              void *userdata)
+{
+  struct iterate_recursively_state state;
+  state.callback = cb;
+  state.userdata = userdata;
+  iterate_recursively_cb (root, &state);
+}

=== added file 'gfxmenu/icon_manager.c'
--- gfxmenu/icon_manager.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/icon_manager.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,254 @@
+/* icon_manager.c - gfxmenu icon manager.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/menu.h>
+#include <grub/icon_manager.h>
+
+/* Currently hard coded to '.png' extension.  */
+static const char icon_extension[] = ".png";
+
+typedef struct icon_entry
+{
+  char *class_name;
+  struct grub_video_bitmap *bitmap;
+  struct icon_entry *next;
+} *icon_entry_t;
+
+struct grub_gfxmenu_icon_manager
+{
+  char *theme_path;
+  int icon_width;
+  int icon_height;
+
+  /* Icon cache: linked list w/ dummy head node.  */
+  struct icon_entry cache;
+};
+
+
+/* Create a new icon manager and return a point to it.  */
+grub_gfxmenu_icon_manager_t
+grub_gfxmenu_icon_manager_new (void)
+{
+  grub_gfxmenu_icon_manager_t mgr;
+  mgr = grub_malloc (sizeof (*mgr));
+  if (! mgr)
+    return 0;
+
+  mgr->theme_path = 0;
+  mgr->icon_width = 0;
+  mgr->icon_height = 0;
+
+  /* Initialize the dummy head node.  */
+  mgr->cache.class_name = 0;
+  mgr->cache.bitmap = 0;
+  mgr->cache.next = 0;
+
+  return mgr;
+}
+
+/* Destroy the icon manager MGR, freeing all resources used by it.
+
+Note: Any bitmaps returned by grub_gfxmenu_icon_manager_get_icon()
+are destroyed and must not be used by the caller after this function
+is called.  */
+void
+grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr)
+{
+  grub_gfxmenu_icon_manager_clear_cache (mgr);
+  grub_free (mgr->theme_path);
+  grub_free (mgr);
+}
+
+/* Clear the icon cache.  */
+void
+grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr)
+{
+  icon_entry_t cur;
+  icon_entry_t next;
+  for (cur = mgr->cache.next; cur; cur = next)
+    {
+      next = cur->next;
+      grub_free (cur->class_name);
+      grub_video_bitmap_destroy (cur->bitmap);
+      grub_free (cur);
+    }
+  mgr->cache.next = 0;
+}
+
+/* Set the theme path.  If the theme path is changed, the icon cache
+   is cleared.  */
+void
+grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
+                                          const char *path)
+{
+  /* Clear the cache if the theme path has changed.  */
+  if (((mgr->theme_path == 0) != (path == 0))
+      || (grub_strcmp (mgr->theme_path, path) != 0))
+    grub_gfxmenu_icon_manager_clear_cache (mgr);
+
+  grub_free (mgr->theme_path);
+  mgr->theme_path = path ? grub_strdup (path) : 0;
+}
+
+/* Set the icon size.  When icons are requested from the icon manager,
+   they are scaled to this size before being returned.  If the size is
+   changed, the icon cache is cleared.  */
+void
+grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr,
+                                         int width, int height)
+{
+  /* If the width or height is changed, we must clear the cache, since the
+     scaled bitmaps are stored in the cache.  */
+  if (width != mgr->icon_width || height != mgr->icon_height)
+    grub_gfxmenu_icon_manager_clear_cache (mgr);
+
+  mgr->icon_width = width;
+  mgr->icon_height = height;
+}
+
+/* Try to load an icon for the specified CLASS_NAME in the directory DIR.
+   Returns 0 if the icon could not be loaded, or returns a pointer to a new
+   bitmap if it was successful.  */
+static struct grub_video_bitmap *
+try_loading_icon (grub_gfxmenu_icon_manager_t mgr,
+                  const char *dir, const char *class_name)
+{
+  char *path = grub_malloc (grub_strlen (dir)
+                            + grub_strlen (class_name)
+                            + grub_strlen (icon_extension)
+                            + 1);
+  if (! path)
+    return 0;
+
+  grub_strcpy (path, dir);
+  grub_strcat (path, class_name);
+  grub_strcat (path, icon_extension);
+
+  struct grub_video_bitmap *raw_bitmap;
+  grub_video_bitmap_load (&raw_bitmap, path);
+  grub_free (path);
+  grub_errno = GRUB_ERR_NONE;  /* Critical to clear the error!!  */
+  if (! raw_bitmap)
+    return 0;
+
+  struct grub_video_bitmap *scaled_bitmap;
+  grub_video_bitmap_create_scaled (&scaled_bitmap,
+                                   mgr->icon_width, mgr->icon_height,
+                                   raw_bitmap,
+                                   GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+  grub_video_bitmap_destroy (raw_bitmap);
+  if (! scaled_bitmap)
+    return 0;
+
+  return scaled_bitmap;
+}
+
+/* Get the icon for the specified class CLASS_NAME.  If an icon for
+   CLASS_NAME already exists in the cache, then a reference to the cached
+   bitmap is returned.  If it is not cached, then it is loaded and cached.
+   If no icon could be could for CLASS_NAME, then 0 is returned.  */
+static struct grub_video_bitmap *
+get_icon_by_class (grub_gfxmenu_icon_manager_t mgr, const char *class_name)
+{
+  /* First check the icon cache.  */
+  icon_entry_t entry;
+  for (entry = mgr->cache.next; entry; entry = entry->next)
+    {
+      if (grub_strcmp (entry->class_name, class_name) == 0)
+        return entry->bitmap;
+    }
+
+  if (! mgr->theme_path)
+    return 0;
+
+  /* Otherwise, we search for an icon to load.  */
+  char *theme_dir = grub_get_dirname (mgr->theme_path);
+  char *icons_dir;
+  struct grub_video_bitmap *icon;
+  icon = 0;
+  /* First try the theme's own icons, from "grub/themes/NAME/icons/"  */
+  icons_dir = grub_resolve_relative_path (theme_dir, "icons/");
+  if (icons_dir)
+    {
+      icon = try_loading_icon (mgr, icons_dir, class_name);
+      grub_free (icons_dir);
+    }
+  if (! icon)
+    {
+      /* If the theme doesn't have an appropriate icon, check in
+         "grub/themes/icons".  */
+      /* TODO use GRUB prefix "/icons" */
+      icons_dir = grub_resolve_relative_path (theme_dir, "../icons/");
+      if (icons_dir)
+        {
+          icon = try_loading_icon (mgr, icons_dir, class_name);
+          grub_free (icons_dir);
+        }
+    }
+  grub_free (theme_dir);
+
+  /* No icon was found.  */
+  /* This should probably be noted in the cache, so that a search is not
+     performed each time an icon for CLASS_NAME is requested.  */
+  if (! icon)
+    return 0;
+
+  /* Insert a new cache entry for this icon.  */
+  entry = grub_malloc (sizeof (*entry));
+  if (! entry)
+    {
+      grub_video_bitmap_destroy (icon);
+      return 0;
+    }
+  entry->class_name = grub_strdup (class_name);
+  entry->bitmap = icon;
+  entry->next = mgr->cache.next;
+  mgr->cache.next = entry;   /* Link it into the cache.  */
+  return entry->bitmap;
+}
+
+/* Get the best available icon for ENTRY.  Beginning with the first class
+   listed in the menu entry and proceeding forward, an icon for each class
+   is searched for.  The first icon found is returned.  The returned icon
+   is scaled to the size specified by
+   grub_gfxmenu_icon_manager_set_icon_size().
+
+     Note:  Bitmaps returned by this function are destroyed when the
+            icon manager is destroyed.
+ */
+struct grub_video_bitmap *
+grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
+                                    grub_menu_entry_t entry)
+{
+  struct grub_menu_entry_class *c;
+  struct grub_video_bitmap *icon;
+
+  /* Try each class in succession.  */
+  icon = 0;
+  for (c = entry->classes->next; c && ! icon; c = c->next)
+    icon = get_icon_by_class (mgr, c->name);
+  return icon;
+}

=== added file 'gfxmenu/model.c'
--- gfxmenu/model.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/model.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,191 @@
+/* model.c - Graphical menu interface MVC model. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/normal.h>
+#include <grub/menu.h>
+#include <grub/time.h>
+#include <grub/gfxmenu_model.h>
+
+/* Model type definition.  */
+struct grub_gfxmenu_model
+{
+  grub_menu_t menu;
+  int num_entries;
+  grub_menu_entry_t *entries;
+  int selected_entry_index;
+  int timeout_set;
+  grub_uint64_t timeout_start;
+  grub_uint64_t timeout_at;
+};
+
+
+grub_gfxmenu_model_t
+grub_gfxmenu_model_new (grub_menu_t menu)
+{
+  grub_gfxmenu_model_t model;
+
+  model = grub_malloc (sizeof (*model));
+  if (! model)
+    return 0;
+
+  model->menu = menu;
+  model->num_entries = menu->size;
+  model->entries = 0;
+  model->selected_entry_index = 0;
+  model->timeout_set = 0;
+  model->timeout_at = 0;
+  if (model->num_entries > 0)
+    {
+      model->entries = grub_malloc (model->num_entries
+                                    * sizeof (*model->entries));
+      if (! model->entries)
+        goto fail_and_free;
+
+      int i;
+      grub_menu_entry_t cur;
+      for (i = 0, cur = menu->entry_list;
+           i < model->num_entries;
+           i++, cur = cur->next)
+        {
+          model->entries[i] = cur;
+        }
+    }
+
+  return model;
+
+fail_and_free:
+  grub_free (model->entries);
+  grub_free (model);
+  return 0;
+}
+
+void
+grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model)
+{
+  if (! model)
+    return;
+
+  grub_free (model->entries);
+  model->entries = 0;
+
+  grub_free (model);
+}
+
+grub_menu_t
+grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model)
+{
+  return model->menu;
+}
+
+void
+grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model)
+{
+  int timeout_sec = grub_menu_get_timeout ();
+  if (timeout_sec >= 0)
+    {
+      model->timeout_start = grub_get_time_ms ();
+      model->timeout_at = model->timeout_start + timeout_sec * 1000;
+      model->timeout_set = 1;
+    }
+  else
+    {
+      model->timeout_set = 0;
+    }
+}
+
+void
+grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model)
+{
+  model->timeout_set = 0;
+  grub_menu_set_timeout (-1);
+}
+
+int
+grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model)
+{
+  if (!model->timeout_set)
+    return -1;
+
+  return model->timeout_at - model->timeout_start;
+}
+
+int
+grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model)
+{
+  if (!model->timeout_set)
+    return -1;
+
+  return model->timeout_at - grub_get_time_ms ();
+}
+
+int
+grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model)
+{
+  if (model->timeout_set
+      && grub_get_time_ms () >= model->timeout_at)
+    return 1;
+
+  return 0;
+}
+
+int
+grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model)
+{
+  return model->num_entries;
+}
+
+int
+grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model)
+{
+  return model->selected_entry_index;
+}
+
+void
+grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, int index)
+{
+  model->selected_entry_index = index;
+}
+
+const char *
+grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, int index)
+{
+  if (index < 0 || index >= model->num_entries)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index");
+      return 0;
+    }
+
+  return model->entries[index]->title;
+}
+
+grub_menu_entry_t
+grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, int index)
+{
+  if (index < 0 || index >= model->num_entries)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index");
+      return 0;
+    }
+
+  return model->entries[index];
+}

=== added file 'gfxmenu/named_colors.c'
--- gfxmenu/named_colors.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/named_colors.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,209 @@
+/* named_colors.c - Named color values.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/gui.h>
+#include <grub/gui_string_util.h>
+#include <grub/misc.h>
+
+struct named_color
+{
+  const char *name;
+  grub_gui_color_t color;
+};
+
+/*
+   Named color list generated from the list of SVG color keywords from
+   <http://www.w3.org/TR/css3-color/#svg-color>,
+   processed through the following Perl command:
+   perl -ne 'chomp;split;print "{ \"$_[0]\", RGB_COLOR($_[2]) },\n"'
+ */
+
+#define RGB_COLOR(r,g,b) {.red = r, .green = g, .blue = b, .alpha = 255}
+
+static struct named_color named_colors[] =
+{
+    { "aliceblue", RGB_COLOR(240,248,255) },
+    { "antiquewhite", RGB_COLOR(250,235,215) },
+    { "aqua", RGB_COLOR(0,255,255) },
+    { "aquamarine", RGB_COLOR(127,255,212) },
+    { "azure", RGB_COLOR(240,255,255) },
+    { "beige", RGB_COLOR(245,245,220) },
+    { "bisque", RGB_COLOR(255,228,196) },
+    { "black", RGB_COLOR(0,0,0) },
+    { "blanchedalmond", RGB_COLOR(255,235,205) },
+    { "blue", RGB_COLOR(0,0,255) },
+    { "blueviolet", RGB_COLOR(138,43,226) },
+    { "brown", RGB_COLOR(165,42,42) },
+    { "burlywood", RGB_COLOR(222,184,135) },
+    { "cadetblue", RGB_COLOR(95,158,160) },
+    { "chartreuse", RGB_COLOR(127,255,0) },
+    { "chocolate", RGB_COLOR(210,105,30) },
+    { "coral", RGB_COLOR(255,127,80) },
+    { "cornflowerblue", RGB_COLOR(100,149,237) },
+    { "cornsilk", RGB_COLOR(255,248,220) },
+    { "crimson", RGB_COLOR(220,20,60) },
+    { "cyan", RGB_COLOR(0,255,255) },
+    { "darkblue", RGB_COLOR(0,0,139) },
+    { "darkcyan", RGB_COLOR(0,139,139) },
+    { "darkgoldenrod", RGB_COLOR(184,134,11) },
+    { "darkgray", RGB_COLOR(169,169,169) },
+    { "darkgreen", RGB_COLOR(0,100,0) },
+    { "darkgrey", RGB_COLOR(169,169,169) },
+    { "darkkhaki", RGB_COLOR(189,183,107) },
+    { "darkmagenta", RGB_COLOR(139,0,139) },
+    { "darkolivegreen", RGB_COLOR(85,107,47) },
+    { "darkorange", RGB_COLOR(255,140,0) },
+    { "darkorchid", RGB_COLOR(153,50,204) },
+    { "darkred", RGB_COLOR(139,0,0) },
+    { "darksalmon", RGB_COLOR(233,150,122) },
+    { "darkseagreen", RGB_COLOR(143,188,143) },
+    { "darkslateblue", RGB_COLOR(72,61,139) },
+    { "darkslategray", RGB_COLOR(47,79,79) },
+    { "darkslategrey", RGB_COLOR(47,79,79) },
+    { "darkturquoise", RGB_COLOR(0,206,209) },
+    { "darkviolet", RGB_COLOR(148,0,211) },
+    { "deeppink", RGB_COLOR(255,20,147) },
+    { "deepskyblue", RGB_COLOR(0,191,255) },
+    { "dimgray", RGB_COLOR(105,105,105) },
+    { "dimgrey", RGB_COLOR(105,105,105) },
+    { "dodgerblue", RGB_COLOR(30,144,255) },
+    { "firebrick", RGB_COLOR(178,34,34) },
+    { "floralwhite", RGB_COLOR(255,250,240) },
+    { "forestgreen", RGB_COLOR(34,139,34) },
+    { "fuchsia", RGB_COLOR(255,0,255) },
+    { "gainsboro", RGB_COLOR(220,220,220) },
+    { "ghostwhite", RGB_COLOR(248,248,255) },
+    { "gold", RGB_COLOR(255,215,0) },
+    { "goldenrod", RGB_COLOR(218,165,32) },
+    { "gray", RGB_COLOR(128,128,128) },
+    { "green", RGB_COLOR(0,128,0) },
+    { "greenyellow", RGB_COLOR(173,255,47) },
+    { "grey", RGB_COLOR(128,128,128) },
+    { "honeydew", RGB_COLOR(240,255,240) },
+    { "hotpink", RGB_COLOR(255,105,180) },
+    { "indianred", RGB_COLOR(205,92,92) },
+    { "indigo", RGB_COLOR(75,0,130) },
+    { "ivory", RGB_COLOR(255,255,240) },
+    { "khaki", RGB_COLOR(240,230,140) },
+    { "lavender", RGB_COLOR(230,230,250) },
+    { "lavenderblush", RGB_COLOR(255,240,245) },
+    { "lawngreen", RGB_COLOR(124,252,0) },
+    { "lemonchiffon", RGB_COLOR(255,250,205) },
+    { "lightblue", RGB_COLOR(173,216,230) },
+    { "lightcoral", RGB_COLOR(240,128,128) },
+    { "lightcyan", RGB_COLOR(224,255,255) },
+    { "lightgoldenrodyellow", RGB_COLOR(250,250,210) },
+    { "lightgray", RGB_COLOR(211,211,211) },
+    { "lightgreen", RGB_COLOR(144,238,144) },
+    { "lightgrey", RGB_COLOR(211,211,211) },
+    { "lightpink", RGB_COLOR(255,182,193) },
+    { "lightsalmon", RGB_COLOR(255,160,122) },
+    { "lightseagreen", RGB_COLOR(32,178,170) },
+    { "lightskyblue", RGB_COLOR(135,206,250) },
+    { "lightslategray", RGB_COLOR(119,136,153) },
+    { "lightslategrey", RGB_COLOR(119,136,153) },
+    { "lightsteelblue", RGB_COLOR(176,196,222) },
+    { "lightyellow", RGB_COLOR(255,255,224) },
+    { "lime", RGB_COLOR(0,255,0) },
+    { "limegreen", RGB_COLOR(50,205,50) },
+    { "linen", RGB_COLOR(250,240,230) },
+    { "magenta", RGB_COLOR(255,0,255) },
+    { "maroon", RGB_COLOR(128,0,0) },
+    { "mediumaquamarine", RGB_COLOR(102,205,170) },
+    { "mediumblue", RGB_COLOR(0,0,205) },
+    { "mediumorchid", RGB_COLOR(186,85,211) },
+    { "mediumpurple", RGB_COLOR(147,112,219) },
+    { "mediumseagreen", RGB_COLOR(60,179,113) },
+    { "mediumslateblue", RGB_COLOR(123,104,238) },
+    { "mediumspringgreen", RGB_COLOR(0,250,154) },
+    { "mediumturquoise", RGB_COLOR(72,209,204) },
+    { "mediumvioletred", RGB_COLOR(199,21,133) },
+    { "midnightblue", RGB_COLOR(25,25,112) },
+    { "mintcream", RGB_COLOR(245,255,250) },
+    { "mistyrose", RGB_COLOR(255,228,225) },
+    { "moccasin", RGB_COLOR(255,228,181) },
+    { "navajowhite", RGB_COLOR(255,222,173) },
+    { "navy", RGB_COLOR(0,0,128) },
+    { "oldlace", RGB_COLOR(253,245,230) },
+    { "olive", RGB_COLOR(128,128,0) },
+    { "olivedrab", RGB_COLOR(107,142,35) },
+    { "orange", RGB_COLOR(255,165,0) },
+    { "orangered", RGB_COLOR(255,69,0) },
+    { "orchid", RGB_COLOR(218,112,214) },
+    { "palegoldenrod", RGB_COLOR(238,232,170) },
+    { "palegreen", RGB_COLOR(152,251,152) },
+    { "paleturquoise", RGB_COLOR(175,238,238) },
+    { "palevioletred", RGB_COLOR(219,112,147) },
+    { "papayawhip", RGB_COLOR(255,239,213) },
+    { "peachpuff", RGB_COLOR(255,218,185) },
+    { "peru", RGB_COLOR(205,133,63) },
+    { "pink", RGB_COLOR(255,192,203) },
+    { "plum", RGB_COLOR(221,160,221) },
+    { "powderblue", RGB_COLOR(176,224,230) },
+    { "purple", RGB_COLOR(128,0,128) },
+    { "red", RGB_COLOR(255,0,0) },
+    { "rosybrown", RGB_COLOR(188,143,143) },
+    { "royalblue", RGB_COLOR(65,105,225) },
+    { "saddlebrown", RGB_COLOR(139,69,19) },
+    { "salmon", RGB_COLOR(250,128,114) },
+    { "sandybrown", RGB_COLOR(244,164,96) },
+    { "seagreen", RGB_COLOR(46,139,87) },
+    { "seashell", RGB_COLOR(255,245,238) },
+    { "sienna", RGB_COLOR(160,82,45) },
+    { "silver", RGB_COLOR(192,192,192) },
+    { "skyblue", RGB_COLOR(135,206,235) },
+    { "slateblue", RGB_COLOR(106,90,205) },
+    { "slategray", RGB_COLOR(112,128,144) },
+    { "slategrey", RGB_COLOR(112,128,144) },
+    { "snow", RGB_COLOR(255,250,250) },
+    { "springgreen", RGB_COLOR(0,255,127) },
+    { "steelblue", RGB_COLOR(70,130,180) },
+    { "tan", RGB_COLOR(210,180,140) },
+    { "teal", RGB_COLOR(0,128,128) },
+    { "thistle", RGB_COLOR(216,191,216) },
+    { "tomato", RGB_COLOR(255,99,71) },
+    { "turquoise", RGB_COLOR(64,224,208) },
+    { "violet", RGB_COLOR(238,130,238) },
+    { "wheat", RGB_COLOR(245,222,179) },
+    { "white", RGB_COLOR(255,255,255) },
+    { "whitesmoke", RGB_COLOR(245,245,245) },
+    { "yellow", RGB_COLOR(255,255,0) },
+    { "yellowgreen", RGB_COLOR(154,205,50) },
+    { 0, { 0, 0, 0, 0 } }  /* Terminator.  */
+};
+
+/* Get the color named NAME.  If the color was found, returns 1 and
+   stores the color into *COLOR.  If the color was not found, returns 0 and
+   does not modify *COLOR.  */
+int
+grub_gui_get_named_color (const char *name,
+                          grub_gui_color_t *color)
+{
+  int i;
+  for (i = 0; named_colors[i].name; i++)
+    {
+      if (grub_strcmp (named_colors[i].name, name) == 0)
+        {
+          *color = named_colors[i].color;
+          return 1;
+        }
+    }
+  return 0;
+}

=== added file 'gfxmenu/theme_loader.c'
--- gfxmenu/theme_loader.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/theme_loader.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,609 @@
+/* theme_loader.c - Theme file loader for gfxmenu.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/video.h>
+#include <grub/gui_string_util.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/gfxwidgets.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gui.h>
+
+/* Construct a new box widget using ABSPATTERN to find the pixmap files for
+   it, storing the new box instance at *BOXPTR.
+   PATTERN should be of the form: "(hd0,0)/somewhere/style*.png".
+   The '*' then gets substituted with the various pixmap names that the
+   box uses.
+
+   Returns zero on failure, nonzero on success.
+ */
+static int
+recreate_box_absolute (grub_gfxmenu_box_t *boxptr, const char *abspattern)
+{
+  char *prefix;
+  char *suffix;
+  char *star;
+  grub_gfxmenu_box_t box;
+
+  star = grub_strchr (abspattern, '*');
+  if (! star)
+    return 0;
+
+  /* Prefix:  Get the part before the '*'.  */
+  prefix = grub_malloc (star - abspattern + 1);
+  if (! prefix)
+    return 0;
+
+  grub_memcpy (prefix, abspattern, star - abspattern);
+  prefix[star - abspattern] = '\0';
+
+  /* Suffix:  Everything after the '*' is the suffix.  */
+  suffix = star + 1;
+
+  box = grub_gfxmenu_create_box (prefix, suffix);
+  grub_free (prefix);
+  if (! box)
+    return 0;
+
+  if (*boxptr)
+    (*boxptr)->destroy (*boxptr);
+  *boxptr = box;
+  return 1;
+}
+
+
+/* Construct a new box widget using PATTERN to find the pixmap files for it,
+   storing the new widget at *BOXPTR.  PATTERN should be of the form:
+   "somewhere/style*.png".  The '*' then gets substituted with the various
+   pixmap names that the widget uses.
+
+   Important!  The value of *BOXPTR must be initialized!  It must either
+   (1) Be 0 (a NULL pointer), or
+   (2) Be a pointer to a valid 'grub_gfxmenu_box_t' instance.
+   In this case, the previous instance is destroyed.
+
+   Returns zero on failure, nonzero on success.
+ */
+int
+grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
+                       const char *pattern, const char *theme_dir)
+{
+  char *abspattern;
+  int success;
+
+  /* Check arguments.  */
+  if (! pattern || ! theme_dir)
+    return 0;
+
+  /* Resolve to an absolute path.  */
+  abspattern = grub_resolve_relative_path (theme_dir, pattern);
+  if (! abspattern)
+    return 0;
+
+  /* Create the box.  */
+  success = recreate_box_absolute (boxptr, abspattern);
+  grub_free (abspattern);
+  return success;
+}
+
+/* Set the specified property NAME on the view to the given string VALUE.
+   This function takes ownership of both NAME and VALUE, so the caller
+   should pass pointers to new heap-allocated strings.  */
+static void
+theme_set_string (grub_gfxmenu_view_t view,
+                  const char *name,
+                  const char *value,
+                  const char *theme_dir)
+{
+  if (! grub_strcmp ("title-font", name))
+    view->title_font = grub_font_get (value);
+  else if (! grub_strcmp ("message-font", name))
+    view->message_font = grub_font_get (value);
+  else if (! grub_strcmp ("terminal-font", name))
+    {
+      grub_free (view->terminal_font_name);
+      view->terminal_font_name = grub_strdup (value);
+    }
+  else if (! grub_strcmp ("title-color", name))
+    view->title_color = grub_gui_parse_color (value);
+  else if (! grub_strcmp ("message-color", name))
+    view->message_color = grub_gui_parse_color (value);
+  else if (! grub_strcmp ("message-bg-color", name))
+    view->message_bg_color = grub_gui_parse_color (value);
+  else if (! grub_strcmp ("desktop-image", name))
+    {
+      struct grub_video_bitmap *raw_bitmap;
+      struct grub_video_bitmap *scaled_bitmap;
+      char *path;
+      path = grub_resolve_relative_path (theme_dir, value);
+      if (! path)
+        return;
+      grub_errno = GRUB_ERR_NONE;
+      if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
+        {
+          grub_errno = GRUB_ERR_NONE;
+          grub_free (path);
+          return;
+        }
+      grub_free(path);
+      grub_video_bitmap_create_scaled (&scaled_bitmap,
+                                       view->screen.width,
+                                       view->screen.height,
+                                       raw_bitmap,
+                                       GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+      grub_video_bitmap_destroy (raw_bitmap);
+      if (!scaled_bitmap)
+        return;
+
+      grub_video_bitmap_destroy (view->desktop_image);
+      view->desktop_image = scaled_bitmap;
+    }
+  else if (! grub_strcmp ("desktop-color", name))
+    view->desktop_color = grub_gui_parse_color (value);
+  else if (! grub_strcmp ("terminal-box", name))
+    grub_gui_recreate_box (&view->terminal_box, value, theme_dir);
+  else if (! grub_strcmp ("title-text", name))
+    {
+      grub_free (view->title_text);
+      view->title_text = grub_strdup (value);
+    }
+}
+
+struct parsebuf
+{
+  char *buf;
+  int pos;
+  int len;
+  char *theme_dir;
+  grub_gfxmenu_view_t view;
+};
+
+static void
+advance_to_next_line (struct parsebuf *p)
+{
+  /* Eat characters up to the newline.  */
+  while (p->pos < p->len && p->buf[p->pos] != '\n')
+    p->pos++;
+  p->pos++;   /* Eat the newline.  */
+}
+
+static void
+skip_whitespace (struct parsebuf *p)
+{
+  while (p->pos < p->len
+         && (p->buf[p->pos] == ' '
+             || p->buf[p->pos] == '\t'
+             || p->buf[p->pos] == '\r'
+             || p->buf[p->pos] == '\n'
+             || p->buf[p->pos] == '\f'))
+    p->pos++;
+}
+
+static int
+read_char (struct parsebuf *p)
+{
+  if (p->pos < p->len)
+    return p->buf[p->pos++];
+  else
+    return -1;
+}
+
+static char *
+read_identifier (struct parsebuf *p)
+{
+  int start;
+  int end;
+
+  skip_whitespace (p);
+  start = p->pos;
+  while (p->pos < p->len
+         && (grub_isalpha(p->buf[p->pos])
+             || grub_isdigit(p->buf[p->pos])
+             || p->buf[p->pos] == '_'
+             || p->buf[p->pos] == '-'))
+    p->pos++;
+  end = p->pos;
+  if (end - start < 1)
+    return 0;
+
+  return grub_new_substring (p->buf, start, end);
+}
+
+static char *
+read_expression (struct parsebuf *p)
+{
+  int start;
+  int end;
+
+  skip_whitespace (p);
+  if (p->pos < p->len && p->buf[p->pos] == '"')
+    {
+      /* Read as a quoted string.  */
+      /* The quotation marks are not included in the expression value.  */
+      p->pos++;
+      start = p->pos;
+      while (p->pos < p->len
+             && p->buf[p->pos] != '"')
+        p->pos++;
+      end = p->pos;
+      if (p->pos < p->len)
+        p->pos++;   /* Skip the terminating quotation mark.  */
+    }
+  else if (p->pos < p->len && p->buf[p->pos] == '(')
+    {
+      /* Read as a parenthesized string -- for tuples/coordinates.  */
+      /* The parentheses are included in the expression value.  */
+      start = p->pos;
+      while (p->pos < p->len)
+        {
+          char c = p->buf[p->pos];
+          p->pos++;
+          if (c == ')')
+            break;
+        }
+      end = p->pos;
+    }
+  else if (p->pos < p->len)
+    {
+      /* Read as a single word -- for numeric values.  */
+      start = p->pos;
+      while (p->pos < p->len
+             && ! grub_isspace (p->buf[p->pos]))
+        p->pos++;
+      end = p->pos;
+    }
+  else
+    return 0;
+
+  return grub_new_substring (p->buf, start, end);
+}
+
+/* Read a GUI object specification from the theme file.
+   Any components created will be added to the GUI container PARENT.
+   Returns 0 on success, nonzero on failure.  */
+static int
+read_object (struct parsebuf *p, grub_gui_container_t parent)
+{
+  char *name;
+  name = read_identifier (p);
+  if (! name)
+    goto fail;
+
+  grub_gui_component_t component = 0;
+  if (grub_strcmp (name, "label") == 0)
+    {
+      component = grub_gui_label_new ();
+    }
+  else if (grub_strcmp (name, "image") == 0)
+    {
+      component = grub_gui_image_new ();
+    }
+  else if (grub_strcmp (name, "vbox") == 0)
+    {
+      component = (grub_gui_component_t) grub_gui_vbox_new ();
+    }
+  else if (grub_strcmp (name, "hbox") == 0)
+    {
+      component = (grub_gui_component_t) grub_gui_hbox_new ();
+    }
+  else if (grub_strcmp (name, "canvas") == 0)
+    {
+      component = (grub_gui_component_t) grub_gui_canvas_new ();
+    }
+  else if (grub_strcmp (name, "progress_bar") == 0)
+    {
+      component = grub_gui_progress_bar_new ();
+    }
+  else if (grub_strcmp (name, "circular_progress") == 0)
+    {
+      component = grub_gui_circular_progress_new ();
+    }
+  else if (grub_strcmp (name, "boot_menu") == 0)
+    {
+      component = grub_gui_list_new ();
+    }
+  else
+    {
+      /* Unknown type; ignore.  */
+      goto fail;   /* We should actually parse the structure.  */
+    }
+
+  if (component)
+    {
+      grub_video_rect_t r = { .x=0, .y=0, .width=-1, .height=-1 };
+      component->ops->set_bounds (component, &r);
+      parent->ops->add (parent, component);
+    }
+
+  skip_whitespace (p);
+  if (read_char (p) != '{')
+    goto fail;
+
+  while (p->pos < p->len)
+    {
+      skip_whitespace (p);
+
+      /* Check whether the end has been encountered.  */
+      if (p->pos < p->len
+          && p->buf[p->pos] == '}')
+        {
+          p->pos++;   /* Skip the closing brace.  */
+          break;
+        }
+
+      if (p->pos < p->len
+          && p->buf[p->pos] == '#')
+        {
+          /* Skip comments.  */
+          advance_to_next_line (p);
+          continue;
+        }
+
+      if (p->pos < p->len
+          && p->buf[p->pos] == '+')
+        {
+          p->pos++;  /* Skip the '+'.  */
+          /* Check whether this component is a container.  */
+          if (component->ops->is_instance (component, "container"))
+            {
+              /* Read the sub-object recursively and add it as a child.  */
+              if (read_object (p, (grub_gui_container_t) component) != 0)
+                goto fail;
+              /* After reading the sub-object, resume parsing, expecting
+                 another property assignment or sub-object definition.  */
+              continue;
+            }
+          else
+            {
+              /* error ("Attempted to add object to non-container.") */
+              goto fail;
+            }
+        }
+
+      char *property;
+      property = read_identifier (p);
+      if (! property)
+        goto fail;
+
+      skip_whitespace (p);
+      if (read_char (p) != '=')
+        {
+          grub_free (property);
+          goto fail;
+        }
+      skip_whitespace (p);
+
+      char *value;
+      value = read_expression (p);
+      if (! value)
+        {
+          grub_free (property);
+          goto fail;
+        }
+
+      /* Handle the property value.  */
+      if (grub_strcmp (property, "position") == 0)
+        {
+          /* Special case for position value.  */
+          int x;
+          int y;
+
+          if (grub_gui_parse_2_tuple (value, &x, &y))
+            {
+              grub_video_rect_t r;
+              component->ops->get_bounds (component, &r);
+              r.x = x;
+              r.y = y;
+              component->ops->set_bounds (component, &r);
+            }
+        }
+      else if (grub_strcmp (property, "size") == 0)
+        {
+          /* Special case for size value.  */
+          int w;
+          int h;
+
+          if (grub_gui_parse_2_tuple (value, &w, &h))
+            {
+              grub_video_rect_t r;
+              component->ops->get_bounds (component, &r);
+              r.width = w;
+              r.height = h;
+              component->ops->set_bounds (component, &r);
+            }
+        }
+      else
+        {
+          /* General property handling.  */
+          component->ops->set_property (component, property, value);
+        }
+
+      grub_free (value);
+      grub_free (property);
+    }
+
+  /* Set the object's size to its preferred size unless the user has
+     explicitly specified the size.  */
+  grub_video_rect_t r;
+  component->ops->get_bounds (component, &r);
+  if (r.width == -1 || r.height == -1)
+    {
+      component->ops->get_preferred_size (component, &r.width, &r.height);
+      component->ops->set_bounds (component, &r);
+    }
+
+  return 0;
+
+fail:
+  grub_free (name);
+
+  /* Once the opening brace has been found, we always jump past the closing
+     brace if an error is encountered.  */
+  while (p->pos < p->len
+         && p->buf[p->pos] != '}')
+    p->pos++;
+  return 1;
+}
+
+static void
+read_property (struct parsebuf *p)
+{
+  char *name;
+
+  name = read_identifier (p);  /* Read the property name.  */
+  if (! name)
+    goto next_line;
+
+  skip_whitespace (p);         /* Skip whitespace before separator.  */
+
+  if (read_char (p) != ':')    /* Read separator.  */
+    goto next_line;
+
+  skip_whitespace (p);         /* Skip whitespace after separator.  */
+
+  /* Get the value based on its type.  */
+  if (p->pos < p->len && p->buf[p->pos] == '"')
+    {
+      /* String value.  (e.g., '"My string"')  */
+      char *value = read_expression (p);
+      if (! value)
+        goto next_line;
+      theme_set_string (p->view, name, value, p->theme_dir);
+      grub_free (value);
+    }
+
+next_line:
+  grub_free (name);
+  advance_to_next_line (p);
+}
+
+struct theme_info
+{
+  const char *theme_dir;
+  const char *theme_path;
+};
+
+static void
+update_theme_info_visit (grub_gui_component_t component, void *userdata)
+{
+  struct theme_info *info;
+  info = userdata;
+  component->ops->set_property (component, "theme_dir", info->theme_dir);
+  component->ops->set_property (component, "theme_path", info->theme_path);
+}
+
+/* Update any boot menu components with the current menu model and
+   theme path.  */
+static void
+update_component_tree_theme_info (grub_gui_component_t root,
+                                  const char *theme_dir,
+                                  const char *theme_path)
+{
+  struct theme_info info;
+  info.theme_dir = theme_dir;
+  info.theme_path = theme_path;
+  grub_gui_iterate_recursively (root, update_theme_info_visit, &info);
+}
+
+/* Set properties on the view based on settings from the specified
+   theme file.  Returns nonzero on success, zero on failure.  */
+int
+grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path)
+{
+  grub_file_t file;
+  struct parsebuf p;
+
+  p.view = view;
+  p.theme_dir = grub_get_dirname (theme_path);
+
+  file = grub_file_open (theme_path);
+  if (!file)
+    {
+      grub_free (p.theme_dir);
+      return 0;
+    }
+
+  p.len = grub_file_size (file);
+  p.buf = grub_malloc (p.len);
+  if (! p.buf)
+    {
+      grub_file_close (file);
+      grub_free (p.theme_dir);
+      return 0;
+    }
+  if (grub_file_read (file, p.buf, p.len) != p.len)
+    {
+      grub_free (p.buf);
+      grub_file_close (file);
+      grub_free (p.theme_dir);
+      return 0;
+    }
+
+  if (view->canvas)
+    view->canvas->ops->component.destroy (view->canvas);
+
+  view->canvas = grub_gui_canvas_new ();
+  ((grub_gui_component_t) view->canvas)
+    ->ops->set_bounds ((grub_gui_component_t) view->canvas,
+                       &view->screen);
+
+  p.pos = 0;
+  while (p.pos < p.len)
+    {
+      /* Skip comments (lines beginning with #).  */
+      if (p.pos < p.len && p.buf[p.pos] == '#')
+        {
+          advance_to_next_line (&p);
+          continue;
+        }
+
+      /* Get name.  */
+      /* Find the first non-whitespace character.  */
+      skip_whitespace (&p);
+
+      /* Handle the content.  */
+      if (p.buf[p.pos] == '+')
+        {
+          p.pos++;   /* Skip the '+'.  */
+          read_object (&p, view->canvas);
+        }
+      else
+        {
+          read_property (&p);
+        }
+    }
+
+  /* Set the new theme path.  */
+  grub_free (view->theme_path);
+  view->theme_path = grub_strdup (theme_path);
+  update_component_tree_theme_info ((grub_gui_component_t) view->canvas,
+                                    p.theme_dir,
+                                    theme_path);
+
+  grub_free (p.buf);
+  grub_file_close (file);
+  grub_free (p.theme_dir);
+  return 1;
+}

=== added file 'gfxmenu/view.c'
--- gfxmenu/view.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/view.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,505 @@
+/* view.c - Graphical menu interface MVC view. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/file.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/video.h>
+#include <grub/gui_string_util.h>
+#include <grub/gfxterm.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/term.h>
+#include <grub/gfxwidgets.h>
+#include <grub/time.h>
+#include <grub/menu.h>
+#include <grub/menu_viewer.h>
+#include <grub/gfxmenu_view.h>
+#include <grub/gui.h>
+#include <grub/icon_manager.h>
+
+/* The component ID identifying GUI components to be updated as the timeout
+   status changes.  */
+#define TIMEOUT_COMPONENT_ID "__timeout__"
+
+static void init_terminal (grub_gfxmenu_view_t view);
+static void destroy_terminal (void);
+static grub_err_t set_graphics_mode (void);
+static grub_err_t set_text_mode (void);
+
+/* Create a new view object, loading the theme specified by THEME_PATH and
+   associating MODEL with the view.  */
+grub_gfxmenu_view_t
+grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model)
+{
+  grub_gfxmenu_view_t view;
+
+  view = grub_malloc (sizeof (*view));
+  if (! view)
+    return 0;
+
+  set_graphics_mode ();
+  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+  grub_video_get_viewport ((unsigned *) &view->screen.x,
+                           (unsigned *) &view->screen.y,
+                           (unsigned *) &view->screen.width,
+                           (unsigned *) &view->screen.height);
+
+  /* Clear the screen; there may be garbage left over in video memory, and
+     loading the menu style (particularly the background) can take a while. */
+  grub_video_fill_rect (grub_video_map_rgb (0, 0, 0),
+                        view->screen.x, view->screen.y,
+                        view->screen.width, view->screen.height);
+  grub_video_swap_buffers ();
+
+  grub_font_t default_font;
+  grub_gui_color_t default_fg_color;
+  grub_gui_color_t default_bg_color;
+
+  default_font = grub_font_get ("Helvetica 12");
+  default_fg_color = grub_gui_color_rgb (0, 0, 0);
+  default_bg_color = grub_gui_color_rgb (255, 255, 255);
+
+  view->model = model;
+  view->canvas = 0;
+
+  view->title_font = default_font;
+  view->message_font = default_font;
+  view->terminal_font_name = grub_strdup ("Fixed 10");
+  view->title_color = default_fg_color;
+  view->message_color = default_bg_color;
+  view->message_bg_color = default_fg_color;
+  view->desktop_image = 0;
+  view->desktop_color = default_bg_color;
+  view->terminal_box = grub_gfxmenu_create_box (0, 0);
+  view->title_text = grub_strdup ("GRUB Boot Menu");
+  view->progress_message_text = 0;
+  view->theme_path = 0;
+
+  if (! grub_gfxmenu_view_load_theme (view, theme_path))
+    {
+      grub_gfxmenu_view_destroy (view);
+      return 0;
+    }
+
+  init_terminal (view);
+
+  return view;
+}
+
+/* Destroy the view object.  All used memory is freed.  */
+void
+grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view)
+{
+  grub_video_bitmap_destroy (view->desktop_image);
+  if (view->terminal_box)
+    view->terminal_box->destroy (view->terminal_box);
+  grub_free (view->terminal_font_name);
+  grub_free (view->title_text);
+  grub_free (view->progress_message_text);
+  grub_free (view->theme_path);
+  if (view->canvas)
+    view->canvas->ops->component.destroy (view->canvas);
+  grub_free (view);
+
+  set_text_mode ();
+  destroy_terminal ();
+}
+
+/* Sets MESSAGE as the progress message for the view.
+   MESSAGE can be 0, in which case no message is displayed.  */
+static void
+set_progress_message (grub_gfxmenu_view_t view, const char *message)
+{
+  grub_free (view->progress_message_text);
+  if (message)
+    view->progress_message_text = grub_strdup (message);
+  else
+    view->progress_message_text = 0;
+}
+
+static void
+draw_background (grub_gfxmenu_view_t view)
+{
+  if (view->desktop_image)
+    {
+      struct grub_video_bitmap *img = view->desktop_image;
+      grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE,
+                              view->screen.x, view->screen.y, 0, 0,
+                              grub_video_bitmap_get_width (img),
+                              grub_video_bitmap_get_height (img));
+    }
+  else
+    {
+      grub_video_fill_rect (grub_gui_map_color (view->desktop_color),
+                            view->screen.x, view->screen.y,
+                            view->screen.width, view->screen.height);
+    }
+}
+
+static void
+draw_title (grub_gfxmenu_view_t view)
+{
+  if (! view->title_text)
+    return;
+
+  /* Center the title. */
+  int title_width = grub_font_get_string_width (view->title_font,
+                                                view->title_text);
+  int x = (view->screen.width - title_width) / 2;
+  int y = 40 + grub_font_get_ascent (view->title_font);
+  grub_font_draw_string (view->title_text,
+                         view->title_font,
+                         grub_gui_map_color (view->title_color),
+                         x, y);
+}
+
+struct progress_value_data
+{
+  const char *visible;
+  const char *start;
+  const char *end;
+  const char *value;
+  const char *text;
+};
+
+static void
+update_timeout_visit (grub_gui_component_t component,
+                      void *userdata)
+{
+  struct progress_value_data *pv;
+  pv = (struct progress_value_data *) userdata;
+  component->ops->set_property (component, "visible", pv->visible);
+  component->ops->set_property (component, "start", pv->start);
+  component->ops->set_property (component, "end", pv->end);
+  component->ops->set_property (component, "value", pv->value);
+  component->ops->set_property (component, "text", pv->text);
+}
+
+static void
+update_timeout (grub_gfxmenu_view_t view)
+{
+  char startbuf[20];
+  char valuebuf[20];
+  char msgbuf[120];
+
+  int timeout = grub_gfxmenu_model_get_timeout_ms (view->model);
+  int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model);
+  struct progress_value_data pv;
+
+  pv.visible = timeout > 0 ? "true" : "false";
+  grub_sprintf (startbuf, "%d", -timeout);
+  pv.start = startbuf;
+  pv.end = "0";
+  grub_sprintf (valuebuf, "%d", remaining > 0 ? -remaining : 0);
+  pv.value = valuebuf;
+
+  int seconds_remaining_rounded_up = (remaining + 999) / 1000;
+  grub_sprintf (msgbuf,
+                "The highlighted entry will be booted automatically in %d s.",
+                seconds_remaining_rounded_up);
+  pv.text = msgbuf;
+
+  grub_gui_find_by_id ((grub_gui_component_t) view->canvas,
+                       TIMEOUT_COMPONENT_ID, update_timeout_visit, &pv);
+}
+
+static void
+update_menu_visit (grub_gui_component_t component,
+                   void *userdata)
+{
+  grub_gfxmenu_view_t view;
+  view = userdata;
+  if (component->ops->is_instance (component, "list"))
+    {
+      grub_gui_list_t list = (grub_gui_list_t) component;
+      list->ops->set_view_info (list, view->theme_path, view->model);
+    }
+}
+
+/* Update any boot menu components with the current menu model and
+   theme path.  */
+static void
+update_menu_components (grub_gfxmenu_view_t view)
+{
+  grub_gui_iterate_recursively ((grub_gui_component_t) view->canvas,
+                                update_menu_visit, view);
+}
+
+static void
+draw_message (grub_gfxmenu_view_t view)
+{
+  char *text = view->progress_message_text;
+  if (! text)
+    return;
+
+  grub_font_t font = view->message_font;
+  grub_video_color_t color = grub_gui_map_color (view->message_color);
+
+  /* Set the timeout bar's frame.  */
+  grub_video_rect_t f;
+  f.width = view->screen.width * 4 / 5;
+  f.height = 50;
+  f.x = view->screen.x + (view->screen.width - f.width) / 2;
+  f.y = view->screen.y + view->screen.height - 90 - 20 - f.height;
+
+  /* Border.  */
+  grub_video_fill_rect (color,
+                        f.x-1, f.y-1, f.width+2, f.height+2);
+  /* Fill.  */
+  grub_video_fill_rect (grub_gui_map_color (view->message_bg_color),
+                        f.x, f.y, f.width, f.height);
+
+  /* Center the text. */
+  int text_width = grub_font_get_string_width (font, text);
+  int x = f.x + (f.width - text_width) / 2;
+  int y = (f.y + (f.height - grub_font_get_descent (font)) / 2
+           + grub_font_get_ascent (font) / 2);
+  grub_font_draw_string (text, font, color, x, y);
+}
+
+
+void
+grub_gfxmenu_view_draw (grub_gfxmenu_view_t view)
+{
+  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+  update_timeout (view);
+  update_menu_components (view);
+
+  draw_background (view);
+  if (view->canvas)
+    view->canvas->ops->component.paint (view->canvas);
+  draw_title (view);
+  draw_message (view);
+}
+
+static grub_err_t
+set_graphics_mode (void)
+{
+  const char *doublebuf_str = grub_env_get ("doublebuffering");
+  int doublebuf_flags =
+    (doublebuf_str && doublebuf_str[0] == 'n')
+    ? 0
+    : GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+
+  const char *modestr = grub_env_get ("gfxmode");
+  if (grub_video_setup_preferred_mode (modestr, doublebuf_flags, 640, 480)
+      != GRUB_ERR_NONE)
+    return grub_errno;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+set_text_mode (void)
+{
+  return grub_video_restore ();
+}
+
+static int term_target_width;
+static int term_target_height;
+static struct grub_video_render_target *term_target;
+static int term_initialized;
+static grub_term_output_t term_original;
+static grub_gfxmenu_view_t term_view;
+
+static void
+repaint_terminal (int x __attribute ((unused)),
+                  int y __attribute ((unused)),
+                  int width __attribute ((unused)),
+                  int height __attribute ((unused)))
+{
+  if (! term_view)
+    return;
+
+  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+  grub_gfxmenu_view_draw (term_view);
+  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
+  int termx = term_view->screen.x
+    + term_view->screen.width * (10 - 7) / 10 / 2;
+  int termy = term_view->screen.y
+    + term_view->screen.height * (10 - 7) / 10 / 2;
+
+  grub_gfxmenu_box_t term_box = term_view->terminal_box;
+  if (term_box)
+    {
+      term_box->set_content_size (term_box,
+                                  term_target_width, term_target_height);
+
+      term_box->draw (term_box,
+                      termx - term_box->get_left_pad (term_box),
+                      termy - term_box->get_top_pad (term_box));
+    }
+
+  grub_video_blit_render_target (term_target, GRUB_VIDEO_BLIT_REPLACE,
+                                 termx, termy,
+                                 0, 0, term_target_width, term_target_height);
+  grub_video_swap_buffers ();
+}
+
+static void
+init_terminal (grub_gfxmenu_view_t view)
+{
+  term_original = grub_term_get_current_output ();
+
+  term_target_width = view->screen.width * 7 / 10;
+  term_target_height = view->screen.height * 7 / 10;
+
+  grub_video_create_render_target (&term_target,
+                                   term_target_width,
+                                   term_target_height,
+                                   GRUB_VIDEO_MODE_TYPE_RGB
+                                   | GRUB_VIDEO_MODE_TYPE_ALPHA);
+  if (grub_errno != GRUB_ERR_NONE)
+    return;
+
+  /* Note: currently there is no API for changing the gfxterm font
+     on the fly, so whatever font the initially loaded theme specifies
+     will be permanent.  */
+  grub_gfxterm_init_window (term_target, 0, 0,
+                            term_target_width, term_target_height,
+                            view->terminal_font_name, 3);
+  if (grub_errno != GRUB_ERR_NONE)
+    return;
+  term_initialized = 1;
+
+  /* XXX: store static pointer to the 'view' object so the repaint callback can access it.  */
+  term_view = view;
+  grub_gfxterm_set_repaint_callback (repaint_terminal);
+  grub_term_set_current_output (grub_gfxterm_get_term ());
+}
+
+static void destroy_terminal (void)
+{
+  term_view = 0;
+  if (term_initialized)
+    grub_gfxterm_destroy_window ();
+  grub_gfxterm_set_repaint_callback (0);
+  if (term_target)
+    grub_video_delete_render_target (term_target);
+  if (term_original)
+    grub_term_set_current_output (term_original);
+}
+
+
+static void
+notify_booting (void *userdata, grub_menu_entry_t entry)
+{
+  grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
+
+  char *s = grub_malloc (100 + grub_strlen (entry->title));
+  if (!s)
+    return;
+
+  grub_sprintf (s, "Booting '%s'", entry->title);
+  set_progress_message (view, s);
+  grub_free (s);
+  grub_gfxmenu_view_draw (view);
+  grub_video_swap_buffers ();
+}
+
+static void
+notify_fallback (void *userdata, grub_menu_entry_t entry)
+{
+  grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata;
+
+  char *s = grub_malloc (100 + grub_strlen (entry->title));
+  if (!s)
+    return;
+
+  grub_sprintf (s, "Falling back to '%s'", entry->title);
+  set_progress_message (view, s);
+  grub_free (s);
+  grub_gfxmenu_view_draw (view);
+  grub_video_swap_buffers ();
+}
+
+static void
+notify_execution_failure (void *userdata __attribute__ ((unused)))
+{
+}
+
+
+static struct grub_menu_execute_callback execute_callback =
+{
+  .notify_booting = notify_booting,
+  .notify_fallback = notify_fallback,
+  .notify_failure = notify_execution_failure
+};
+
+int
+grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view,
+                                         grub_menu_entry_t entry)
+{
+  grub_menu_execute_with_fallback (grub_gfxmenu_model_get_menu (view->model),
+                                   entry, &execute_callback, (void *) view);
+
+  if (set_graphics_mode () != GRUB_ERR_NONE)
+    return 0;  /* Failure.  */
+
+  /* If we returned, there was a failure.  */
+  set_progress_message (view,
+                        "Unable to automatically boot.  "
+                        "Press SPACE to continue.");
+  grub_gfxmenu_view_draw (view);
+  grub_video_swap_buffers ();
+  while (GRUB_TERM_ASCII_CHAR(grub_getkey ()) != ' ')
+    {
+      /* Wait for SPACE to be pressed.  */
+    }
+
+  set_progress_message (view, 0);   /* Clear the message.  */
+
+  return 1;   /* Ok.  */
+}
+
+int
+grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view,
+                                 grub_menu_entry_t entry)
+{
+  /* Currently we switch back to text mode by restoring
+     the original terminal before executing the menu entry.
+     It is hard to make it work when executing a menu entry
+     that switches video modes -- it using gfxterm in a
+     window, the repaint callback seems to crash GRUB. */
+  /* TODO: Determine if this works when 'gfxterm' was set as
+     the current terminal before invoking the gfxmenu. */
+  destroy_terminal ();
+
+  grub_menu_execute_entry (entry);
+  if (grub_errno != GRUB_ERR_NONE)
+    grub_wait_after_message ();
+
+  if (set_graphics_mode () != GRUB_ERR_NONE)
+    return 0;  /* Failure.  */
+
+  init_terminal (view);
+  return 1;   /* Ok.  */
+}
+
+void
+grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view __attribute__((unused)))
+{
+  grub_cmdline_run (1);
+}

=== added file 'gfxmenu/widget-box.c'
--- gfxmenu/widget-box.c	1970-01-01 00:00:00 +0000
+++ gfxmenu/widget-box.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,271 @@
+/* widget_box.c - Pixmap-stylized box widget. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/bitmap_scale.h>
+#include <grub/gfxwidgets.h>
+
+enum box_pixmaps
+{
+  BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW,
+  BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W,
+  BOX_PIXMAP_CENTER
+};
+
+static const char *box_pixmap_names[] = {
+  /* Corners: */
+  "nw", "ne", "se", "sw",
+  /* Sides: */
+  "n", "e", "s", "w",
+  /* Center: */
+  "c"
+};
+
+#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names))
+
+static int
+get_height (struct grub_video_bitmap *bitmap)
+{
+  if (bitmap)
+    return grub_video_bitmap_get_height (bitmap);
+  else
+    return 0;
+}
+
+static int
+get_width (struct grub_video_bitmap *bitmap)
+{
+  if (bitmap)
+    return grub_video_bitmap_get_width (bitmap);
+  else
+    return 0;
+}
+
+static void
+blit (grub_gfxmenu_box_t self, int pixmap_index, int x, int y)
+{
+  struct grub_video_bitmap *bitmap;
+  bitmap = self->scaled_pixmaps[pixmap_index];
+  if (! bitmap)
+    return;
+  grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND,
+                          x, y, 0, 0,
+                          grub_video_bitmap_get_width (bitmap),
+                          grub_video_bitmap_get_height (bitmap));
+}
+
+static void
+draw (grub_gfxmenu_box_t self, int x, int y)
+{
+  int height_n;
+  int height_s;
+  int height_e;
+  int height_w;
+  int width_n;
+  int width_s;
+  int width_e;
+  int width_w;
+
+  height_n = get_height (self->scaled_pixmaps[BOX_PIXMAP_N]);
+  height_s = get_height (self->scaled_pixmaps[BOX_PIXMAP_S]);
+  height_e = get_height (self->scaled_pixmaps[BOX_PIXMAP_E]);
+  height_w = get_height (self->scaled_pixmaps[BOX_PIXMAP_W]);
+  width_n = get_width (self->scaled_pixmaps[BOX_PIXMAP_N]);
+  width_s = get_width (self->scaled_pixmaps[BOX_PIXMAP_S]);
+  width_e = get_width (self->scaled_pixmaps[BOX_PIXMAP_E]);
+  width_w = get_width (self->scaled_pixmaps[BOX_PIXMAP_W]);
+
+  /* Draw sides.  */
+  blit (self, BOX_PIXMAP_N, x + width_w, y);
+  blit (self, BOX_PIXMAP_S, x + width_w, y + height_n + self->content_height);
+  blit (self, BOX_PIXMAP_E, x + width_w + self->content_width, y + height_n);
+  blit (self, BOX_PIXMAP_W, x, y + height_n);
+
+  /* Draw corners.  */
+  blit (self, BOX_PIXMAP_NW, x, y);
+  blit (self, BOX_PIXMAP_NE, x + width_w + self->content_width, y);
+  blit (self, BOX_PIXMAP_SE,
+        x + width_w + self->content_width,
+        y + height_n + self->content_height);
+  blit (self, BOX_PIXMAP_SW, x, y + height_n + self->content_height);
+
+  /* Draw center.  */
+  blit (self, BOX_PIXMAP_CENTER, x + width_w, y + height_n);
+}
+
+static void
+scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h)
+{
+  struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i];
+  struct grub_video_bitmap *raw = self->raw_pixmaps[i];
+
+  if (raw == 0)
+    return;
+
+  if (w == -1)
+    w = grub_video_bitmap_get_width (raw);
+  if (h == -1)
+    h = grub_video_bitmap_get_height (raw);
+
+  if (*scaled == 0
+      || ((int) grub_video_bitmap_get_width (*scaled) != w)
+      || ((int) grub_video_bitmap_get_height (*scaled) != h))
+    {
+      if (*scaled)
+        {
+          grub_video_bitmap_destroy (*scaled);
+          *scaled = 0;
+        }
+      grub_video_bitmap_create_scaled (scaled, w, h, raw,
+                                       GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+    }
+}
+
+static void
+set_content_size (grub_gfxmenu_box_t self,
+                  int width, int height)
+{
+  self->content_width = width;
+  self->content_height = height;
+  /* Resize sides to match the width and height.  */
+  /* It is assumed that the corners width/height match the adjacent sides.  */
+
+  /* Resize N and S sides to match width.  */
+  scale_pixmap(self, BOX_PIXMAP_N, width, -1);
+  scale_pixmap(self, BOX_PIXMAP_S, width, -1);
+
+  /* Resize E and W sides to match height.  */
+  scale_pixmap(self, BOX_PIXMAP_E, -1, height);
+  scale_pixmap(self, BOX_PIXMAP_W, -1, height);
+
+  /* Don't scale the corners--they are assumed to match the sides. */
+  scale_pixmap(self, BOX_PIXMAP_NW, -1, -1);
+  scale_pixmap(self, BOX_PIXMAP_SW, -1, -1);
+  scale_pixmap(self, BOX_PIXMAP_NE, -1, -1);
+  scale_pixmap(self, BOX_PIXMAP_SE, -1, -1);
+
+  /* Scale the center area. */
+  scale_pixmap(self, BOX_PIXMAP_CENTER, width, height);
+}
+
+static int
+get_left_pad (grub_gfxmenu_box_t self)
+{
+  return get_width (self->raw_pixmaps[BOX_PIXMAP_W]);
+}
+
+static int
+get_top_pad (grub_gfxmenu_box_t self)
+{
+  return get_height (self->raw_pixmaps[BOX_PIXMAP_N]);
+}
+
+static int
+get_right_pad (grub_gfxmenu_box_t self)
+{
+  return get_width (self->raw_pixmaps[BOX_PIXMAP_E]);
+}
+
+static int
+get_bottom_pad (grub_gfxmenu_box_t self)
+{
+  return get_height (self->raw_pixmaps[BOX_PIXMAP_S]);
+}
+
+static void
+destroy (grub_gfxmenu_box_t self)
+{
+  unsigned i;
+  for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+    {
+      if (self->raw_pixmaps[i])
+        grub_video_bitmap_destroy(self->raw_pixmaps[i]);
+      self->raw_pixmaps[i] = 0;
+
+      if (self->scaled_pixmaps[i])
+        grub_video_bitmap_destroy(self->scaled_pixmaps[i]);
+      self->scaled_pixmaps[i] = 0;
+    }
+  grub_free (self->raw_pixmaps);
+  self->raw_pixmaps = 0;
+  grub_free (self->scaled_pixmaps);
+  self->scaled_pixmaps = 0;
+
+  grub_free (self);             /* Free self:  must be the last step! */
+}
+
+
+/* Create a new box.  If PIXMAPS_PREFIX and PIXMAPS_SUFFIX are both non-null,
+   then an attempt is made to load the north, south, east, west, northwest,
+   northeast, southeast, southwest, and center pixmaps.
+   If either PIXMAPS_PREFIX or PIXMAPS_SUFFIX is 0, then no pixmaps are
+   loaded, and the box has zero-width borders and is drawn transparent.  */
+grub_gfxmenu_box_t
+grub_gfxmenu_create_box (const char *pixmaps_prefix,
+                         const char *pixmaps_suffix)
+{
+  char path[200];
+  unsigned i;
+  grub_gfxmenu_box_t box;
+
+  box = (grub_gfxmenu_box_t) grub_malloc (sizeof (*box));
+  if (!box)
+    return 0;
+  box->content_width = 0;
+  box->content_height = 0;
+  box->raw_pixmaps =
+    (struct grub_video_bitmap **)
+    grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *));
+  box->scaled_pixmaps =
+    (struct grub_video_bitmap **)
+    grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *));
+
+  for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+    {
+      if (pixmaps_prefix && pixmaps_suffix)
+        {
+          /* TODO XXX dynamically allocate PATH, ensure it's large enough */
+          grub_sprintf (path, "%s%s%s",
+                        pixmaps_prefix, box_pixmap_names[i], pixmaps_suffix);
+          grub_errno = GRUB_ERR_NONE;
+          grub_video_bitmap_load (&box->raw_pixmaps[i], path);
+          grub_errno = GRUB_ERR_NONE;   /* Clear any potential error.  */
+        }
+      else
+        {
+          box->raw_pixmaps[i] = 0;
+        }
+      box->scaled_pixmaps[i] = 0;
+    }
+
+  box->draw = draw;
+  box->set_content_size = set_content_size;
+  box->get_left_pad = get_left_pad;
+  box->get_top_pad = get_top_pad;
+  box->get_right_pad = get_right_pad;
+  box->get_bottom_pad = get_bottom_pad;
+  box->destroy = destroy;
+
+  return box;
+}

=== added file 'include/grub/gfxmenu_model.h'
--- include/grub/gfxmenu_model.h	1970-01-01 00:00:00 +0000
+++ include/grub/gfxmenu_model.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,59 @@
+/* gfxmenu_model.h - gfxmenu model interface. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_GFXMENU_MODEL_HEADER
+#define GRUB_GFXMENU_MODEL_HEADER 1
+
+#include <grub/menu.h>
+
+struct grub_gfxmenu_model;   /* Forward declaration of opaque type.  */
+typedef struct grub_gfxmenu_model *grub_gfxmenu_model_t;
+
+
+grub_gfxmenu_model_t grub_gfxmenu_model_new (grub_menu_t menu);
+
+void grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model);
+
+grub_menu_t grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model);
+
+void grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model);
+
+void grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model);
+
+int grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model);
+
+int grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model);
+
+int grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model);
+
+int grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model);
+
+int grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model);
+
+void grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model,
+                                            int index);
+
+const char *grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model,
+                                                int index);
+
+grub_menu_entry_t grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model,
+                                                int index);
+
+#endif /* GRUB_GFXMENU_MODEL_HEADER */
+

=== added file 'include/grub/gfxmenu_view.h'
--- include/grub/gfxmenu_view.h	1970-01-01 00:00:00 +0000
+++ include/grub/gfxmenu_view.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,92 @@
+/* gfxmenu_view.h - gfxmenu view interface. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_GFXMENU_VIEW_HEADER
+#define GRUB_GFXMENU_VIEW_HEADER 1
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/menu.h>
+#include <grub/font.h>
+#include <grub/gfxmenu_model.h>
+#include <grub/gfxwidgets.h>
+
+struct grub_gfxmenu_view;   /* Forward declaration of opaque type.  */
+typedef struct grub_gfxmenu_view *grub_gfxmenu_view_t;
+
+
+grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path,
+                                           grub_gfxmenu_model_t model);
+
+void grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view);
+
+/* Set properties on the view based on settings from the specified
+   theme file. */
+int grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view,
+                                  const char *theme_path);
+
+int
+grub_gui_recreate_box (grub_gfxmenu_box_t *boxptr,
+                       const char *pattern, const char *theme_dir);
+
+void grub_gfxmenu_view_draw (grub_gfxmenu_view_t view);
+
+int grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view,
+                                             grub_menu_entry_t entry);
+
+int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view,
+                                     grub_menu_entry_t entry);
+
+void grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view);
+
+
+
+/* Implementation details -- this should not be used outside of the
+   view itself.  */
+
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/gui.h>
+#include <grub/gfxwidgets.h>
+#include <grub/icon_manager.h>
+
+/* Definition of the private representation of the view.  */
+struct grub_gfxmenu_view
+{
+  grub_video_rect_t screen;
+
+  grub_font_t title_font;
+  grub_font_t message_font;
+  char *terminal_font_name;
+  grub_gui_color_t title_color;
+  grub_gui_color_t message_color;
+  grub_gui_color_t message_bg_color;
+  struct grub_video_bitmap *desktop_image;
+  grub_gui_color_t desktop_color;
+  grub_gfxmenu_box_t terminal_box;
+  char *title_text;
+  char *progress_message_text;
+  char *theme_path;
+
+  grub_gui_container_t canvas;
+
+  grub_gfxmenu_model_t model;
+};
+
+#endif /* ! GRUB_GFXMENU_VIEW_HEADER */

=== added file 'include/grub/gfxterm.h'
--- include/grub/gfxterm.h	1970-01-01 00:00:00 +0000
+++ include/grub/gfxterm.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,41 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_GFXTERM_HEADER
+#define GRUB_GFXTERM_HEADER	1
+
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/term.h>
+#include <grub/video.h>
+
+grub_err_t
+grub_gfxterm_init_window (struct grub_video_render_target *target,
+                          int x, int y, int width, int height, 
+                          const char *font_name, int border_width);
+
+void grub_gfxterm_destroy_window (void);
+
+grub_term_output_t grub_gfxterm_get_term (void);
+
+typedef void (*grub_gfxterm_repaint_callback_t)(int x, int y,
+                                                int width, int height);
+
+void grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func);
+
+#endif /* ! GRUB_GFXTERM_HEADER */

=== added file 'include/grub/gfxwidgets.h'
--- include/grub/gfxwidgets.h	1970-01-01 00:00:00 +0000
+++ include/grub/gfxwidgets.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,49 @@
+/* gfxwidgets.h - Widgets for the graphical menu (gfxmenu).  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_GFXWIDGETS_HEADER
+#define GRUB_GFXWIDGETS_HEADER 1
+
+#include <grub/video.h>
+
+typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t;
+
+struct grub_gfxmenu_box
+{
+  /* The size of the content.  */
+  int content_width;
+  int content_height;
+
+  struct grub_video_bitmap **raw_pixmaps;
+  struct grub_video_bitmap **scaled_pixmaps;
+
+  void (*draw) (grub_gfxmenu_box_t self, int x, int y);
+  void (*set_content_size) (grub_gfxmenu_box_t self,
+                            int width, int height);
+  int (*get_left_pad) (grub_gfxmenu_box_t self);
+  int (*get_top_pad) (grub_gfxmenu_box_t self);
+  int (*get_right_pad) (grub_gfxmenu_box_t self);
+  int (*get_bottom_pad) (grub_gfxmenu_box_t self);
+  void (*destroy) (grub_gfxmenu_box_t self);
+};
+
+grub_gfxmenu_box_t grub_gfxmenu_create_box (const char *pixmaps_prefix,
+                                            const char *pixmaps_suffix);
+
+#endif /* ! GRUB_GFXWIDGETS_HEADER */

=== added file 'include/grub/gui.h'
--- include/grub/gui.h	1970-01-01 00:00:00 +0000
+++ include/grub/gui.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,165 @@
+/* gui.h - GUI components header file. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/types.h>
+#include <grub/err.h>
+#include <grub/video.h>
+#include <grub/bitmap.h>
+#include <grub/gfxmenu_model.h>
+
+#ifndef GRUB_GUI_H
+#define GRUB_GUI_H 1
+
+/* A representation of a color.  Unlike grub_video_color_t, this
+   representation is independent of any video mode specifics.  */
+typedef struct grub_gui_color
+{
+  grub_uint8_t red;
+  grub_uint8_t green;
+  grub_uint8_t blue;
+  grub_uint8_t alpha;
+} grub_gui_color_t;
+
+typedef struct grub_gui_component *grub_gui_component_t;
+typedef struct grub_gui_container *grub_gui_container_t;
+typedef struct grub_gui_list *grub_gui_list_t;
+
+typedef void (*grub_gui_component_callback) (grub_gui_component_t component,
+                                             void *userdata);
+
+/* Component interface.  */
+
+struct grub_gui_component_ops
+{
+  void (*destroy) (void *self);
+  const char * (*get_id) (void *self);
+  int (*is_instance) (void *self, const char *type);
+  void (*paint) (void *self);
+  void (*set_parent) (void *self, grub_gui_container_t parent);
+  grub_gui_container_t (*get_parent) (void *self);
+  void (*set_bounds) (void *self, const grub_video_rect_t *bounds);
+  void (*get_bounds) (void *self, grub_video_rect_t *bounds);
+  void (*get_preferred_size) (void *self, int *width, int *height);
+  void (*set_property) (void *self, const char *name, const char *value);
+};
+
+struct grub_gui_container_ops
+{
+  struct grub_gui_component_ops component;
+  void (*add) (void *self, grub_gui_component_t comp);
+  void (*remove) (void *self, grub_gui_component_t comp);
+  void (*iterate_children) (void *self,
+                            grub_gui_component_callback cb, void *userdata);
+};
+
+struct grub_gui_list_ops
+{
+  struct grub_gui_component_ops component_ops;
+  void (*set_view_info) (void *self,
+                         const char *theme_path,
+                         grub_gfxmenu_model_t menu);
+};
+
+struct grub_gui_component
+{
+  struct grub_gui_component_ops *ops;
+};
+
+struct grub_gui_container
+{
+  struct grub_gui_container_ops *ops;
+};
+
+struct grub_gui_list
+{
+  struct grub_gui_list_ops *ops;
+};
+
+
+/* Interfaces to concrete component classes.  */
+
+grub_gui_container_t grub_gui_canvas_new (void);
+grub_gui_container_t grub_gui_vbox_new (void);
+grub_gui_container_t grub_gui_hbox_new (void);
+grub_gui_component_t grub_gui_label_new (void);
+grub_gui_component_t grub_gui_image_new (void);
+grub_gui_component_t grub_gui_progress_bar_new (void);
+grub_gui_component_t grub_gui_list_new (void);
+grub_gui_component_t grub_gui_circular_progress_new (void);
+
+/* Manipulation functions.  */
+
+/* Visit all components with the specified ID.  */
+void grub_gui_find_by_id (grub_gui_component_t root,
+                          const char *id,
+                          grub_gui_component_callback cb,
+                          void *userdata);
+
+/* Visit all components.  */
+void grub_gui_iterate_recursively (grub_gui_component_t root,
+                                   grub_gui_component_callback cb,
+                                   void *userdata);
+
+/* Helper functions.  */
+
+static __inline void
+grub_gui_save_viewport (grub_video_rect_t *r)
+{
+  grub_video_get_viewport ((unsigned *) &r->x,
+                           (unsigned *) &r->y,
+                           (unsigned *) &r->width,
+                           (unsigned *) &r->height);
+}
+
+static __inline void
+grub_gui_restore_viewport (const grub_video_rect_t *r)
+{
+  grub_video_set_viewport (r->x, r->y, r->width, r->height);
+}
+
+/* Set a new viewport relative the the current one, saving the current
+   viewport in OLD so it can be later restored.  */
+static __inline void
+grub_gui_set_viewport (const grub_video_rect_t *r, grub_video_rect_t *old)
+{
+  grub_gui_save_viewport (old);
+  grub_video_set_viewport (old->x + r->x,
+                           old->y + r->y,
+                           r->width,
+                           r->height);
+}
+
+static __inline grub_gui_color_t
+grub_gui_color_rgb (int r, int g, int b)
+{
+  grub_gui_color_t c;
+  c.red = r;
+  c.green = g;
+  c.blue = b;
+  c.alpha = 255;
+  return c;
+}
+
+static __inline grub_video_color_t
+grub_gui_map_color (grub_gui_color_t c)
+{
+  return grub_video_map_rgba (c.red, c.green, c.blue, c.alpha);
+}
+
+#endif /* ! GRUB_GUI_H */

=== added file 'include/grub/gui_string_util.h'
--- include/grub/gui_string_util.h	1970-01-01 00:00:00 +0000
+++ include/grub/gui_string_util.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,39 @@
+/* gui_string_util.h - String utilities for the graphical menu interface. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_GUI_STRING_UTIL_HEADER
+#define GRUB_GUI_STRING_UTIL_HEADER 1
+
+#include <grub/types.h>
+#include <grub/gui.h>
+
+char *grub_new_substring (const char *buf,
+                          grub_size_t start, grub_size_t end);
+
+char *grub_resolve_relative_path (const char *base, const char *path);
+
+char *grub_get_dirname (const char *file_path);
+
+int grub_gui_get_named_color (const char *name, grub_gui_color_t *color);
+
+grub_gui_color_t grub_gui_parse_color (const char *s);
+
+int grub_gui_parse_2_tuple (const char *s, int *px, int *py);
+
+#endif /* GRUB_GUI_STRING_UTIL_HEADER */

=== added file 'include/grub/icon_manager.h'
--- include/grub/icon_manager.h	1970-01-01 00:00:00 +0000
+++ include/grub/icon_manager.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,41 @@
+/* icon_manager.h - gfxmenu icon manager. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_ICON_MANAGER_HEADER
+#define GRUB_ICON_MANAGER_HEADER 1
+
+#include <grub/menu.h>
+#include <grub/bitmap.h>
+
+/* Forward declaration of opaque structure handle type.  */
+typedef struct grub_gfxmenu_icon_manager *grub_gfxmenu_icon_manager_t;
+
+grub_gfxmenu_icon_manager_t grub_gfxmenu_icon_manager_new (void);
+void grub_gfxmenu_icon_manager_destroy (grub_gfxmenu_icon_manager_t mgr);
+void grub_gfxmenu_icon_manager_clear_cache (grub_gfxmenu_icon_manager_t mgr);
+void grub_gfxmenu_icon_manager_set_theme_path (grub_gfxmenu_icon_manager_t mgr,
+                                               const char *path);
+void grub_gfxmenu_icon_manager_set_icon_size (grub_gfxmenu_icon_manager_t mgr,
+                                              int width, int height);
+struct grub_video_bitmap *
+grub_gfxmenu_icon_manager_get_icon (grub_gfxmenu_icon_manager_t mgr,
+                                    grub_menu_entry_t entry);
+
+#endif /* GRUB_ICON_MANAGER_HEADER */
+

=== modified file 'include/grub/menu_viewer.h'
--- include/grub/menu_viewer.h	2009-01-31 09:15:43 +0000
+++ include/grub/menu_viewer.h	2009-01-31 20:43:30 +0000
@@ -36,8 +36,13 @@
 };
 typedef struct grub_menu_viewer *grub_menu_viewer_t;
 
+void grub_menu_viewer_init (void);
+
 void grub_menu_viewer_register (grub_menu_viewer_t viewer);
 
 grub_err_t grub_menu_viewer_show_menu (grub_menu_t menu, int nested);
 
+/* Return nonzero iff the menu viewer should clean up and return ASAP.  */
+int grub_menu_viewer_should_return (void);
+
 #endif /* GRUB_MENU_VIEWER_HEADER */

=== modified file 'include/grub/misc.h'
--- include/grub/misc.h	2009-01-31 16:50:07 +0000
+++ include/grub/misc.h	2009-01-31 20:43:30 +0000
@@ -1,7 +1,7 @@
 /* misc.h - prototypes for misc functions */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2003,2005,2006,2007,2008,2009,2008  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2003,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -53,11 +53,14 @@
 char *EXPORT_FUNC(grub_strstr) (const char *haystack, const char *needle);
 int EXPORT_FUNC(grub_iswordseparator) (int c);
 int EXPORT_FUNC(grub_isspace) (int c);
+int EXPORT_FUNC(grub_iscntrl) (int c);
 int EXPORT_FUNC(grub_isprint) (int c);
 int EXPORT_FUNC(grub_isalpha) (int c);
+int EXPORT_FUNC(grub_isalnum) (int c);
 int EXPORT_FUNC(grub_isgraph) (int c);
 int EXPORT_FUNC(grub_isdigit) (int c);
 int EXPORT_FUNC(grub_tolower) (int c);
+long EXPORT_FUNC(grub_strtol) (const char *str, char **end, int base);
 unsigned long EXPORT_FUNC(grub_strtoul) (const char *str, char **end, int base);
 unsigned long long EXPORT_FUNC(grub_strtoull) (const char *str, char **end, int base);
 char *EXPORT_FUNC(grub_strdup) (const char *s);

=== modified file 'include/grub/normal.h'
--- include/grub/normal.h	2009-01-31 16:48:22 +0000
+++ include/grub/normal.h	2009-01-31 20:43:30 +0000
@@ -114,6 +114,7 @@
                                       void *callback_data);
 void grub_menu_entry_run (grub_menu_entry_t entry);
 void grub_menu_execute_entry(grub_menu_entry_t entry);
+int grub_menu_get_default_entry_index (grub_menu_t menu);
 int grub_menu_get_timeout (void);
 void grub_menu_set_timeout (int timeout);
 void grub_cmdline_run (int nested);

=== added file 'include/grub/trig.h'
--- include/grub/trig.h	1970-01-01 00:00:00 +0000
+++ include/grub/trig.h	2009-01-31 20:43:30 +0000
@@ -0,0 +1,44 @@
+/* trig.h - Trigonometric function support.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_TRIG_HEADER
+#define GRUB_TRIG_HEADER 1
+
+#define GRUB_TRIG_ANGLE_MAX 256
+#define GRUB_TRIG_ANGLE_MASK 255
+#define GRUB_TRIG_FRACTION_SCALE 16384
+
+extern short grub_trig_sintab[];
+extern short grub_trig_costab[];
+
+static __inline int
+grub_sin (int x)
+{
+  x &= GRUB_TRIG_ANGLE_MASK;
+  return grub_trig_sintab[x];
+}
+
+static __inline int
+grub_cos (int x)
+{
+  x &= GRUB_TRIG_ANGLE_MASK;
+  return grub_trig_costab[x];
+}
+
+#endif /* ! GRUB_TRIG_HEADER */

=== modified file 'include/grub/types.h'
--- include/grub/types.h	2009-01-10 13:07:44 +0000
+++ include/grub/types.h	2009-01-31 20:43:30 +0000
@@ -94,10 +94,12 @@
 
 #if GRUB_CPU_SIZEOF_VOID_P == 8
 # define ULONG_MAX 18446744073709551615UL
-# define LONG_MAX 9223372036854775807UL
+# define LONG_MIN (-9223372036854775808UL)      /* -2**63 */
+# define LONG_MAX 9223372036854775807L          /* 2**63 + 1 */
 #else
 # define ULONG_MAX 4294967295UL
-# define LONG_MAX 2147483647UL
+# define LONG_MIN (-2147483648L)                /* -2**31 */
+# define LONG_MAX 2147483647L                   /* 2**31 + 1 */
 #endif
 
 /* The type for representing a file offset.  */

=== modified file 'include/grub/video.h'
--- include/grub/video.h	2009-01-05 17:38:14 +0000
+++ include/grub/video.h	2009-01-31 20:43:30 +0000
@@ -73,7 +73,7 @@
 
     /* When needed, decode color or just use value as is.  */
     GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR,
-    
+
     /* Two color bitmap; bits packed: rows are not padded to byte boundary.  */
     GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED
   };
@@ -309,4 +309,11 @@
 
 grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target);
 
+
+/* Defined in video/setmode.c */
+grub_err_t
+grub_video_setup_preferred_mode (const char *mode_list, int mode_flags,
+                                 int default_width, int default_height);
+
+
 #endif /* ! GRUB_VIDEO_HEADER */

=== modified file 'kern/misc.c'
--- kern/misc.c	2009-01-31 16:50:07 +0000
+++ kern/misc.c	2009-01-31 20:43:30 +0000
@@ -378,6 +378,12 @@
 }
 
 int
+grub_iscntrl (int c)
+{
+  return (c >= 0x00 && c <= 0x1F) || c == 0x7F;
+}
+
+int
 grub_isprint (int c)
 {
   return (c >= ' ' && c <= '~');
@@ -390,6 +396,12 @@
 }
 
 int
+grub_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+int
 grub_isdigit (int c)
 {
   return (c >= '0' && c <= '9');
@@ -410,6 +422,41 @@
   return c;
 }
 
+long
+grub_strtol (const char *str, char **end, int base)
+{
+  int negative = 0;
+
+  while (*str && grub_isspace (*str))
+    str++;
+
+  if (*str == '-')
+    {
+      negative = 1;
+      str++;
+    }
+
+  unsigned long long magnitude;
+  magnitude = grub_strtoull (str, end, base);
+  if (negative)
+    {
+      if (magnitude > -((long long) LONG_MIN))
+        {
+          grub_error (GRUB_ERR_OUT_OF_RANGE, "negative overflow");
+          return LONG_MIN;
+        }
+      return -((long long) magnitude);
+    }
+  else
+    {
+      if (magnitude > LONG_MAX)
+        {
+          grub_error (GRUB_ERR_OUT_OF_RANGE, "positive overflow");
+          return LONG_MAX;
+        }
+      return (long) magnitude;
+    }
+}
 
 unsigned long
 grub_strtoul (const char *str, char **end, int base)

=== added file 'lib/trig.c'
--- lib/trig.c	1970-01-01 00:00:00 +0000
+++ lib/trig.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,83 @@
+/* trig.c - Trigonometric table definitions.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/trig.h>
+
+/* These tables were generated with `gentrigtables.py'.  */
+
+short grub_trig_sintab[] =
+{
+  0,402,804,1205,1606,2006,2404,2801,3196,3590,
+  3981,4370,4756,5139,5520,5897,6270,6639,7005,7366,
+  7723,8076,8423,8765,9102,9434,9760,10080,10394,10702,
+  11003,11297,11585,11866,12140,12406,12665,12916,13160,13395,
+  13623,13842,14053,14256,14449,14635,14811,14978,15137,15286,
+  15426,15557,15679,15791,15893,15986,16069,16143,16207,16261,
+  16305,16340,16364,16379,16384,16379,16364,16340,16305,16261,
+  16207,16143,16069,15986,15893,15791,15679,15557,15426,15286,
+  15137,14978,14811,14635,14449,14256,14053,13842,13623,13395,
+  13160,12916,12665,12406,12140,11866,11585,11297,11003,10702,
+  10394,10080,9760,9434,9102,8765,8423,8076,7723,7366,
+  7005,6639,6270,5897,5520,5139,4756,4370,3981,3590,
+  3196,2801,2404,2006,1606,1205,804,402,0,-402,
+  -804,-1205,-1606,-2006,-2404,-2801,-3196,-3590,-3981,-4370,
+  -4756,-5139,-5520,-5897,-6270,-6639,-7005,-7366,-7723,-8076,
+  -8423,-8765,-9102,-9434,-9760,-10080,-10394,-10702,-11003,-11297,
+  -11585,-11866,-12140,-12406,-12665,-12916,-13160,-13395,-13623,-13842,
+  -14053,-14256,-14449,-14635,-14811,-14978,-15137,-15286,-15426,-15557,
+  -15679,-15791,-15893,-15986,-16069,-16143,-16207,-16261,-16305,-16340,
+  -16364,-16379,-16384,-16379,-16364,-16340,-16305,-16261,-16207,-16143,
+  -16069,-15986,-15893,-15791,-15679,-15557,-15426,-15286,-15137,-14978,
+  -14811,-14635,-14449,-14256,-14053,-13842,-13623,-13395,-13160,-12916,
+  -12665,-12406,-12140,-11866,-11585,-11297,-11003,-10702,-10394,-10080,
+  -9760,-9434,-9102,-8765,-8423,-8076,-7723,-7366,-7005,-6639,
+  -6270,-5897,-5520,-5139,-4756,-4370,-3981,-3590,-3196,-2801,
+  -2404,-2006,-1606,-1205,-804,-402
+};
+
+short grub_trig_costab[] =
+{
+  16384,16379,16364,16340,16305,16261,16207,16143,16069,15986,
+  15893,15791,15679,15557,15426,15286,15137,14978,14811,14635,
+  14449,14256,14053,13842,13623,13395,13160,12916,12665,12406,
+  12140,11866,11585,11297,11003,10702,10394,10080,9760,9434,
+  9102,8765,8423,8076,7723,7366,7005,6639,6270,5897,
+  5520,5139,4756,4370,3981,3590,3196,2801,2404,2006,
+  1606,1205,804,402,0,-402,-804,-1205,-1606,-2006,
+  -2404,-2801,-3196,-3590,-3981,-4370,-4756,-5139,-5520,-5897,
+  -6270,-6639,-7005,-7366,-7723,-8076,-8423,-8765,-9102,-9434,
+  -9760,-10080,-10394,-10702,-11003,-11297,-11585,-11866,-12140,-12406,
+  -12665,-12916,-13160,-13395,-13623,-13842,-14053,-14256,-14449,-14635,
+  -14811,-14978,-15137,-15286,-15426,-15557,-15679,-15791,-15893,-15986,
+  -16069,-16143,-16207,-16261,-16305,-16340,-16364,-16379,-16384,-16379,
+  -16364,-16340,-16305,-16261,-16207,-16143,-16069,-15986,-15893,-15791,
+  -15679,-15557,-15426,-15286,-15137,-14978,-14811,-14635,-14449,-14256,
+  -14053,-13842,-13623,-13395,-13160,-12916,-12665,-12406,-12140,-11866,
+  -11585,-11297,-11003,-10702,-10394,-10080,-9760,-9434,-9102,-8765,
+  -8423,-8076,-7723,-7366,-7005,-6639,-6270,-5897,-5520,-5139,
+  -4756,-4370,-3981,-3590,-3196,-2801,-2404,-2006,-1606,-1205,
+  -804,-402,0,402,804,1205,1606,2006,2404,2801,
+  3196,3590,3981,4370,4756,5139,5520,5897,6270,6639,
+  7005,7366,7723,8076,8423,8765,9102,9434,9760,10080,
+  10394,10702,11003,11297,11585,11866,12140,12406,12665,12916,
+  13160,13395,13623,13842,14053,14256,14449,14635,14811,14978,
+  15137,15286,15426,15557,15679,15791,15893,15986,16069,16143,
+  16207,16261,16305,16340,16364,16379
+};
+

=== modified file 'normal/main.c'
--- normal/main.c	2009-01-31 16:48:22 +0000
+++ normal/main.c	2009-01-31 20:43:30 +0000
@@ -638,6 +638,8 @@
 
   /* This registers some built-in commands.  */
   grub_command_init ();
+
+  grub_menu_viewer_init ();
 }
 
 GRUB_MOD_FINI(normal)

=== modified file 'normal/menu.c'
--- normal/menu.c	2009-01-31 17:29:26 +0000
+++ normal/menu.c	2009-01-31 20:43:30 +0000
@@ -311,6 +311,20 @@
   return entry;
 }
 
+/* Get the default menu entry index.  */
+int
+grub_menu_get_default_entry_index (grub_menu_t menu)
+{
+  int i = get_entry_number ("default");
+
+  /* If DEFAULT_ENTRY is not within the menu entries, fall back to
+     the first entry.  */
+  if (i < 0 || i >= menu->size)
+    i = 0;
+
+  return i;
+}
+
 /* Get the first entry number from the variable NAME, which is a
    space-separated list of nonnegative integers.  The entry number which
    is returned is stripped from the value of NAME.  If no entry number can
@@ -373,12 +387,7 @@
 
   first = 0;
 
-  default_entry = get_entry_number ("default");
-
-  /* If DEFAULT_ENTRY is not within the menu entries, fall back to
-     the first entry.  */
-  if (default_entry < 0 || default_entry >= menu->size)
-    default_entry = 0;
+  default_entry = grub_menu_get_default_entry_index (menu);
 
   /* If timeout is 0, drawing is pointless (and ugly).  */
   if (grub_menu_get_timeout () == 0)
@@ -405,7 +414,7 @@
   if (timeout > 0)
     print_timeout (timeout, offset, 0);
 
-  while (1)
+  while (!grub_menu_viewer_should_return ())
     {
       int c;
       timeout = grub_menu_get_timeout ();
@@ -576,6 +585,10 @@
 		}
 	      goto refresh;
 
+	    case 't':
+	      grub_env_set ("menuviewer", "gfxmenu");
+	      goto refresh;
+
 	    default:
 	      break;
 	    }
@@ -584,7 +597,8 @@
 	}
     }
 
-  /* Never reach here.  */
+  /* Exit menu without activating an item.  This occurs if the user presses
+   * 't', switching to the graphical menu viewer.  */
   return -1;
 }
 

=== modified file 'normal/menu_viewer.c'
--- normal/menu_viewer.c	2009-01-31 09:15:43 +0000
+++ normal/menu_viewer.c	2009-01-31 20:43:30 +0000
@@ -25,6 +25,9 @@
 /* The list of menu viewers.  */
 static grub_menu_viewer_t menu_viewer_list;
 
+static int should_return;
+static int menu_viewer_changed;
+
 void
 grub_menu_viewer_register (grub_menu_viewer_t viewer)
 {
@@ -36,7 +39,7 @@
 {
   const char *selected_name = grub_env_get ("menuviewer");
 
-  /* If none selected, pick the last registered one. */
+  /* If none selected, pick the last registered one.  */
   if (selected_name == 0)
     return menu_viewer_list;
 
@@ -54,10 +57,43 @@
 grub_err_t
 grub_menu_viewer_show_menu (grub_menu_t menu, int nested)
 {
-  grub_menu_viewer_t cur = get_current_menu_viewer ();
-  if (!cur) 
-    return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available.");
-
-  return cur->show_menu (menu, nested);
+  grub_err_t err;
+  int repeat = 0;
+  do
+    {
+      repeat = 0;
+      menu_viewer_changed = 0;
+      grub_menu_viewer_t cur = get_current_menu_viewer ();
+      if (!cur)
+        return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available.");
+
+      should_return = 0;
+      err = cur->show_menu (menu, nested);
+      if (menu_viewer_changed)
+        repeat = 1;
+    }
+  while (repeat);
+  return err;
+}
+
+int
+grub_menu_viewer_should_return (void)
+{
+  return should_return;
+}
+
+static char *
+menuviewer_write_hook (struct grub_env_var *var __attribute__ ((unused)),
+                       const char *val)
+{
+  menu_viewer_changed = 1;
+  should_return = 1;
+  return grub_strdup (val);
+}
+
+void
+grub_menu_viewer_init (void)
+{
+  grub_register_variable_hook ("menuviewer", 0, menuviewer_write_hook);
 }
 

=== modified file 'term/gfxterm.c'
--- term/gfxterm.c	2009-01-19 17:48:34 +0000
+++ term/gfxterm.c	2009-01-31 20:43:30 +0000
@@ -26,12 +26,12 @@
 #include <grub/mm.h>
 #include <grub/env.h>
 #include <grub/video.h>
+#include <grub/gfxterm.h>
 #include <grub/bitmap.h>
 #include <grub/bitmap_scale.h>
 
 #define DEFAULT_VIDEO_WIDTH	640
 #define DEFAULT_VIDEO_HEIGHT	480
-#define DEFAULT_VIDEO_FLAGS	0
 
 #define DEFAULT_BORDER_WIDTH	10
 
@@ -106,10 +106,20 @@
   struct grub_colored_char *text_buffer;
 };
 
+static int refcount;
+static struct grub_video_render_target *render_target;
+static grub_video_rect_t window;
 static struct grub_virtual_screen virtual_screen;
+static grub_gfxterm_repaint_callback_t repaint_callback;
+
+static grub_err_t init_window (struct grub_video_render_target *target,
+                               int x, int y, int width, int height, 
+                               const char *font_name, int border_width);
+
+static void destroy_window (void);
+
 
 static grub_dl_t my_mod;
-static struct grub_video_mode_info mode_info;
 
 static struct grub_video_render_target *text_layer;
 
@@ -235,15 +245,61 @@
 }
 
 static grub_err_t
+init_window (struct grub_video_render_target *target,
+             int x, int y, int width, int height, 
+             const char *font_name, int border_width)
+{
+  /* Clean up any prior instance.  */
+  destroy_window ();
+
+  /* Create virtual screen.  */
+  if (grub_virtual_screen_setup (border_width, border_width, 
+                                 width - 2 * border_width, 
+                                 height - 2 * border_width, 
+                                 font_name) 
+      != GRUB_ERR_NONE)
+    {
+      return grub_errno;
+    }
+
+  /* Set the render target.  */
+  render_target = target;
+
+  /* Set window bounds.  */
+  window.x = x;
+  window.y = y;
+  window.width = width;
+  window.height = height;
+
+  /* Mark whole window as dirty.  */
+  dirty_region_reset ();
+  dirty_region_add (0, 0, width, height);
+
+  return (grub_errno = GRUB_ERR_NONE);
+}
+
+grub_err_t
+grub_gfxterm_init_window (struct grub_video_render_target *target,
+                          int x, int y, int width, int height, 
+                          const char *font_name, int border_width)
+{
+  grub_errno = GRUB_ERR_NONE;
+  if (refcount++ == 0)
+    init_window (target, x, y, width, height, font_name, border_width);
+  return grub_errno;
+}
+
+static grub_err_t
 grub_gfxterm_init (void)
 {
-  char *font_name;
-  char *modevar;
-  int width = DEFAULT_VIDEO_WIDTH;
-  int height = DEFAULT_VIDEO_HEIGHT;
-  int depth = -1;
-  int flags = DEFAULT_VIDEO_FLAGS;
-  grub_video_color_t color;
+  const char *font_name;
+  const char *modevar;
+  struct grub_video_mode_info mode_info;
+
+  /* If gfxterm has already been initialized by calling the init_window
+     function, then leave it alone when it is set as the current terminal. */
+  if (refcount++ != 0)
+    return GRUB_ERR_NONE;
 
   /* Select the font to use. */
   font_name = grub_env_get ("gfxterm_font");
@@ -252,270 +308,74 @@
 
   /* Parse gfxmode environment variable if set.  */
   modevar = grub_env_get ("gfxmode");
-  if (modevar)
-    {
-      char *tmp;
-      char *next_mode;
-      char *current_mode;
-      char *param;
-      char *value;
-      int mode_found = 0;
-
-      /* Take copy of env.var. as we don't want to modify that.  */
-      tmp = grub_strdup (modevar);
-      modevar = tmp;
-
-      if (grub_errno != GRUB_ERR_NONE)
-        return grub_errno;
-        
-      /* Initialize next mode.  */
-      next_mode = modevar;
-      
-      /* Loop until all modes has been tested out.  */
-      while (next_mode != NULL)
-        {
-          /* Use last next_mode as current mode.  */
-          tmp = next_mode;
-          
-          /* Reset video mode settings.  */
-          width = DEFAULT_VIDEO_WIDTH;
-          height = DEFAULT_VIDEO_HEIGHT;
-          depth = -1;
-          flags = DEFAULT_VIDEO_FLAGS;
-        
-          /* Save position of next mode and separate modes.  */
-          next_mode = grub_strchr(next_mode, ';');
-          if (next_mode)
-            {
-              *next_mode = 0;
-              next_mode++;
-            }
-
-          /* Skip whitespace.  */
-          while (grub_isspace (*tmp))
-            tmp++;
-
-          /* Initialize token holders.  */
-          current_mode = tmp;
-          param = tmp;
-          value = NULL;
-
-          /* Parse <width>x<height>[x<depth>]*/
-
-          /* Find width value.  */
-          value = param;
-          param = grub_strchr(param, 'x');
-          if (param == NULL)
-            {
-              grub_err_t rc;
-              
-              /* First setup error message.  */
-              rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
-                               "Invalid mode: %s\n",
-                               current_mode);
-              
-              /* Free memory before returning.  */
-              grub_free (modevar);
-              
-              return rc;
-            }
-
-          *param = 0;
-          param++;
-
-          width = grub_strtoul (value, 0, 0);
-          if (grub_errno != GRUB_ERR_NONE)
-            {
-              grub_err_t rc;
-              
-              /* First setup error message.  */
-              rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
-                               "Invalid mode: %s\n",
-                               current_mode);
-              
-              /* Free memory before returning.  */
-              grub_free (modevar);
-              
-              return rc;
-            }
-
-          /* Find height value.  */
-          value = param;
-          param = grub_strchr(param, 'x');
-          if (param == NULL)
-            {
-              height = grub_strtoul (value, 0, 0);
-              if (grub_errno != GRUB_ERR_NONE)
-                {
-                  grub_err_t rc;
-                  
-                  /* First setup error message.  */
-                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
-                                   "Invalid mode: %s\n",
-                                   current_mode);
-                                   
-                  /* Free memory before returning.  */
-                  grub_free (modevar);
-                  
-                  return rc;
-                }
-            }
-          else
-            {
-              /* We have optional color depth value.  */
-              *param = 0;
-              param++;
-
-              height = grub_strtoul (value, 0, 0);
-              if (grub_errno != GRUB_ERR_NONE)
-                {
-                  grub_err_t rc;
-                  
-                  /* First setup error message.  */
-                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
-                                   "Invalid mode: %s\n",
-                                   current_mode);
-                                   
-                  /* Free memory before returning.  */
-                  grub_free (modevar);
-                  
-                  return rc;
-                }
-
-              /* Convert color depth value.  */
-              value = param;
-              depth = grub_strtoul (value, 0, 0);
-              if (grub_errno != GRUB_ERR_NONE)
-                {
-                  grub_err_t rc;
-                  
-                  /* First setup error message.  */
-                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
-                                   "Invalid mode: %s\n",
-                                   current_mode);
-                                   
-                  /* Free memory before returning.  */
-                  grub_free (modevar);
-                  
-                  return rc;
-                }
-            }
-          
-          /* Try out video mode.  */
-          
-          /* If we have 8 or less bits, then assume that it is indexed color mode.  */
-          if ((depth <= 8) && (depth != -1))
-            flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
-
-          /* We have more than 8 bits, then assume that it is RGB color mode.  */
-          if (depth > 8)
-            flags |= GRUB_VIDEO_MODE_TYPE_RGB;
-
-          /* If user requested specific depth, forward that information to driver.  */
-          if (depth != -1)
-            flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
-                     & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
-
-          /* Try to initialize requested mode.  Ignore any errors.  */
-          grub_error_push ();
-          if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
-            {
-              grub_error_pop ();
-              continue;
-            }
-            
-          /* Figure out what mode we ended up.  */
-          if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
-            {
-              /* Couldn't get video mode info, restore old mode and continue to next one.  */
-              grub_error_pop ();
-              
-              grub_video_restore ();
-              continue;
-            }
-          
-          /* Restore state of error stack.  */
-          grub_error_pop ();
-          
-          /* Mode found!  Exit loop.  */
-          mode_found = 1;
-          break;
-        }
-
-      /* Free memory.  */
-      grub_free (modevar);
-      
-      if (!mode_found)
-        return grub_error (GRUB_ERR_BAD_ARGUMENT,
-                           "No suitable mode found.");
-    }
-  else
-    {
-      /* No gfxmode variable set, use defaults.  */
-      
-      /* If we have 8 or less bits, then assume that it is indexed color mode.  */
-      if ((depth <= 8) && (depth != -1))
-        flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
-
-      /* We have more than 8 bits, then assume that it is RGB color mode.  */
-      if (depth > 8)
-        flags |= GRUB_VIDEO_MODE_TYPE_RGB;
-
-      /* If user requested specific depth, forward that information to driver.  */
-      if (depth != -1)
-        flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
-                 & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
-
-      /* Initialize user requested mode.  */
-      if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
-        return grub_errno;
-
-      /* Figure out what mode we ended up.  */
-      if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
-        {
-          grub_video_restore ();
-          return grub_errno;
-        }
+  if (grub_video_setup_preferred_mode (modevar, 0, 
+                                       DEFAULT_VIDEO_WIDTH, 
+                                       DEFAULT_VIDEO_HEIGHT) 
+      != GRUB_ERR_NONE)
+    return grub_errno;
+
+  /* Figure out what mode we ended up.  */
+  if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
+    {
+      grub_video_restore ();
+      return grub_errno;
     }
 
   /* Make sure screen is black.  */
-  color = grub_video_map_rgb (0, 0, 0);
-  grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
+  grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), 
+                        0, 0, mode_info.width, mode_info.height);
   bitmap = 0;
 
+  /* Select the font to use.  */
+  font_name = grub_env_get ("gfxterm_font");
+  if (!font_name)
+    font_name = "";   /* Allow fallback to any font.  */
+
   /* Leave borders for virtual screen.  */
-  width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH);
-  height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH);
-
-  /* Create virtual screen.  */
-  if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH,
-                                 width, height, font_name) != GRUB_ERR_NONE)
+  if (init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY,
+                   0, 0, mode_info.width, mode_info.height,
+                   font_name, 
+                   DEFAULT_BORDER_WIDTH) != GRUB_ERR_NONE)
     {
       grub_video_restore ();
       return grub_errno;
     }
 
-  /* Mark whole screen as dirty.  */
-  dirty_region_reset ();
-  dirty_region_add (0, 0, mode_info.width, mode_info.height);
-
   return (grub_errno = GRUB_ERR_NONE);
 }
 
+static void
+destroy_window (void)
+{
+  if (bitmap)
+    {
+      grub_video_bitmap_destroy (bitmap);
+      bitmap = 0;
+    }
+
+  repaint_callback = 0;
+  grub_virtual_screen_free ();
+}
+
+void
+grub_gfxterm_destroy_window (void)
+{
+  if (--refcount == 0)
+    destroy_window ();
+}
+
 static grub_err_t
 grub_gfxterm_fini (void)
 {
-  if (bitmap)
+  /* Don't destroy an explicitly initialized terminal instance when it is
+     unset as the current terminal.  */
+  if (--refcount == 0)
     {
-      grub_video_bitmap_destroy (bitmap);
-      bitmap = 0;
+      destroy_window ();
+      grub_video_restore ();
     }
 
-  grub_virtual_screen_free ();
-
-  grub_video_restore ();
-
-  return GRUB_ERR_NONE;
+  return (grub_errno = GRUB_ERR_NONE);
 }
 
 static void
@@ -523,9 +383,15 @@
                     unsigned int width, unsigned int height)
 {
   grub_video_color_t color;
-
-  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
-
+  grub_video_rect_t saved_view;
+
+  grub_video_set_active_render_target (render_target);
+  /* Save viewport and set it to our window. */
+  grub_video_get_viewport ((unsigned *) &saved_view.x, 
+                           (unsigned *) &saved_view.y, 
+                           (unsigned *) &saved_view.width, 
+                           (unsigned *) &saved_view.height);
+  grub_video_set_viewport (window.x, window.y, window.width, window.height);
 
   if (bitmap)
     {
@@ -592,6 +458,14 @@
                                      y - virtual_screen.offset_y,
                                      width, height);      
     }
+
+  /* Restore saved viewport. */
+  grub_video_set_viewport (saved_view.x, saved_view.y,
+                           saved_view.width, saved_view.height);
+  grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+  
+  if (repaint_callback)
+    repaint_callback (x, y, width, height);
 }
 
 static void
@@ -800,7 +674,16 @@
       dirty_region_add_virtualscreen ();
     }
   else
-    {      
+    {
+      grub_video_rect_t saved_view;
+      grub_video_set_active_render_target (render_target);
+      /* Save viewport and set it to our window.  */
+      grub_video_get_viewport ((unsigned *) &saved_view.x, 
+                               (unsigned *) &saved_view.y, 
+                               (unsigned *) &saved_view.width, 
+                               (unsigned *) &saved_view.height);
+      grub_video_set_viewport (window.x, window.y, window.width, window.height);
+
       /* Clear new border area.  */
       grub_video_fill_rect (color,
                             virtual_screen.offset_x, virtual_screen.offset_y,
@@ -809,10 +692,18 @@
       /* Scroll physical screen.  */
       grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
 
+      /* Restore saved viewport. */
+      grub_video_set_viewport (saved_view.x, saved_view.y,
+                               saved_view.width, saved_view.height);
+      grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+
       /* Draw cursor if visible.  */
       if (virtual_screen.cursor_state)
 	draw_cursor (1);
     }
+
+  if (repaint_callback)
+    repaint_callback (window.x, window.y, window.width, window.height);
 }
 
 static void
@@ -952,7 +843,7 @@
 }
 
 static grub_ssize_t
-grub_gfxterm_getcharwidth (grub_uint32_t c)
+grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused)))
 {
   struct grub_font_glyph *glyph;
   unsigned char char_width;
@@ -1027,7 +918,8 @@
   /* Clear text layer.  */
   grub_video_set_active_render_target (text_layer);
   color = virtual_screen.bg_color;
-  grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height);
+  grub_video_fill_rect (color, 0, 0,
+                        virtual_screen.width, virtual_screen.height);
   grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
 
   /* Mark virtual screen to be redrawn.  */
@@ -1096,6 +988,11 @@
   dirty_region_redraw ();
 }
 
+void 
+grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func)
+{
+  repaint_callback = func;
+}
 
 /* Option array indices. */
 #define BACKGROUND_CMD_ARGINDEX_MODE 0
@@ -1123,7 +1020,7 @@
       
       /* Mark whole screen as dirty.  */
       dirty_region_reset ();
-      dirty_region_add (0, 0, mode_info.width, mode_info.height);
+      dirty_region_add (0, 0, window.width, window.height);
     }
 
   /* If filename was provided, try to load that.  */
@@ -1139,15 +1036,15 @@
         || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg,
                         "stretch") == 0)
         {
-          if (mode_info.width != grub_video_bitmap_get_width (bitmap)
-              || mode_info.height != grub_video_bitmap_get_height (bitmap)) 
+          if (window.width != (int) grub_video_bitmap_get_width (bitmap)
+              || window.height != (int) grub_video_bitmap_get_height (bitmap)) 
             {
               struct grub_video_bitmap *scaled_bitmap;
               grub_video_bitmap_create_scaled (&scaled_bitmap,
-                                              mode_info.width, 
-                                              mode_info.height,
-                                              bitmap,
-                                              GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+                                               window.width, 
+                                               window.height,
+                                               bitmap,
+                                               GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
               if (grub_errno == GRUB_ERR_NONE)
                 {
                   /* Replace the original bitmap with the scaled one. */
@@ -1167,7 +1064,7 @@
         
         /* Mark whole screen as dirty.  */
         dirty_region_reset ();
-        dirty_region_add (0, 0, mode_info.width, mode_info.height);
+        dirty_region_add (0, 0, window.width, window.height);
       }
     }
   
@@ -1196,9 +1093,16 @@
     .next = 0
   };
 
+grub_term_output_t
+grub_gfxterm_get_term (void)
+{
+  return &grub_video_term;
+}
+
 GRUB_MOD_INIT(term_gfxterm)
 {
   my_mod = mod;
+  refcount = 0;
   grub_term_register_output (&grub_video_term);
 
   grub_register_command ("background_image",

=== added file 'video/setmode.c'
--- video/setmode.c	1970-01-01 00:00:00 +0000
+++ video/setmode.c	2009-01-31 20:43:30 +0000
@@ -0,0 +1,249 @@
+/* video/setmode.c - Smart video mode selection based on preferences. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/video.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+/* Set the video mode based on the preferred modes specified in MODE_LIST in
+   the form: <width>x<height>[x<depth>][;...]
+
+   For example:  640x480;800x600x8;400x300x32
+
+   If MODE_LIST is null, or no modes in it are usable, then DEFAULT_WIDTH and
+   DEFAULT_HEIGHT are used to set the mode.  The MODE_FLAGS argument determines
+   the video mode flags such as double buffering that are used.  */
+
+grub_err_t
+grub_video_setup_preferred_mode (const char *mode_list, int mode_flags,
+                                 int default_width, int default_height)
+{
+  int mode_found = 0;
+
+  if (mode_list != NULL)
+    {
+      /* Take copy of mode_list as we don't want tat.  */
+      char *const modes_copy = grub_strdup (mode_list);
+      if (modes_copy == NULL)
+        return grub_errno;
+
+      /* Initialize next mode.  */
+      char *next_mode = modes_copy;
+
+      /* Loop until all modes has been tested out.  */
+      while ((next_mode != NULL) && !mode_found)
+        {
+          /* Use last next_mode as current mode.  */
+          char *tmp = next_mode;
+
+          int width = -1;
+          int height = -1;
+          int depth = -1;
+
+          /* Save position of next mode and separate modes.  */
+          next_mode = grub_strchr(next_mode, ';');
+          if (next_mode)
+            {
+              *next_mode = 0;
+              next_mode++;
+            }
+
+          /* Skip whitespace.  */
+          while (grub_isspace (*tmp))
+            tmp++;
+
+          /* Initialize token holders.  */
+          char *current_mode = tmp;
+          char *param = tmp;
+          char *value = NULL;
+
+          /* Parse <width>x<height>[x<depth>]*/
+
+          /* Find width value.  */
+          value = param;
+          param = grub_strchr(param, 'x');
+          if (param == NULL)
+            {
+              grub_err_t rc;
+
+              /* First setup error message.  */
+              rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
+                               "Invalid mode: %s\n",
+                               current_mode);
+
+              /* Free memory before returning.  */
+              grub_free (modes_copy);
+
+              return rc;
+            }
+
+          *param = 0;
+          param++;
+
+          width = grub_strtoul (value, 0, 0);
+          if (grub_errno != GRUB_ERR_NONE)
+            {
+              grub_err_t rc;
+
+              /* First setup error message.  */
+              rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
+                               "Invalid mode: %s\n",
+                               current_mode);
+
+              /* Free memory before returning.  */
+              grub_free (modes_copy);
+
+              return rc;
+            }
+
+          /* Find height value.  */
+          value = param;
+          param = grub_strchr(param, 'x');
+          if (param == NULL)
+            {
+              height = grub_strtoul (value, 0, 0);
+              if (grub_errno != GRUB_ERR_NONE)
+                {
+                  grub_err_t rc;
+
+                  /* First setup error message.  */
+                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
+                                   "Invalid mode: %s\n",
+                                   current_mode);
+
+                  /* Free memory before returning.  */
+                  grub_free (modes_copy);
+
+                  return rc;
+                }
+            }
+          else
+            {
+              /* We have optional color depth value.  */
+              *param = 0;
+              param++;
+
+              height = grub_strtoul (value, 0, 0);
+              if (grub_errno != GRUB_ERR_NONE)
+                {
+                  grub_err_t rc;
+
+                  /* First setup error message.  */
+                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
+                                   "Invalid mode: %s\n",
+                                   current_mode);
+
+                  /* Free memory before returning.  */
+                  grub_free (modes_copy);
+
+                  return rc;
+                }
+
+              /* Convert color depth value.  */
+              value = param;
+              depth = grub_strtoul (value, 0, 0);
+              if (grub_errno != GRUB_ERR_NONE)
+                {
+                  grub_err_t rc;
+
+                  /* First setup error message.  */
+                  rc = grub_error (GRUB_ERR_BAD_ARGUMENT, 
+                                   "Invalid mode: %s\n",
+                                   current_mode);
+
+                  /* Free memory before returning.  */
+                  grub_free (modes_copy);
+
+                  return rc;
+                }
+            }
+
+          /* Try out video mode.  */
+
+          int flags = mode_flags;
+          /* If we have <= 8 bits, assume it is an indexed color mode.  */
+          if ((depth <= 8) && (depth != -1))
+            flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR;
+
+          /* We have > 8 bits; assume that it is RGB color mode.  */
+          if (depth > 8)
+            flags |= GRUB_VIDEO_MODE_TYPE_RGB;
+
+          /* If user requested specific depth, pass the request to driver.  */
+          if (depth != -1)
+            flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
+              & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK;
+
+          /* Try to initialize requested mode.  Ignore any errors.  */
+          grub_error_push ();
+          if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE)
+            {
+              grub_error_pop ();
+              continue;
+            }
+
+          /* Figure out what mode we ended up.  */
+          struct grub_video_mode_info mode_info;
+          if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
+            {
+              /* Couldn't get video mode info, restore old mode
+                 and continue to next one.  */
+              grub_error_pop ();
+
+              grub_video_restore ();
+              continue;
+            }
+
+          /* Restore state of error stack.  */
+          grub_error_pop ();
+
+          /* Mode found!  Exit loop.  */
+          mode_found = 1;
+        }
+
+      /* Free memory.  */
+      grub_free (modes_copy);
+    }
+
+  if (!mode_found)
+    {
+      /* No gfxmode variable set, or no listed mode was supported.
+         Use the caller-specified defaults.  */
+      int flags = mode_flags | GRUB_VIDEO_MODE_TYPE_RGB;
+
+      /* Initialize user requested mode.  */
+      if (grub_video_setup (default_width, default_height, flags) 
+          != GRUB_ERR_NONE)
+        return grub_errno;
+
+      /* Figure out what mode we ended up.  */
+      struct grub_video_mode_info mode_info;
+      if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE)
+        grub_video_restore ();
+      else
+        mode_found = 1;
+    }
+
+  if (!mode_found)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                       "No suitable mode found.");
+
+  return (grub_errno = GRUB_ERR_NONE);
+}


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 197 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2009-03-21 20:13 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-31 20:59 [PATCH] 5/5 Graphical menu (gfxmenu) Colin D Bennett
2009-01-31 21:39 ` Vesa Jääskeläinen
2009-02-08  0:54 ` Robert Millan
2009-03-21 20:13 ` Vesa Jääskeläinen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.