=== modified file 'commands/videotest.c'
--- commands/videotest.c 2008-07-09 16:14:04 +0000
+++ commands/videotest.c 2008-07-19 18:25:18 +0000
@@ -49,19 +49,6 @@
int double_buffering;
};
-/* A 2D rectangle type.
- * This could be worth integrating into the video API if it proves useful.*/
-struct grub_video_rect
-{
- /* These are signed because if there are unsigned it causes Bad Things
- * to happen when arithmetic and comparisions involving signed types is
- * done. Important signed types include offsets from absolute locations. */
- int x;
- int y;
- int width;
- int height;
-};
-typedef struct grub_video_rect grub_video_rect_t;
static void
basic_video_test (struct videotest_options *vt_opts)
=== modified file 'conf/common.rmk'
--- conf/common.rmk 2008-08-03 03:57:46 +0000
+++ conf/common.rmk 2008-08-03 04:00:22 +0000
@@ -274,6 +274,7 @@
cmp.mod cat.mod help.mod font.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.
@@ -281,6 +282,16 @@
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/widget-box.c \
+ gfxmenu/stringutil.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)
=== modified file 'conf/i386-pc.rmk'
--- conf/i386-pc.rmk 2008-07-28 16:26:46 +0000
+++ conf/i386-pc.rmk 2008-07-28 17:02:56 +0000
@@ -48,11 +48,13 @@
kern/generic/rtc_get_time_ms.c \
kern/generic/millisleep.c \
kern/env.c \
+ kern/menu_viewer.c \
term/i386/pc/console.c \
symlist.c
kernel_img_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \
env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \
partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \
+ menu_viewer.h \
machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \
machine/memory.h machine/loader.h machine/vga.h machine/vbe.h machine/kernel.h
kernel_img_CFLAGS = $(COMMON_CFLAGS)
@@ -250,7 +252,7 @@
play_mod_LDFLAGS = $(COMMON_LDFLAGS)
# 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)
=== modified file 'config.h.in'
--- config.h.in 2008-07-13 00:55:15 +0000
+++ config.h.in 2008-07-19 21:46:40 +0000
@@ -113,10 +113,37 @@
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
+/* Enable threading extensions on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
-/* Define for large files, on AIX-style hosts. */
-#undef _LARGE_FILES
=== added directory 'gfxmenu'
=== added file 'gfxmenu/gfxmenu.c'
--- gfxmenu/gfxmenu.c 1970-01-01 00:00:00 +0000
+++ gfxmenu/gfxmenu.c 2008-07-27 00:06:20 +0000
@@ -0,0 +1,221 @@
+/* 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 (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/model.c'
--- gfxmenu/model.c 1970-01-01 00:00:00 +0000
+++ gfxmenu/model.c 2008-07-26 23:37:04 +0000
@@ -0,0 +1,192 @@
+/* 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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/stringutil.c'
--- gfxmenu/stringutil.c 1970-01-01 00:00:00 +0000
+++ gfxmenu/stringutil.c 2008-07-26 00:44:48 +0000
@@ -0,0 +1,196 @@
+/* stringutil.c - String utilities. */
+/*
+ * 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 .
+ */
+
+#include
+#include
+#include
+#include
+
+/* 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)
+{
+ char *s = grub_malloc (end - start + 1);
+ if (! s)
+ return 0;
+ grub_memcpy (s, buf + start, end - start);
+ s[end - start] = '\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);
+}
=== added file 'gfxmenu/view.c'
--- gfxmenu/view.c 1970-01-01 00:00:00 +0000
+++ gfxmenu/view.c 2008-08-03 02:14:40 +0000
@@ -0,0 +1,1096 @@
+/* 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Specifies a time (in ms) to delay so the user can read the message if a
+ default boot attempt fails and an attempt is made to fall back on
+ another entry. */
+#define FALLBACK_MESSAGE_DELAY 2000
+
+typedef struct grub_gfxmenu_view_icon
+{
+ const char *class_name;
+ struct grub_video_bitmap *bitmap;
+ struct grub_gfxmenu_view_icon *next;
+} *grub_gfxmenu_view_icon_t;
+
+/* Definition of the private representation of the view. */
+struct grub_gfxmenu_view
+{
+ grub_video_rect_t screen;
+
+ int icon_width;
+ int icon_height;
+ int item_height;
+ int item_padding;
+ int item_icon_space;
+ int item_spacing;
+ grub_font_t title_font;
+ grub_font_t item_font;
+ grub_font_t selected_item_font;
+ grub_font_t status_font;
+ char *terminal_font_name;
+ grub_video_color_t title_color;
+ grub_video_color_t item_color;
+ grub_video_color_t selected_item_color;
+ grub_video_color_t status_color;
+ grub_video_color_t status_bg_color;
+ grub_video_color_t progress_bar_border_color;
+ grub_video_color_t progress_bar_fg_color;
+ grub_video_color_t progress_bar_bg_color;
+ struct grub_video_bitmap *desktop_image;
+ grub_video_color_t desktop_color;
+ grub_gfxmenu_box_t menu_box;
+ grub_gfxmenu_box_t selected_item_box;
+ grub_gfxmenu_box_t terminal_box;
+ char *title_text;
+ char *progress_message_text;
+ char *theme_path;
+ /* Icon cache: linked list w/ dummy head node. */
+ struct grub_gfxmenu_view_icon icon_cache;
+
+ grub_gfxmenu_model_t model;
+};
+
+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_video_color_t default_fg_color;
+ grub_video_color_t default_bg_color;
+
+ default_font = grub_font_get ("Helvetica 12");
+ default_fg_color = grub_video_map_rgb (0, 0, 0);
+ default_bg_color = grub_video_map_rgb (255, 255, 255);
+
+ view->icon_width = 32;
+ view->icon_height = 32;
+ view->item_height = 42;
+ view->item_padding = 14;
+ view->item_icon_space = 4;
+ view->item_spacing = 16;
+ view->title_font = default_font;
+ view->item_font = default_font;
+ view->selected_item_font = default_font;
+ view->status_font = default_font;
+ view->terminal_font_name = grub_strdup ("Fixed 10");
+ view->title_color = default_fg_color;
+ view->item_color = default_fg_color;
+ view->selected_item_color = default_fg_color;
+ view->status_color = default_bg_color;
+ view->status_bg_color = default_fg_color;
+ view->progress_bar_border_color = default_fg_color;
+ view->progress_bar_fg_color = grub_video_map_rgb (160, 160, 160);
+ view->progress_bar_bg_color = default_bg_color;
+ view->desktop_image = 0;
+ view->desktop_color = default_bg_color;
+ view->menu_box = 0;
+ view->selected_item_box = 0;
+ view->terminal_box = 0;
+ view->title_text = grub_strdup ("GRUB Boot Menu");
+ view->progress_message_text = 0;
+ view->theme_path = 0;
+ view->icon_cache.class_name = 0;
+ view->icon_cache.bitmap = 0;
+ view->icon_cache.next = 0;
+
+ view->model = model;
+
+ if (! grub_gfxmenu_view_load_theme (view, theme_path))
+ {
+ grub_gfxmenu_view_destroy (view);
+ return 0;
+ }
+
+ init_terminal (view);
+
+ return view;
+}
+
+static void
+free_icon_cache (grub_gfxmenu_view_t view)
+{
+ grub_gfxmenu_view_icon_t cur;
+ grub_gfxmenu_view_icon_t next;
+ for (cur = view->icon_cache.next; cur; cur = next)
+ {
+ next = cur->next;
+ grub_free (cur);
+ }
+ view->icon_cache.next = 0;
+}
+
+/* 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->menu_box)
+ view->menu_box->destroy (view->menu_box);
+ if (view->selected_item_box)
+ view->selected_item_box->destroy (view->selected_item_box);
+ 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);
+ free_icon_cache (view);
+ grub_free (view);
+
+ set_text_mode ();
+ destroy_terminal ();
+}
+
+/* Sets MESSAGE as the progress message for the view. The string MESSAGE
+ must be heap-allocated and will be owned by VIEW. MESSAGE can be 0, in
+ which case no message is displayed. */
+static void
+set_progress_message (grub_gfxmenu_view_t view, char *message)
+{
+ grub_free (view->progress_message_text);
+ view->progress_message_text = message;
+}
+
+/* Parse a color string of the form "r, g, b".
+ Whitespace is insignificant. */
+static grub_video_color_t
+parse_color (const char *s)
+{
+ int red = grub_strtoul (s, 0, 0);
+ if ((s = grub_strchr (s, ',')) == 0)
+ goto fail;
+ s++;
+ int green = grub_strtoul (s, 0, 0);
+ if ((s = grub_strchr (s, ',')) == 0)
+ goto fail;
+ s++;
+ int blue = grub_strtoul (s, 0, 0);
+ int alpha;
+ if ((s = grub_strchr (s, ',')) == 0)
+ alpha = 255;
+ else
+ {
+ s++;
+ alpha = grub_strtoul (s, 0, 0);
+ }
+ grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
+ return grub_video_map_rgba (red, green, blue, alpha);
+
+fail:
+ return 0;
+}
+
+/* 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.
+ Return zero on failure, nonzero on success. */
+static int
+theme_set_box (grub_gfxmenu_box_t *boxptr,
+ char *pattern, const char *theme_dir)
+{
+ char *abspattern;
+ char *prefix;
+ char *suffix;
+ char *star;
+ grub_gfxmenu_box_t box;
+
+ abspattern = grub_resolve_relative_path (theme_dir, pattern);
+ if (! abspattern)
+ return 0;
+
+ star = grub_strchr (abspattern, '*');
+ if (! star)
+ {
+ grub_free (abspattern);
+ return 0;
+ }
+
+ /* Prefix: Get the part before the '*'. */
+ prefix = grub_malloc (star - abspattern + 1);
+ if (! prefix)
+ {
+ grub_free (abspattern);
+ 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 (abspattern); /* Note: suffix, star point into abspattern. */
+ grub_free (prefix);
+ if (! box)
+ return 0;
+
+ if (*boxptr)
+ (*boxptr)->destroy (*boxptr);
+ *boxptr = box;
+ return 1;
+
+}
+
+/* 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, char *name, char *value,
+ const char *theme_dir)
+{
+ if (! grub_strcmp ("title-font", name))
+ view->title_font = grub_font_get (value);
+ else if (! grub_strcmp ("item-font", name))
+ view->item_font = grub_font_get (value);
+ else if (! grub_strcmp ("selected-item-font", name))
+ view->selected_item_font = grub_font_get (value);
+ else if (! grub_strcmp ("status-font", name))
+ view->status_font = grub_font_get (value);
+ else if (! grub_strcmp ("terminal-font", name))
+ {
+ grub_free (view->terminal_font_name);
+ view->terminal_font_name = value;
+ value = 0; /* Prevent value from being freed below. */
+ }
+ else if (! grub_strcmp ("title-color", name))
+ view->title_color = parse_color (value);
+ else if (! grub_strcmp ("item-color", name))
+ view->item_color = parse_color (value);
+ else if (! grub_strcmp ("selected-item-color", name))
+ view->selected_item_color = parse_color (value);
+ else if (! grub_strcmp ("status-color", name))
+ view->status_color = parse_color (value);
+ else if (! grub_strcmp ("status-bg-color", name))
+ view->status_bg_color = parse_color (value);
+ else if (! grub_strcmp ("progress-bar-border-color", name))
+ view->progress_bar_border_color = parse_color (value);
+ else if (! grub_strcmp ("progress-bar-fg-color", name))
+ view->progress_bar_fg_color = parse_color (value);
+ else if (! grub_strcmp ("progress-bar-bg-color", name))
+ view->progress_bar_bg_color = 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)
+ goto fail;
+ if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE)
+ {
+ grub_free (path);
+ goto fail;
+ }
+ 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)
+ goto fail;
+
+ grub_video_bitmap_destroy (view->desktop_image);
+ view->desktop_image = scaled_bitmap;
+ }
+ else if (! grub_strcmp ("desktop-color", name))
+ view->desktop_color = parse_color (value);
+ else if (! grub_strcmp ("menu-box", name))
+ theme_set_box (&view->menu_box, value, theme_dir);
+ else if (! grub_strcmp ("selected-item-box", name))
+ theme_set_box (&view->selected_item_box, value, theme_dir);
+ else if (! grub_strcmp ("terminal-box", name))
+ theme_set_box (&view->terminal_box, value, theme_dir);
+ else if (! grub_strcmp ("title-text", name))
+ {
+ grub_free (view->title_text);
+ view->title_text = value;
+ value = 0; /* Prevent value from being freed below. */
+ }
+
+fail:
+ grub_free (value);
+ grub_free (name);
+}
+
+/* Set the specified property NAME on the view to the given numeric VALUE.
+ This function takes ownership NAME, so the caller should pass a pointer
+ to a new heap-allocated string. */
+static void
+theme_set_number (grub_gfxmenu_view_t view, char *name, int value)
+{
+ if (! grub_strcmp ("icon-width", name))
+ view->icon_width = value;
+ else if (! grub_strcmp ("icon-height", name))
+ view->icon_height = value;
+ else if (! grub_strcmp ("item-height", name))
+ view->item_height = value;
+ else if (! grub_strcmp ("item-padding", name))
+ view->item_padding = value;
+ else if (! grub_strcmp ("item-icon-space", name))
+ view->item_icon_space = value;
+ else if (! grub_strcmp ("item-spacing", name))
+ view->item_spacing = value;
+
+ grub_free (name);
+}
+
+/* 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)
+{
+ char *theme_dir;
+ grub_file_t file;
+ char *buf;
+ int pos;
+ int len;
+
+ theme_dir = grub_get_dirname (theme_path);
+
+ file = grub_file_open (theme_path);
+ if (!file)
+ {
+ grub_free (theme_dir);
+ return 0;
+ }
+
+ len = grub_file_size (file);
+ buf = grub_malloc (len);
+ if (! buf)
+ {
+ grub_file_close (file);
+ grub_free (theme_dir);
+ return 0;
+ }
+ if (grub_file_read (file, buf, len) != len)
+ {
+ grub_free (buf);
+ grub_file_close (file);
+ grub_free (theme_dir);
+ return 0;
+ }
+
+ pos = 0;
+ while (pos < len)
+ {
+ /* Skip comments (lines beginning with #). */
+ if (pos < len && buf[pos] == '#')
+ goto nextline;
+
+ /* Get name. */
+ /* Find a word character. */
+ while (pos < len && grub_isspace(buf[pos]))
+ pos++;
+ int name_start = pos;
+ /* Find the end of the name. */
+ while (pos < len
+ && (grub_isalpha(buf[pos])
+ || grub_isdigit(buf[pos])
+ || buf[pos] == '_'
+ || buf[pos] == '-'))
+ pos++;
+ int name_end = pos;
+
+ if (name_end - name_start < 1)
+ goto nextline;
+
+ /* Skip whitespace before separator. */
+ while (pos < len
+ && (buf[pos] == ' '
+ || buf[pos] == '\t'
+ || buf[pos] == '\f'))
+ pos++;
+
+ /* Read separator. */
+ if (buf[pos] != ':')
+ goto nextline;
+
+ pos++; /* Skip separator. */
+
+ /* Skip whitespace after separator. */
+ while (pos < len
+ && (buf[pos] == ' '
+ || buf[pos] == '\t'
+ || buf[pos] == '\f'))
+ pos++;
+
+ /* Get the value based on its type. */
+ if (pos < len && buf[pos] == '"')
+ {
+ /* String value. (e.g., '"My string"') */
+
+ int value_start;
+ int value_end;
+
+ /* Skip the opening quotation mark. */
+ pos++;
+ /* Get string value. */
+ value_start = pos;
+ /* Find the ending quotation mark. */
+ while (pos < len
+ && !(buf[pos] == '"'
+ || buf[pos] == '\n'))
+ pos++;
+ value_end = pos;
+ theme_set_string (view,
+ grub_new_substring (buf, name_start, name_end),
+ grub_new_substring (buf, value_start, value_end),
+ theme_dir);
+ }
+ else if (pos < len && grub_isdigit(buf[pos]))
+ {
+ /* Numeric value. (e.g., '123') */
+
+ int value_start;
+ int value_end;
+ char *value_str;
+ int value;
+
+ /* Get numeric value. */
+ value_start = pos;
+ /* Find the end of the digit sequence. */
+ while (pos < len && grub_isdigit(buf[pos]))
+ pos++;
+ value_end = pos;
+ value_str = grub_new_substring (buf, value_start, value_end);
+ if (!value_str)
+ continue;
+ value = grub_strtoul (value_str, 0, 0);
+ grub_free (value_str);
+ theme_set_number (view,
+ grub_new_substring (buf, name_start, name_end),
+ value);
+ }
+
+nextline:
+ /* Eat characters up to the newline. */
+ while (pos < len && buf[pos] != '\n')
+ pos++;
+ pos++; /* Eat the newline. */
+ }
+
+ grub_free (view->theme_path);
+ view->theme_path = grub_strdup (theme_path);
+ free_icon_cache (view); /* There may be new icons for this theme. */
+ grub_free (buf);
+ grub_file_close (file);
+ grub_free (theme_dir);
+ return 1;
+}
+
+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 (view->desktop_color,
+ view->screen.x, view->screen.y,
+ view->screen.width, view->screen.height);
+ }
+}
+
+static const char icon_extension[] = ".png";
+
+static struct grub_video_bitmap *
+try_loading_icon (grub_gfxmenu_view_t view,
+ const char *dir, const char *class_name)
+{
+ char *path = grub_malloc (grub_strlen (dir)
+ + grub_strlen (class_name)
+ + grub_strlen (icon_extension)
+ + 100);
+ 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,
+ view->icon_width, view->icon_height,
+ raw_bitmap,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ grub_video_bitmap_destroy (raw_bitmap);
+ if (! scaled_bitmap)
+ return 0;
+
+ return scaled_bitmap;
+}
+
+static struct grub_video_bitmap *
+get_icon_by_class (grub_gfxmenu_view_t view, const char *class_name)
+{
+
+ /* First check the view's icon cache. */
+ grub_gfxmenu_view_icon_t vicon;
+ for (vicon = view->icon_cache.next; vicon; vicon = vicon->next)
+ {
+ if (grub_strcmp (vicon->class_name, class_name) == 0)
+ return vicon->bitmap;
+ }
+
+ /* Otherwise, we search for an icon to load. */
+ if (! view->theme_path)
+ return 0;
+
+ char *theme_dir = grub_get_dirname (view->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 (view, icons_dir, class_name);
+ grub_free (icons_dir);
+ }
+ if (! icon)
+ {
+ /* If the theme doesn't have an appropriate icon, check in
+ "grub/themes/icons". */
+ icons_dir = grub_resolve_relative_path (theme_dir, "../icons/");
+ if (icons_dir)
+ {
+ icon = try_loading_icon (view, icons_dir, class_name);
+ grub_free (icons_dir);
+ }
+ }
+ grub_free (theme_dir);
+
+ if (! icon)
+ return 0;
+
+ vicon = grub_malloc (sizeof (*vicon));
+ if (! vicon)
+ {
+ grub_video_bitmap_destroy (icon);
+ return 0;
+ }
+ vicon->class_name = class_name;
+ vicon->bitmap = icon;
+ vicon->next = view->icon_cache.next;
+ view->icon_cache.next = vicon; /* Link it into the cache. */
+ return vicon->bitmap;
+}
+
+static struct grub_video_bitmap *
+get_item_icon (grub_gfxmenu_view_t view, int item_index)
+{
+ grub_menu_entry_t entry;
+ entry = grub_gfxmenu_model_get_entry (view->model, item_index);
+ if (! entry)
+ return 0;
+
+ struct grub_menu_entry_class *c;
+ struct grub_video_bitmap *icon;
+ icon = 0;
+ for (c = entry->classes->next; c && ! icon; c = c->next)
+ icon = get_icon_by_class (view, c->name);
+ return icon;
+}
+
+static void
+draw_menu (grub_gfxmenu_view_t view)
+{
+ int boxpad = view->item_padding;
+ int icon_text_space = view->item_icon_space;
+ int item_vspace = view->item_spacing;
+
+ int ascent = grub_font_get_ascent (view->item_font);
+ int descent = grub_font_get_descent (view->item_font);
+ int item_height = view->item_height;
+
+ int num_items = grub_gfxmenu_model_get_num_entries (view->model);
+ grub_video_rect_t r;
+ r.width = view->screen.width * 4 / 5;
+ /* Set the menu box height to fit the items. */
+ r.height = (item_height * num_items
+ + item_vspace * (num_items - 1)
+ + 2 * boxpad);
+ r.x = (view->screen.width - r.width) / 2;
+ r.y = (view->screen.height - r.height) / 2;
+ view->menu_box->set_content_size (view->menu_box, r.width, r.height);
+
+ int menu_box_left_pad = view->menu_box->get_left_pad (view->menu_box);
+ int menu_box_top_pad = view->menu_box->get_top_pad (view->menu_box);
+ view->menu_box->draw (view->menu_box,
+ r.x - menu_box_left_pad, r.y - menu_box_top_pad);
+
+ int item_top = r.y + boxpad;
+ int item_left = r.x + boxpad;
+ int i;
+
+ for (i = 0; i < num_items; i++)
+ {
+ int is_selected =
+ (i == grub_gfxmenu_model_get_selected_index (view->model));
+
+ if (is_selected)
+ {
+ view->selected_item_box->set_content_size (view->selected_item_box,
+ r.width - 2 * boxpad,
+ item_height);
+ int leftpad = view->selected_item_box->get_left_pad (view->selected_item_box);
+ int toppad = view->selected_item_box->get_top_pad (view->selected_item_box);
+ view->selected_item_box->draw (view->selected_item_box,
+ item_left - leftpad,
+ item_top - toppad);
+ }
+
+ struct grub_video_bitmap *icon;
+ if ((icon = get_item_icon (view, i)) != 0)
+ grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND,
+ item_left,
+ item_top + (item_height - view->icon_height) / 2,
+ 0, 0, view->icon_width, view->icon_height);
+
+ const char *item_title =
+ grub_gfxmenu_model_get_entry_title (view->model, i);
+ grub_video_draw_string (item_title,
+ (is_selected
+ ? view->selected_item_font
+ : view->item_font),
+ (is_selected
+ ? view->selected_item_color
+ : view->item_color),
+ item_left + view->icon_width + icon_text_space,
+ (item_top + (item_height - (ascent + descent))
+ / 2 + ascent));
+
+ item_top += item_height + item_vspace;
+ }
+}
+
+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_video_draw_string (view->title_text,
+ view->title_font, view->title_color,
+ x, y);
+}
+
+static void
+draw_status (grub_gfxmenu_view_t view)
+{
+ int descent = grub_font_get_descent (view->status_font);
+ int ascent = grub_font_get_ascent (view->status_font);
+ int vpad = 5;
+ int textheight = descent + ascent + 1;
+ int h = 2 * vpad + 2 * textheight;
+
+ grub_video_fill_rect (view->status_bg_color,
+ 0, view->screen.height - h,
+ view->screen.width, view->screen.height - 1);
+
+ int texty = view->screen.height - h + vpad + ascent;
+ grub_video_draw_string ("Select an item with the arrow keys and "
+ "press Enter to boot.",
+ view->status_font, view->status_color, 30, texty);
+ texty += textheight;
+ grub_video_draw_string ("Press: 'c' for command line; 't' to switch to "
+ "non-graphical menu.",
+ view->status_font, view->status_color, 30, texty);
+}
+
+static void
+draw_timeout (grub_gfxmenu_view_t view)
+{
+ int timeout = grub_gfxmenu_model_get_timeout_ms (view->model);
+ if (timeout == -1)
+ return;
+
+ int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model);
+ if (remaining < 0)
+ remaining = 0;
+
+ int t = timeout - remaining;
+
+ /* Set the timeout bar's frame. */
+ grub_video_rect_t f;
+ f.width = view->screen.width * 3 / 5;
+ f.height = view->screen.height / 25;
+ f.x = view->screen.x + (view->screen.width - f.width) / 2;
+ f.y = view->screen.y + view->screen.height - 90;
+
+ /* First attempt just uses filled rectangles;
+ TODO we should enhance with a pixmap themed progress bar component. */
+
+ /* Border. */
+ grub_video_fill_rect (view->progress_bar_border_color,
+ f.x - 1, f.y - 1,
+ f.width + 2, f.height + 2);
+
+ /* Bar background. */
+ int barwidth = f.width * t / timeout;
+ grub_video_fill_rect (view->progress_bar_bg_color,
+ f.x + barwidth, f.y,
+ f.width - barwidth, f.height);
+
+ /* Bar foreground. */
+ grub_video_fill_rect (view->progress_bar_fg_color,
+ f.x, f.y,
+ barwidth, f.height);
+
+ char *text = grub_malloc (200);
+ if (!text)
+ return;
+ int seconds_remaining_rounded_up = (remaining + 999) / 1000;
+ grub_sprintf (text,
+ "The highlighted entry will be booted automatically in %d s.",
+ seconds_remaining_rounded_up);
+ grub_font_t font = view->status_font;
+ grub_video_color_t color = view->progress_bar_border_color;
+
+ /* 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_video_draw_string (text, font, color, x, y);
+ grub_free (text);
+}
+
+static void
+draw_message (grub_gfxmenu_view_t view)
+{
+ char *text = view->progress_message_text;
+ if (! text)
+ return;
+
+ grub_font_t font = view->status_font;
+ grub_video_color_t color = view->status_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 (view->status_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_video_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);
+ draw_background (view);
+ draw_menu (view);
+ draw_title (view);
+ draw_status (view);
+ draw_timeout (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_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 ();
+
+ 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 (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 (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_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_gfxmenu_view_draw (view);
+ grub_video_swap_buffers ();
+ grub_millisleep (FALLBACK_MESSAGE_DELAY);
+}
+
+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, grub_strdup ("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 2008-07-19 18:25:18 +0000
@@ -0,0 +1,244 @@
+/* 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+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 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;
+ unsigned i;
+
+ /* Don't try to draw if any pixmaps are null, except the center. */
+ for (i = 0; i < BOX_NUM_PIXMAPS; i++)
+ {
+ if (i != BOX_PIXMAP_CENTER && self->scaled_pixmaps[i] == 0)
+ return;
+ }
+
+ height_n = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_N]);
+ height_s = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_S]);
+ height_e = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_E]);
+ height_w = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_W]);
+ width_n = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_N]);
+ width_s = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_S]);
+ width_e = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_E]);
+ width_w = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_W]);
+
+ /* Draw sides. */
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_N],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w, y, 0, 0, width_n, height_n);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_S],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w, y + height_n + height_w,
+ 0, 0, width_s, height_s);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_E],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w + width_n, y + height_n,
+ 0, 0, width_e, height_e);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_W],
+ GRUB_VIDEO_BLIT_BLEND,
+ x, y + height_n, 0, 0, width_w, height_w);
+
+ /* Draw corners. */
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NW],
+ GRUB_VIDEO_BLIT_BLEND,
+ x, y, 0, 0, width_w, height_n);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NE],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w + width_n, y,
+ 0, 0, width_e, height_n);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SE],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w + width_n, y + height_n + height_e,
+ 0, 0, width_e, height_s);
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SW],
+ GRUB_VIDEO_BLIT_BLEND,
+ x, y + height_n + height_w,
+ 0, 0, width_w, height_s);
+
+ /* Draw center. */
+ if (self->scaled_pixmaps[BOX_PIXMAP_CENTER])
+ grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_CENTER],
+ GRUB_VIDEO_BLIT_BLEND,
+ x + width_w, y + height_n,
+ 0, 0, self->width, self->height);
+}
+
+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->width = width;
+ self->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 grub_video_bitmap_get_width (self->raw_pixmaps[BOX_PIXMAP_W]);
+}
+
+static int
+get_top_pad (grub_gfxmenu_box_t self)
+{
+ return grub_video_bitmap_get_height (self->raw_pixmaps[BOX_PIXMAP_N]);
+}
+
+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! */
+}
+
+
+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->width = 0;
+ box->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++)
+ {
+ /* TODO XXX dynamically allocate PATH, making sure it's large enough */
+ grub_sprintf (path, "%s%s%s",
+ pixmaps_prefix, box_pixmap_names[i], pixmaps_suffix);
+ grub_video_bitmap_load (&box->raw_pixmaps[i], path);
+ 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->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 2008-07-26 23:37:04 +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 .
+ */
+
+#ifndef GRUB_GFXMENU_MODEL_HEADER
+#define GRUB_GFXMENU_MODEL_HEADER 1
+
+#include
+
+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 2008-07-26 23:37:04 +0000
@@ -0,0 +1,53 @@
+/* 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 .
+ */
+
+#ifndef GRUB_GFXMENU_VIEW_HEADER
+#define GRUB_GFXMENU_VIEW_HEADER 1
+
+#include
+#include
+#include
+#include
+
+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);
+
+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);
+
+#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 2008-07-19 19:56:06 +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 .
+ */
+
+#ifndef GRUB_GFXTERM_HEADER
+#define GRUB_GFXTERM_HEADER 1
+
+#include
+#include
+#include
+#include
+
+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_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 2008-07-19 18:25:18 +0000
@@ -0,0 +1,47 @@
+/* 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 .
+ */
+
+#ifndef GRUB_GFXWIDGETS_HEADER
+#define GRUB_GFXWIDGETS_HEADER 1
+
+#include
+
+typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t;
+
+struct grub_gfxmenu_box
+{
+ /* The size of the content. */
+ int width;
+ int 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);
+ 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/menu.h'
--- include/grub/menu.h 1970-01-01 00:00:00 +0000
+++ include/grub/menu.h 2008-08-03 02:14:40 +0000
@@ -0,0 +1,63 @@
+/* menu.h - Menu and menu entry model declarations. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,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 .
+ */
+
+#ifndef GRUB_MENU_HEADER
+#define GRUB_MENU_HEADER 1
+
+struct grub_menu_entry_class
+{
+ char *name;
+ struct grub_menu_entry_class *next;
+};
+
+/* The menu entry. */
+struct grub_menu_entry
+{
+ /* The title name. */
+ const char *title;
+
+ /* The classes associated with the menu entry:
+ used to choose an icon or other style attributes.
+ This is a dummy head node for the linked list, so for an entry E,
+ E.classes->next is the first class if it is not NULL. */
+ struct grub_menu_entry_class *classes;
+
+ /* The commands associated with this menu entry. */
+ struct grub_script *commands;
+
+ /* The sourcecode of the menu entry, used by the editor. */
+ const char *sourcecode;
+
+ /* The next element. */
+ struct grub_menu_entry *next;
+};
+typedef struct grub_menu_entry *grub_menu_entry_t;
+
+/* The menu. */
+struct grub_menu
+{
+ /* The size of a menu. */
+ int size;
+
+ /* The list of menu entries. */
+ grub_menu_entry_t entry_list;
+};
+typedef struct grub_menu *grub_menu_t;
+
+#endif /* GRUB_MENU_HEADER */
=== added file 'include/grub/menu_viewer.h'
--- include/grub/menu_viewer.h 1970-01-01 00:00:00 +0000
+++ include/grub/menu_viewer.h 2008-07-19 19:07:47 +0000
@@ -0,0 +1,48 @@
+/* menu_viewer.h - Interface to menu viewer implementations. */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,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 .
+ */
+
+#ifndef GRUB_MENU_VIEWER_HEADER
+#define GRUB_MENU_VIEWER_HEADER 1
+
+#include
+#include
+#include
+#include
+
+struct grub_menu_viewer
+{
+ /* The menu viewer name. */
+ const char *name;
+
+ grub_err_t (*show_menu) (grub_menu_t menu, int nested);
+
+ struct grub_menu_viewer *next;
+};
+typedef struct grub_menu_viewer *grub_menu_viewer_t;
+
+void EXPORT_FUNC(grub_menu_viewer_init) (void);
+
+void EXPORT_FUNC(grub_menu_viewer_register) (grub_menu_viewer_t viewer);
+
+grub_err_t EXPORT_FUNC(grub_menu_viewer_show_menu) (grub_menu_t menu, int nested);
+
+/* Return nonzero iff the menu viewer should clean up and return ASAP. */
+int EXPORT_FUNC(grub_menu_viewer_should_return) (void);
+
+#endif /* GRUB_MENU_VIEWER_HEADER */
=== modified file 'include/grub/normal.h'
--- include/grub/normal.h 2008-08-03 03:47:20 +0000
+++ include/grub/normal.h 2008-08-03 04:00:22 +0000
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
/* The maximum size of a command-line. */
#define GRUB_MAX_CMDLINE 1600
@@ -84,34 +85,6 @@
};
typedef struct grub_command *grub_command_t;
-/* The menu entry. */
-struct grub_menu_entry
-{
- /* The title name. */
- const char *title;
-
- /* The commands associated with this menu entry. */
- struct grub_script *commands;
-
- /* The sourcecode of the menu entry, used by the editor. */
- const char *sourcecode;
-
- /* The next element. */
- struct grub_menu_entry *next;
-};
-typedef struct grub_menu_entry *grub_menu_entry_t;
-
-/* The menu. */
-struct grub_menu
-{
- /* The size of a menu. */
- int size;
-
- /* The list of menu entries. */
- grub_menu_entry_t entry_list;
-};
-typedef struct grub_menu *grub_menu_t;
-
/* This is used to store the names of filesystem modules for auto-loading. */
struct grub_fs_module_list
{
@@ -123,10 +96,27 @@
/* To exit from the normal mode. */
extern grub_jmp_buf grub_exit_env;
+extern struct grub_menu_viewer grub_normal_terminal_menu_viewer;
+
+typedef struct grub_menu_execute_callback
+{
+ void (*notify_booting) (void *userdata, grub_menu_entry_t entry);
+ void (*notify_fallback) (void *userdata, grub_menu_entry_t entry);
+ void (*notify_failure) (void *userdata);
+}
+*grub_menu_execute_callback_t;
+
void grub_enter_normal_mode (const char *config);
void grub_normal_execute (const char *config, int nested);
-void grub_menu_run (grub_menu_t menu, int nested);
+void grub_menu_execute_with_fallback (grub_menu_t menu,
+ grub_menu_entry_t entry,
+ grub_menu_execute_callback_t callback,
+ void *callback_data);
void grub_menu_entry_run (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_menu_execute_entry(grub_menu_entry_t entry);
void grub_cmdline_run (int nested);
int grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len,
int echo_char, int readline);
=== added file 'include/grub/stringutil.h'
--- include/grub/stringutil.h 1970-01-01 00:00:00 +0000
+++ include/grub/stringutil.h 2008-07-25 15:52:57 +0000
@@ -0,0 +1,32 @@
+/* gfxmenu_stringutil.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 .
+ */
+
+#ifndef GRUB_GFXMENU_STRINGUTIL_HEADER
+#define GRUB_GFXMENU_STRINGUTIL_HEADER 1
+
+#include
+
+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);
+
+#endif /* GRUB_GFXMENU_STRINGUTIL_HEADER */
=== modified file 'include/grub/video.h'
--- include/grub/video.h 2008-07-03 14:12:08 +0000
+++ include/grub/video.h 2008-07-19 19:31:46 +0000
@@ -151,6 +151,16 @@
grub_uint8_t a; /* Reserved bits value (0-255). */
};
+/* A 2D rectangle type. */
+struct grub_video_rect
+{
+ int x;
+ int y;
+ int width;
+ int height;
+};
+typedef struct grub_video_rect grub_video_rect_t;
+
struct grub_video_adapter
{
/* The video adapter name. */
@@ -307,4 +317,21 @@
grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target);
+
+/* Defined in video/setmode.c */
+
+/* Set the video mode based on the preferred modes specified in MODE_LIST in
+ the form: x[x][;...]
+
+ 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);
+
+
#endif /* ! GRUB_VIDEO_HEADER */
=== added file 'kern/menu_viewer.c'
--- kern/menu_viewer.c 1970-01-01 00:00:00 +0000
+++ kern/menu_viewer.c 2008-07-19 19:07:47 +0000
@@ -0,0 +1,99 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005,2007 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+/* 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)
+{
+ viewer->next = menu_viewer_list;
+ menu_viewer_list = viewer;
+}
+
+static grub_menu_viewer_t get_current_menu_viewer (void)
+{
+ const char *selected_name = grub_env_get ("menuviewer");
+
+ /* If none selected, pick the last registered one. */
+ if (selected_name == 0)
+ return menu_viewer_list;
+
+ grub_menu_viewer_t cur;
+ for (cur = menu_viewer_list; cur; cur = cur->next)
+ {
+ if (grub_strcmp (cur->name, selected_name) == 0)
+ return cur;
+ }
+
+ /* Fall back to the first entry (or null). */
+ return menu_viewer_list;
+}
+
+grub_err_t
+grub_menu_viewer_show_menu (grub_menu_t menu, int 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 'normal/main.c'
--- normal/main.c 2008-07-29 14:07:47 +0000
+++ normal/main.c 2008-08-03 04:00:22 +0000
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
grub_jmp_buf grub_exit_env;
@@ -147,14 +148,132 @@
grub_env_unset_data_slot ("menu");
}
+static void
+free_menu_entry_classes (struct grub_menu_entry_class *head)
+{
+ /* Free all the classes. */
+ while (head)
+ {
+ struct grub_menu_entry_class *next;
+
+ grub_free (head->name);
+ next = head->next;
+ grub_free (head);
+ head = next;
+ }
+}
+
+/* The tag that can be added to a menu entry's title to specify a class
+ for the UI to use in selecting an icon or other visual attributes. */
+static const char entry_class_attr_tag[] = "|class=";
+
+#define ENTRY_ATTR_SEPARATOR_CHAR ','
+
+/* Parse and strip a possible "class" attribute in the title.
+ This code is not designed to support other attributes than "class"
+ in the title since, we are planning to use a better method of
+ specifying this information in the future. The parameter TITLE is
+ modified by storing a '\0' at the appropriate location to strip the
+ class information, if it exists. The class list is stored into *HEAD. */
+static struct grub_menu_entry_class *
+get_classes_from_entry_title (char *title)
+{
+ struct grub_menu_entry_class *head;
+ char *attr_start;
+
+ head = grub_malloc (sizeof (struct grub_menu_entry_class));
+ if (! head)
+ return 0;
+ head->name = 0;
+ head->next = 0;
+
+ attr_start = grub_strstr (title, entry_class_attr_tag);
+ if (attr_start)
+ {
+ struct grub_menu_entry_class *tail;
+ const char *p;
+
+ /* Trim the properties off of the title. */
+ *attr_start = '\0';
+
+ /* Move the pointer to the beginning of the first class name. */
+ attr_start += grub_strlen (entry_class_attr_tag);
+
+ tail = head;
+ p = attr_start;
+ while (p && *p)
+ {
+ const char *q;
+ const char *end;
+ const char *next_start;
+
+ /* Skip any leading whitespace. */
+ while (*p && grub_isspace (*p))
+ p++;
+
+ /* Find the comma terminating this one ... */
+ q = grub_strchr (p, ENTRY_ATTR_SEPARATOR_CHAR);
+ /* ... or if it's the last one, find the '\0' terminator. */
+ if (q)
+ {
+ end = q - 1;
+ next_start = q + 1;
+ }
+ else
+ {
+ /* For the last class, extend it to the end. */
+ end = p + grub_strlen (p);
+ next_start = 0;
+ }
+
+ /* Trim any trailing whitespace. */
+ while (end > p && grub_isspace (*end))
+ end--;
+
+ grub_size_t len = end - p + 1;
+ /* Copy the class name into a new string. */
+ char *class_name = grub_malloc (len + 1);
+ if (! class_name)
+ {
+ free_menu_entry_classes (head);
+ return 0;
+ }
+ grub_memcpy (class_name, p, len);
+ class_name[len] = '\0';
+
+ /* Create a new class and add it at the tail of the list. */
+ struct grub_menu_entry_class *new_class;
+ new_class = grub_malloc (sizeof (struct grub_menu_entry_class));
+ if (! new_class)
+ {
+ grub_free (class_name);
+ free_menu_entry_classes (head);
+ return 0;
+ }
+ /* Fill in the new class node. */
+ new_class->name = class_name;
+ new_class->next = 0;
+ /* Link the tail to it, and make it the new tail. */
+ tail->next = new_class;
+ tail = new_class;
+
+ /* Advance the character pointer. */
+ p = next_start;
+ }
+ }
+
+ return head;
+}
+
grub_err_t
grub_normal_menu_addentry (const char *title, struct grub_script *script,
const char *sourcecode)
{
- const char *menutitle;
+ char *menutitle;
const char *menusourcecode;
grub_menu_t menu;
grub_menu_entry_t *last;
+ struct grub_menu_entry_class *classes;
menu = grub_env_get_data_slot("menu");
if (! menu)
@@ -173,6 +292,14 @@
return grub_errno;
}
+ classes = get_classes_from_entry_title (menutitle);
+ if (! classes)
+ {
+ grub_free ((void *) menutitle);
+ grub_free ((void *) menusourcecode);
+ return grub_errno;
+ }
+
/* Add the menu entry at the end of the list. */
while (*last)
last = &(*last)->next;
@@ -180,6 +307,7 @@
*last = grub_malloc (sizeof (**last));
if (! *last)
{
+ free_menu_entry_classes (classes);
grub_free ((void *) menutitle);
grub_free ((void *) menusourcecode);
return grub_errno;
@@ -187,6 +315,7 @@
(*last)->commands = script;
(*last)->title = menutitle;
+ (*last)->classes = classes;
(*last)->next = 0;
(*last)->sourcecode = menusourcecode;
@@ -476,7 +605,7 @@
if (menu && menu->size)
{
- grub_menu_run (menu, nested);
+ grub_menu_viewer_show_menu (menu, nested);
if (nested)
free_menu (menu);
}
@@ -519,6 +648,8 @@
if (mod)
grub_dl_ref (mod);
+ grub_menu_viewer_register (&grub_normal_terminal_menu_viewer);
+
grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
/* Register a command "normal" for the rescue mode. */
@@ -535,6 +666,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 2008-02-09 11:00:19 +0000
+++ normal/menu.c 2008-07-26 23:37:04 +0000
@@ -21,9 +21,11 @@
#include
#include
#include
+#include
#include
#include
#include
+#include
static grub_uint8_t grub_color_menu_normal;
static grub_uint8_t grub_color_menu_highlight;
@@ -241,8 +243,8 @@
/* Return the current timeout. If the variable "timeout" is not set or
invalid, return -1. */
-static int
-get_timeout (void)
+int
+grub_menu_get_timeout (void)
{
char *val;
int timeout;
@@ -269,8 +271,8 @@
}
/* Set current timeout in the variable "timeout". */
-static void
-set_timeout (int timeout)
+void
+grub_menu_set_timeout (int timeout)
{
/* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
if (timeout > 0)
@@ -308,6 +310,57 @@
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. */
+static int
+get_and_remove_first_entry_number (const char *name)
+{
+ char *val;
+ char *tail;
+ int entry;
+
+ val = grub_env_get (name);
+ if (! val)
+ return -1;
+
+ grub_error_push ();
+
+ entry = (int) grub_strtoul (val, &tail, 0);
+
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ /* Skip whitespace to find the next digit. */
+ while (*tail && grub_isspace (*tail))
+ tail++;
+ grub_env_set (name, tail);
+ }
+ else
+ {
+ grub_env_unset (name);
+ grub_errno = GRUB_ERR_NONE;
+ entry = -1;
+ }
+
+ grub_error_pop ();
+
+ return entry;
+}
+
static void
print_timeout (int timeout, int offset, int second_stage)
{
@@ -332,15 +385,10 @@
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 (get_timeout () == 0)
+ if (grub_menu_get_timeout () == 0)
return default_entry;
offset = default_entry;
@@ -359,15 +407,15 @@
print_entries (menu, first, offset);
grub_refresh ();
- timeout = get_timeout ();
+ timeout = grub_menu_get_timeout ();
if (timeout > 0)
print_timeout (timeout, offset, 0);
- while (1)
+ while (!grub_menu_viewer_should_return ())
{
int c;
- timeout = get_timeout ();
+ timeout = grub_menu_get_timeout ();
if (timeout > 0)
{
@@ -377,7 +425,7 @@
if (current_time - saved_time >= GRUB_TICKS_PER_SECOND)
{
timeout--;
- set_timeout (timeout);
+ grub_menu_set_timeout (timeout);
saved_time = current_time;
print_timeout (timeout, offset, 1);
}
@@ -468,6 +516,10 @@
}
goto refresh;
+ case 't':
+ grub_env_set ("menuviewer", "protomenu");
+ goto refresh;
+
default:
break;
}
@@ -476,13 +528,14 @@
}
}
- /* Never reach here. */
+ /* Exit menu without activating an item. This occurs if the user presses
+ * 't', switching to the graphical menu viewer. */
return -1;
}
/* Run a menu entry. */
-static void
-run_menu_entry (grub_menu_entry_t entry)
+void
+grub_menu_execute_entry(grub_menu_entry_t entry)
{
grub_script_execute (entry->commands);
@@ -491,15 +544,80 @@
grub_command_execute ("boot", 0);
}
+/* Execute ENTRY from the menu MENU, falling back to entries specified
+ in the environment variable "fallback" if it fails. CALLBACK is a
+ pointer to a struct of function pointers which are used to allow the
+ caller provide feedback to the user. */
void
-grub_menu_run (grub_menu_t menu, int nested)
+grub_menu_execute_with_fallback (grub_menu_t menu,
+ grub_menu_entry_t entry,
+ grub_menu_execute_callback_t callback,
+ void *callback_data)
+{
+ int fallback_entry;
+
+ callback->notify_booting (callback_data, entry);
+
+ grub_menu_execute_entry (entry);
+
+ /* Deal with fallback entries. */
+ while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
+ >= 0)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+
+ entry = get_entry (menu, fallback_entry);
+ callback->notify_fallback (callback_data, entry);
+ grub_menu_execute_entry (entry);
+ }
+
+ if (grub_errno != GRUB_ERR_NONE)
+ callback->notify_failure (callback_data);
+}
+
+static void
+notify_booting (void *userdata __attribute__((unused)),
+ grub_menu_entry_t entry)
+{
+ grub_printf (" Booting \'%s\'\n\n", entry->title);
+}
+
+static void
+notify_fallback (void *userdata __attribute__((unused)),
+ grub_menu_entry_t entry)
+{
+ grub_printf ("\n Falling back to \'%s\'\n\n", entry->title);
+ grub_millisleep (2000);
+}
+
+static void
+notify_execution_failure (void *userdata __attribute__((unused)))
+{
+ if (grub_errno != GRUB_ERR_NONE)
+ {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+
+ grub_wait_after_message ();
+ }
+}
+
+static struct grub_menu_execute_callback execution_callback =
+{
+ .notify_booting = notify_booting,
+ .notify_fallback = notify_fallback,
+ .notify_failure = notify_execution_failure
+};
+
+static grub_err_t
+show_menu (grub_menu_t menu, int nested)
{
while (1)
{
int boot_entry;
grub_menu_entry_t e;
- int fallback_entry;
-
+
boot_entry = run_menu (menu, nested);
if (boot_entry < 0)
break;
@@ -507,34 +625,18 @@
e = get_entry (menu, boot_entry);
if (! e)
continue; /* Menu is empty. */
-
+
grub_cls ();
grub_setcursor (1);
- grub_printf (" Booting \'%s\'\n\n", e->title);
-
- run_menu_entry (e);
-
- /* Deal with a fallback entry. */
- /* FIXME: Multiple fallback entries like GRUB Legacy. */
- fallback_entry = get_entry_number ("fallback");
- if (fallback_entry >= 0)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
-
- e = get_entry (menu, fallback_entry);
- grub_env_unset ("fallback");
- grub_printf ("\n Falling back to \'%s\'\n\n", e->title);
- run_menu_entry (e);
- }
-
- if (grub_errno != GRUB_ERR_NONE)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
-
- grub_wait_after_message ();
- }
+ grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
}
+
+ return GRUB_ERR_NONE;
}
+
+struct grub_menu_viewer grub_normal_terminal_menu_viewer =
+{
+ .name = "terminal",
+ .show_menu = show_menu
+};
=== modified file 'term/gfxterm.c'
--- term/gfxterm.c 2008-07-03 14:28:01 +0000
+++ term/gfxterm.c 2008-07-19 19:56:06 +0000
@@ -28,12 +28,12 @@
#include
#include
#include
+#include
#include
#include
#define DEFAULT_VIDEO_WIDTH 640
#define DEFAULT_VIDEO_HEIGHT 480
-#define DEFAULT_VIDEO_FLAGS 0
#define DEFAULT_BORDER_WIDTH 10
@@ -108,10 +108,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,287 +245,129 @@
}
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;
-
- /* Select the font to use. */
- font_name = grub_env_get ("gfxterm_font");
- if (!font_name)
- font_name = ""; /* Allow fallback to any font. */
+ /* 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;
/* 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 x[x]*/
-
- /* 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;
- }
+ const char *modevar = grub_env_get ("gfxmode");
+ 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. */
+ struct grub_video_mode_info mode_info;
+ 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. */
+ char *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 +375,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 +450,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
@@ -786,7 +652,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,
@@ -795,10 +670,18 @@
/* Scroll physical screen. */
grub_video_scroll (color, 0, -virtual_screen.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)
write_cursor ();
}
+
+ if (repaint_callback)
+ repaint_callback (window.x, window.y, window.width, window.height);
}
static void
@@ -898,7 +781,7 @@
}
static grub_ssize_t
-grub_gfxterm_getcharwidth (grub_uint32_t c)
+grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused)))
{
#if 0
struct grub_font_glyph *glyph;
@@ -970,7 +853,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. */
@@ -1039,6 +923,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
@@ -1066,7 +955,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. */
@@ -1082,13 +971,13 @@
|| 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,
+ window.width,
+ window.height,
bitmap,
GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
if (grub_errno == GRUB_ERR_NONE)
@@ -1110,7 +999,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);
}
}
@@ -1141,9 +1030,16 @@
.next = 0
};
+grub_term_t
+grub_gfxterm_get_term (void)
+{
+ return &grub_video_term;
+}
+
GRUB_MOD_INIT(term_gfxterm)
{
my_mod = mod;
+ refcount = 0;
grub_term_register (&grub_video_term);
grub_register_command ("background_image",
=== modified file 'video/bitmap_scale.c'
--- video/bitmap_scale.c 2008-07-03 14:27:43 +0000
+++ video/bitmap_scale.c 2008-08-03 01:30:02 +0000
@@ -43,6 +43,7 @@
enum grub_video_bitmap_scale_method
scale_method)
{
+ *dst = 0;
/* Verify the simplifying assumptions. */
if (src == 0)
@@ -94,6 +95,7 @@
{
/* Destroy the bitmap and return the error code. */
grub_video_bitmap_destroy (*dst);
+ *dst = 0;
return ret;
}
}
=== modified file 'video/i386/pc/vbe.c'
--- video/i386/pc/vbe.c 2008-07-09 20:59:34 +0000
+++ video/i386/pc/vbe.c 2008-07-19 19:42:37 +0000
@@ -652,6 +652,8 @@
if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE)
return 1; /* Unable to set the display start. */
+ framebuffer.render_target.mode_info.mode_type
+ |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
return 0;
}
@@ -690,6 +692,8 @@
doublebuf_state.update_screen = doublebuf_blit_update_screen;
doublebuf_state.destroy = doublebuf_blit_destroy;
+ framebuffer.render_target.mode_info.mode_type
+ |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
return 0;
}
@@ -713,6 +717,9 @@
framebuffer.render_target.data = framebuffer.ptr;
doublebuf_state.update_screen = doublebuf_null_update_screen;
doublebuf_state.destroy = doublebuf_null_destroy;
+
+ framebuffer.render_target.mode_info.mode_type
+ &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
return 0;
}
=== added file 'video/setmode.c'
--- video/setmode.c 1970-01-01 00:00:00 +0000
+++ video/setmode.c 2008-07-19 19:31:46 +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 .
+ */
+
+#include
+#include
+#include
+#include
+
+/* Set the video mode based on the preferred modes specified in MODE_LIST in
+ the form: x[x][;...]
+
+ 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 x[x]*/
+
+ /* 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);
+}