# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: colin@gibibit.com-20080711163832-cutvrahspirds0fw
# target_branch: http://grub.gibibit.com/bzr/trunk-clean
# testament_sha1: 25c6cfc2b182753473e3e2647dd4a223f17a28ec
# timestamp: 2008-07-11 10:10:07 -0700
# source_branch: http://grub.gibibit.com/bzr/trunk-clean
# base_revision_id: colin@gibibit.com-20080711163747-7v43uh66u2lp4mb7
#
# Begin patch
=== modified file 'commands/videotest.c'
--- commands/videotest.c 2008-07-09 16:14:04 +0000
+++ commands/videotest.c 2008-07-09 16:52:26 +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-07-03 14:12:08 +0000
+++ conf/common.rmk 2008-07-07 15:07:13 +0000
@@ -287,6 +287,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 \
+ protomenu.mod \
read.mod sleep.mod loadenv.mod
# For hello.mod.
@@ -374,6 +375,11 @@
hexdump_mod_CFLAGS = $(COMMON_CFLAGS)
hexdump_mod_LDFLAGS = $(COMMON_LDFLAGS)
+# For protomenu.mod.
+protomenu_mod_SOURCES = protomenu/protomenu.c protomenu/widget-box.c
+protomenu_mod_CFLAGS = $(COMMON_CFLAGS)
+protomenu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
# For read.mod.
read_mod_SOURCES = commands/read.c
read_mod_CFLAGS = $(COMMON_CFLAGS)
=== modified file 'conf/i386-pc.rmk'
--- conf/i386-pc.rmk 2008-07-03 14:27:43 +0000
+++ conf/i386-pc.rmk 2008-07-10 13:55:34 +0000
@@ -46,11 +46,13 @@
kern/i386/tsc.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)
=== added file 'include/grub/menu.h'
--- include/grub/menu.h 1970-01-01 00:00:00 +0000
+++ include/grub/menu.h 2008-07-10 13:55:34 +0000
@@ -0,0 +1,51 @@
+/* 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
+
+/* 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;
+
+#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-11 14:56:59 +0000
@@ -0,0 +1,48 @@
+/*
+ * 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-03-26 12:01:02 +0000
+++ include/grub/normal.h 2008-07-11 16:03:24 +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,12 @@
/* To exit from the normal mode. */
extern grub_jmp_buf grub_exit_env;
+extern struct grub_menu_viewer grub_normal_terminal_menu_viewer;
+
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_entry_run (grub_menu_entry_t entry);
+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/proto_widgets.h'
--- include/grub/proto_widgets.h 1970-01-01 00:00:00 +0000
+++ include/grub/proto_widgets.h 2008-07-08 13:59:48 +0000
@@ -0,0 +1,48 @@
+/* proto_widgets.h - Widgets for the graphical menu prototype. */
+/*
+ * 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_PROTO_WIDGETS_HEADER
+#define GRUB_PROTO_WIDGETS_HEADER 1
+
+#include
+
+typedef struct grub_protomenu_widgets_box
+{
+ /* The size of the content. */
+ int width;
+ int height;
+
+ struct grub_video_bitmap **raw_pixmaps;
+ struct grub_video_bitmap **scaled_pixmaps;
+
+ void (*draw) (struct grub_protomenu_widgets_box *self, int x, int y);
+ void (*set_content_size) (struct grub_protomenu_widgets_box *self,
+ int width, int height);
+ int (*get_left_pad) (struct grub_protomenu_widgets_box *self);
+ int (*get_top_pad) (struct grub_protomenu_widgets_box *self);
+ void (*destroy) (struct grub_protomenu_widgets_box *self);
+}
+*grub_protomenu_widgets_box_t;
+
+grub_protomenu_widgets_box_t
+grub_protomenu_widgets_create_box (const char *pixmaps_prefix,
+ const char *pixmaps_suffix);
+
+#endif /* ! GRUB_PROTO_WIDGETS_HEADER */
+
=== modified file 'include/grub/video.h'
--- include/grub/video.h 2008-07-03 14:12:08 +0000
+++ include/grub/video.h 2008-07-07 15:07:13 +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. */
=== added file 'kern/menu_viewer.c'
--- kern/menu_viewer.c 1970-01-01 00:00:00 +0000
+++ kern/menu_viewer.c 2008-07-11 14:56:59 +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-02-02 16:48:52 +0000
+++ normal/main.c 2008-07-11 14:56:59 +0000
@@ -28,6 +28,7 @@
#include
#include
#include
+#include
grub_jmp_buf grub_exit_env;
@@ -476,7 +477,7 @@
if (menu && menu->size)
{
- grub_menu_run (menu, nested);
+ grub_menu_viewer_show_menu (menu, nested);
if (nested)
free_menu (menu);
}
@@ -519,6 +520,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 +538,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-11 16:03:24 +0000
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
static grub_uint8_t grub_color_menu_normal;
static grub_uint8_t grub_color_menu_highlight;
@@ -364,7 +365,7 @@
if (timeout > 0)
print_timeout (timeout, offset, 0);
- while (1)
+ while (!grub_menu_viewer_should_return ())
{
int c;
timeout = get_timeout ();
@@ -468,6 +469,10 @@
}
goto refresh;
+ case 't':
+ grub_env_set ("menuviewer", "protomenu");
+ goto refresh;
+
default:
break;
}
@@ -476,13 +481,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,8 +497,8 @@
grub_command_execute ("boot", 0);
}
-void
-grub_menu_run (grub_menu_t menu, int nested)
+static grub_err_t
+show_menu (grub_menu_t menu, int nested)
{
while (1)
{
@@ -513,7 +519,7 @@
grub_printf (" Booting \'%s\'\n\n", e->title);
- run_menu_entry (e);
+ grub_menu_execute_entry (e);
/* Deal with a fallback entry. */
/* FIXME: Multiple fallback entries like GRUB Legacy. */
@@ -526,7 +532,7 @@
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);
+ grub_menu_execute_entry (e);
}
if (grub_errno != GRUB_ERR_NONE)
@@ -537,4 +543,14 @@
grub_wait_after_message ();
}
}
+
+ return GRUB_ERR_NONE;
}
+
+struct grub_menu_viewer grub_normal_terminal_menu_viewer =
+{
+ .name = "terminal",
+ .show_menu = show_menu
+};
+
+
=== added directory 'protomenu'
=== added file 'protomenu/protomenu.c'
--- protomenu/protomenu.c 1970-01-01 00:00:00 +0000
+++ protomenu/protomenu.c 2008-07-11 16:03:24 +0000
@@ -0,0 +1,417 @@
+/* protomenu.c - Prototype graphical menu: command implementation. */
+/*
+ * 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
+
+typedef struct menu_item
+{
+ const char *name;
+ struct grub_video_bitmap *icon;
+ grub_menu_entry_t entry;
+}
+menu_item_t;
+
+static const int icon_width = 48;
+static const int icon_height = 48;
+static grub_video_rect_t screen;
+static grub_font_t title_font;
+static grub_font_t item_font;
+static grub_font_t status_font;
+static grub_video_color_t title_color;
+static grub_video_color_t item_color;
+static grub_video_color_t status_color;
+static grub_video_color_t status_bg_color;
+static struct grub_video_bitmap *background_image;
+static const char title_text[] = "GRUB 2 Boot Menu";
+static int num_items;
+static menu_item_t *items;
+static int selected_item_index;
+static grub_protomenu_widgets_box_t menu_box;
+static grub_protomenu_widgets_box_t selected_item_box;
+
+static grub_err_t
+menu_init (grub_menu_t menu)
+{
+ num_items = menu->size;
+ if (num_items < 1)
+ return grub_error (GRUB_ERR_MENU, "Empty menu");
+
+ items = grub_malloc (num_items * sizeof (*items));
+ if (!items)
+ return grub_errno;
+
+ int i;
+ grub_menu_entry_t cur;
+ for (i = 0, cur = menu->entry_list; i < num_items; i++, cur = cur->next)
+ {
+ items[i].name = cur->title;
+ items[i].icon = 0; /* TODO match w/ patterns to choose an icon */
+ items[i].entry = cur;
+ }
+
+ /*
+ items[0].name = "Ubuntu 8.10";
+ grub_video_bitmap_load (&img, "/boot/images/icon-ubuntu.tga");
+ if (grub_errno == GRUB_ERR_NONE)
+ {
+ grub_video_bitmap_create_scaled (&items[0].icon, icon_width,
+ icon_height, img,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ if (grub_errno != GRUB_ERR_NONE)
+ return;
+ grub_video_bitmap_destroy (img);
+ }
+ */
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+menu_destroy (void)
+{
+ if (items)
+ {
+ int i;
+ for (i = 0; i < num_items; i++)
+ {
+ if (items[i].icon)
+ {
+ grub_video_bitmap_destroy (items[i].icon);
+ items[i].icon = 0;
+ }
+ }
+ grub_free (items);
+ items = 0;
+ }
+}
+
+static grub_err_t
+style_init (void)
+{
+ if (!(title_font = grub_font_get ("Helvetica Bold 24"))
+ || !(item_font = grub_font_get ("Helvetica Bold 14"))
+ || !(status_font = grub_font_get ("Helvetica 10")))
+ return grub_errno;
+
+ title_color = grub_video_map_rgb (0, 0, 0);
+ item_color = grub_video_map_rgb (0, 0, 0);
+ status_color = grub_video_map_rgb (255, 255, 255);
+ status_bg_color = grub_video_map_rgba (0, 0, 0, 112);
+
+ struct grub_video_bitmap *img;
+ grub_video_bitmap_load (&img, "/boot/images/bg-protomenu1.tga");
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+
+ grub_video_bitmap_create_scaled (&background_image, screen.width,
+ screen.height, img,
+ GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST);
+ if (grub_errno != GRUB_ERR_NONE)
+ return grub_errno;
+ grub_video_bitmap_destroy (img);
+
+ menu_box = grub_protomenu_widgets_create_box ("/boot/images/menubox_", ".tga");
+ if (menu_box == 0)
+ return grub_error (GRUB_ERR_MENU, "Unable to create menu box");
+
+ selected_item_box = grub_protomenu_widgets_create_box ("/boot/images/select_blue_", ".tga");
+ if (selected_item_box == 0)
+ return grub_error (GRUB_ERR_MENU, "Unable to create selection box");
+
+ return GRUB_ERR_NONE;
+}
+
+static void
+style_destroy (void)
+{
+ if (menu_box)
+ {
+ menu_box->destroy(menu_box);
+ menu_box = 0;
+ }
+ if (selected_item_box)
+ {
+ selected_item_box->destroy(selected_item_box);
+ selected_item_box = 0;
+ }
+}
+
+static void
+draw_background (void)
+{
+ grub_video_blit_bitmap (background_image, GRUB_VIDEO_BLIT_REPLACE,
+ 0, 0, 0, 0,
+ grub_video_bitmap_get_width (background_image),
+ grub_video_bitmap_get_height (background_image));
+}
+
+static void
+draw_menu (void)
+{
+ int boxpad = 14;
+ int icon_text_space = 4;
+ int item_vspace = 16;
+
+ int ascent = grub_font_get_ascent (item_font);
+ int descent = grub_font_get_descent (item_font);
+ int item_height = 52;
+
+ grub_video_rect_t r;
+ r.width = screen.width * 2 / 3;
+ /* Set the menu box height to fit the items. */
+ r.height = (item_height * num_items
+ + item_vspace * (num_items - 1)
+ + 2 * boxpad);
+ r.x = (screen.width - r.width) / 2;
+ r.y = (screen.height - r.height) / 2;
+ menu_box->set_content_size (menu_box, r.width, r.height);
+
+ int menu_box_left_pad = menu_box->get_left_pad (menu_box);
+ int menu_box_top_pad = menu_box->get_top_pad (menu_box);
+ menu_box->draw (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++)
+ {
+ if (i == selected_item_index)
+ {
+ selected_item_box->set_content_size (selected_item_box,
+ r.width - 2 * boxpad,
+ item_height);
+ int leftpad = selected_item_box->get_left_pad (selected_item_box);
+ int toppad = selected_item_box->get_top_pad (selected_item_box);
+ selected_item_box->draw (selected_item_box,
+ item_left - leftpad,
+ item_top - toppad);
+ }
+
+ if (items[i].icon)
+ grub_video_blit_bitmap (items[i].icon, GRUB_VIDEO_BLIT_BLEND,
+ item_left,
+ item_top + (item_height - icon_height) / 2,
+ 0, 0, icon_width, icon_height);
+
+ grub_video_draw_string (items[i].name, item_font, item_color,
+ item_left + icon_width + icon_text_space,
+ (item_top + (item_height - (ascent + descent))
+ / 2 + ascent));
+
+ item_top += item_height + item_vspace;
+ }
+}
+
+static void
+draw_title (void)
+{
+ /* Center the title. */
+ int title_width = grub_font_get_string_width (title_font, title_text);
+ int x = (screen.width - title_width) / 2;
+ int y = 40 + grub_font_get_ascent (title_font);
+ grub_video_draw_string (title_text, title_font, title_color, x, y);
+}
+
+static void
+draw_status (void)
+{
+ int descent = grub_font_get_descent (status_font);
+ int ascent = grub_font_get_ascent (status_font);
+ int vpad = 5;
+ int textheight = descent + ascent + 1;
+ int h = 2 * vpad + 2 * textheight;
+
+ grub_video_fill_rect (status_bg_color, 0, screen.height - h,
+ screen.width, screen.height - 1);
+
+ int texty = screen.height - h + vpad + ascent;
+ grub_video_draw_string ("Select an item with the arrow keys and "
+ "press Enter to boot.",
+ status_font, status_color, 30, texty);
+ texty += textheight;
+ grub_video_draw_string ("Press: 'c' for command line; 't' to switch to "
+ "non-graphical menu.",
+ status_font, status_color, 30, texty);
+
+}
+
+static grub_err_t
+set_graphics_mode (void)
+{
+ const char *nodoublebuf_str = grub_env_get ("protomenu_nodoublebuf");
+ int doublebuf_flags =
+ (nodoublebuf_str && nodoublebuf_str[0] == 'y')
+ ? 0
+ : GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED;
+
+ if (grub_video_setup (1024, 768, GRUB_VIDEO_MODE_TYPE_RGB | doublebuf_flags)
+ != GRUB_ERR_NONE)
+ return grub_errno;
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+set_text_mode (void)
+{
+ return grub_video_restore ();
+}
+
+/* TODO set the 'terminal' type to console for now, here.
+ (until gfxterm use is integrated). */
+static grub_err_t
+show_menu (grub_menu_t menu, int nested __attribute__ ((unused)))
+{
+ set_graphics_mode ();
+
+ grub_error_push ();
+ grub_video_get_viewport ((unsigned *) &screen.x,
+ (unsigned *) &screen.y,
+ (unsigned *) &screen.width,
+ (unsigned *) &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 (100, 100, 100),
+ screen.x, screen.y, screen.width, screen.height);
+ grub_video_swap_buffers ();
+
+ if (menu_init (menu) != GRUB_ERR_NONE)
+ goto menufail;
+
+ if (style_init () != GRUB_ERR_NONE)
+ goto stylefail;
+
+ selected_item_index = 0;
+ int done = 0;
+ while (!done && !grub_menu_viewer_should_return ())
+ {
+ draw_background ();
+ draw_menu ();
+ draw_title ();
+ draw_status ();
+ grub_video_swap_buffers ();
+
+ int c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
+ if (c == 'j' || c == 14)
+ {
+ selected_item_index++;
+ if (selected_item_index >= num_items)
+ selected_item_index = 0;
+ }
+ else if (c == 'k' || c == 16)
+ {
+ selected_item_index--;
+ if (selected_item_index < 0)
+ selected_item_index = num_items - 1;
+ }
+ else if (c == '\r' || c == '\n' || c == 6)
+ {
+ if (selected_item_index >=0 && selected_item_index < num_items)
+ {
+ menu_item_t *item = &items[selected_item_index];
+ if (item->entry != 0)
+ {
+ set_text_mode ();
+ grub_menu_execute_entry (item->entry);
+ if (grub_errno != GRUB_ERR_NONE)
+ grub_wait_after_message ();
+ if (set_graphics_mode () != GRUB_ERR_NONE)
+ done = 1;
+ }
+ }
+ }
+ else if (c == 'c')
+ {
+ set_text_mode ();
+ grub_cmdline_run (1);
+ if (grub_errno != GRUB_ERR_NONE)
+ grub_wait_after_message ();
+ if (set_graphics_mode () != GRUB_ERR_NONE)
+ done = 1;
+ }
+ else if (c == 't')
+ {
+ /* The write hook for 'menuviewer' will cause
+ * grub_menu_viewer_should_return to return nonzero. */
+ grub_env_set ("menuviewer", "terminal");
+ }
+ else if (nested && c == GRUB_TERM_ESC)
+ {
+ done = 1;
+ }
+ }
+
+ style_destroy ();
+stylefail:
+ menu_destroy ();
+menufail:
+ set_text_mode ();
+
+ grub_print_error ();
+
+ return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_protomenu (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");
+
+ show_menu (menu, 1);
+
+ return 0;
+}
+
+static struct grub_menu_viewer menu_viewer =
+{
+ .name = "protomenu",
+ .show_menu = show_menu
+};
+
+GRUB_MOD_INIT (protomenu)
+{
+ (void) mod; /* To stop warning. */
+ grub_register_command ("protomenu",
+ grub_cmd_protomenu, GRUB_COMMAND_FLAG_BOTH,
+ "protomenu", "Show graphical menu prototype", 0);
+ grub_menu_viewer_register (&menu_viewer);
+}
+
+GRUB_MOD_FINI (protomenu)
+{
+ grub_unregister_command ("protomenu");
+}
=== added file 'protomenu/widget-box.c'
--- protomenu/widget-box.c 1970-01-01 00:00:00 +0000
+++ protomenu/widget-box.c 2008-07-08 13:59:48 +0000
@@ -0,0 +1,245 @@
+/* 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 (struct grub_protomenu_widgets_box *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_protomenu_widgets_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_protomenu_widgets_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 (struct grub_protomenu_widgets_box *self)
+{
+ return grub_video_bitmap_get_width (self->raw_pixmaps[BOX_PIXMAP_W]);
+}
+
+static int
+get_top_pad (struct grub_protomenu_widgets_box *self)
+{
+ return grub_video_bitmap_get_height (self->raw_pixmaps[BOX_PIXMAP_N]);
+}
+
+static void
+destroy (struct grub_protomenu_widgets_box *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_protomenu_widgets_box_t
+grub_protomenu_widgets_create_box (const char *pixmaps_prefix,
+ const char *pixmaps_suffix)
+{
+ char path[200];
+ unsigned i;
+ grub_protomenu_widgets_box_t box;
+
+ box = (grub_protomenu_widgets_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 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;
+}
# Begin bundle
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
RUP/4u5IpwoSCKoPHUA=