Hi,

This patch moves menu_text.c to menu/text/, and it changes
grub_menu_viewer structure as handler.

-- 
Bean
diff --git a/conf/common.rmk b/conf/common.rmk
index 00aff3d..fac3c1a 100644
--- a/conf/common.rmk
+++ b/conf/common.rmk
@@ -342,7 +342,7 @@ pkglib_MODULES += minicmd.mod extcmd.mod hello.mod handler.mod	\
 	loopback.mod fs_uuid.mod configfile.mod echo.mod	\
 	terminfo.mod test.mod blocklist.mod hexdump.mod		\
 	read.mod sleep.mod loadenv.mod crc.mod parttool.mod	\
-	pcpart.mod memrw.mod normal.mod sh.mod
+	pcpart.mod memrw.mod normal.mod sh.mod textmenu.mod
 
 # For minicmd.mod.
 minicmd_mod_SOURCES = commands/minicmd.c
@@ -466,10 +466,9 @@ memrw_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For normal.mod.
 normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \
-	normal/autofs.c normal/handler.c \
-	normal/color.c normal/completion.c normal/datetime.c normal/menu.c \
-	normal/menu_entry.c normal/menu_text.c normal/menu_viewer.c \
-	normal/misc.c
+	normal/autofs.c normal/handler.c normal/completion.c \
+	normal/datetime.c normal/misc.c normal/menu_viewer.c \
+	normal/color.c normal/menu.c normal/menu_entry.c
 normal_mod_CFLAGS = $(COMMON_CFLAGS)
 normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
@@ -479,6 +478,11 @@ sh_mod_SOURCES = script/sh/main.c script/sh/script.c script/sh/execute.c \
 sh_mod_CFLAGS = $(COMMON_CFLAGS)
 sh_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
+# For textmenu.mod.
+textmenu_mod_SOURCES = menu/text/menu_text.c
+textmenu_mod_CFLAGS = $(COMMON_CFLAGS)
+textmenu_mod_LDFLAGS = $(COMMON_LDFLAGS)
+
 # Common Video Subsystem specific modules.
 pkglib_MODULES += video.mod videotest.mod bitmap.mod tga.mod jpeg.mod	\
 	png.mod	font.mod gfxterm.mod
diff --git a/include/grub/menu_viewer.h b/include/grub/menu_viewer.h
index 725c975..bd6b937 100644
--- a/include/grub/menu_viewer.h
+++ b/include/grub/menu_viewer.h
@@ -24,19 +24,43 @@
 #include <grub/symbol.h>
 #include <grub/types.h>
 #include <grub/menu.h>
+#include <grub/handler.h>
 
 struct grub_menu_viewer
 {
+  struct grub_menu_viewer *next;
+
   /* The menu viewer name.  */
   const char *name;
 
-  grub_err_t (*show_menu) (grub_menu_t menu, int nested);
+  grub_err_t (*init) (void);
 
-  struct grub_menu_viewer *next;
+  grub_err_t (*fini) (void);
+
+  grub_err_t (*show_menu) (grub_menu_t menu, int nested);
 };
 typedef struct grub_menu_viewer *grub_menu_viewer_t;
 
-void grub_menu_viewer_register (grub_menu_viewer_t viewer);
+extern struct grub_handler_class grub_menu_viewer_class;
+
+static inline void
+grub_menu_viewer_register (const char *name __attribute__ ((unused)),
+			   grub_menu_viewer_t viewer)
+{
+  grub_handler_register (&grub_menu_viewer_class, GRUB_AS_HANDLER (viewer));
+}
+
+static inline void
+grub_menu_viewer_unregister (grub_menu_viewer_t viewer)
+{
+  grub_handler_unregister (&grub_menu_viewer_class, GRUB_AS_HANDLER (viewer));
+}
+
+static inline grub_menu_viewer_t
+grub_menu_viewer_get_current (void)
+{
+  return (grub_menu_viewer_t) grub_menu_viewer_class.cur_handler;
+}
 
 grub_err_t grub_menu_viewer_show_menu (grub_menu_t menu, int nested);
 
diff --git a/include/grub/normal.h b/include/grub/normal.h
index 948289a..cf61f07 100644
--- a/include/grub/normal.h
+++ b/include/grub/normal.h
@@ -41,8 +41,6 @@ enum grub_completion_type
   };
 typedef enum grub_completion_type grub_completion_type_t;
 
-extern struct grub_menu_viewer grub_normal_text_menu_viewer;
-
 /* Callback structure menu viewers can use to provide user feedback when
    default entries are executed, possibly including fallback entries.  */
 typedef struct grub_menu_execute_callback
diff --git a/menu/text/menu_text.c b/menu/text/menu_text.c
new file mode 100644
index 0000000..35ff623
--- /dev/null
+++ b/menu/text/menu_text.c
@@ -0,0 +1,614 @@
+/* main.c - Basic text menu implementation.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/term.h>
+#include <grub/misc.h>
+#include <grub/loader.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/env.h>
+#include <grub/menu_viewer.h>
+#include <grub/menu.h>
+#include <grub/normal.h>
+
+/* Time to delay after displaying an error message about a default/fallback
+   entry failing to boot.  */
+#define DEFAULT_ENTRY_ERROR_DELAY_MS  2500
+
+static grub_uint8_t grub_color_menu_normal;
+static grub_uint8_t grub_color_menu_highlight;
+
+/* Wait until the user pushes any key so that the user
+   can see what happened.  */
+void
+grub_wait_after_message (void)
+{
+  grub_printf ("\nPress any key to continue...");
+  (void) grub_getkey ();
+}
+
+static void
+draw_border (void)
+{
+  unsigned i;
+
+  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
+
+  grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
+  grub_putcode (GRUB_TERM_DISP_UL);
+  for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
+    grub_putcode (GRUB_TERM_DISP_HLINE);
+  grub_putcode (GRUB_TERM_DISP_UR);
+
+  for (i = 0; i < (unsigned) GRUB_TERM_NUM_ENTRIES; i++)
+    {
+      grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
+      grub_putcode (GRUB_TERM_DISP_VLINE);
+      grub_gotoxy (GRUB_TERM_MARGIN + GRUB_TERM_BORDER_WIDTH - 1,
+		   GRUB_TERM_TOP_BORDER_Y + i + 1);
+      grub_putcode (GRUB_TERM_DISP_VLINE);
+    }
+
+  grub_gotoxy (GRUB_TERM_MARGIN,
+	       GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES + 1);
+  grub_putcode (GRUB_TERM_DISP_LL);
+  for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
+    grub_putcode (GRUB_TERM_DISP_HLINE);
+  grub_putcode (GRUB_TERM_DISP_LR);
+
+  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
+
+  grub_gotoxy (GRUB_TERM_MARGIN,
+	       (GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES
+		+ GRUB_TERM_MARGIN + 1));
+}
+
+static void
+print_message (int nested, int edit)
+{
+  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
+
+  if (edit)
+    {
+      grub_printf ("\n\
+      Minimum Emacs-like screen editing is supported. TAB lists\n\
+      completions. Press Ctrl-x to boot, Ctrl-c for a command-line\n\
+      or ESC to return menu.");
+    }
+  else
+    {
+      grub_printf ("\n\
+      Use the %C and %C keys to select which entry is highlighted.\n",
+		   (grub_uint32_t) GRUB_TERM_DISP_UP, (grub_uint32_t) GRUB_TERM_DISP_DOWN);
+      grub_printf ("\
+      Press enter to boot the selected OS, \'e\' to edit the\n\
+      commands before booting or \'c\' for a command-line.");
+      if (nested)
+	grub_printf ("\n\
+      ESC to return previous menu.");
+    }
+}
+
+static void
+print_entry (int y, int highlight, grub_menu_entry_t entry)
+{
+  int x;
+  const char *title;
+  grub_size_t title_len;
+  grub_ssize_t len;
+  grub_uint32_t *unicode_title;
+  grub_ssize_t i;
+  grub_uint8_t old_color_normal, old_color_highlight;
+
+  title = entry ? entry->title : "";
+  title_len = grub_strlen (title);
+  unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
+  if (! unicode_title)
+    /* XXX How to show this error?  */
+    return;
+
+  len = grub_utf8_to_ucs4 (unicode_title, title_len,
+			   (grub_uint8_t *) title, -1, 0);
+  if (len < 0)
+    {
+      /* It is an invalid sequence.  */
+      grub_free (unicode_title);
+      return;
+    }
+
+  grub_getcolor (&old_color_normal, &old_color_highlight);
+  grub_setcolor (grub_color_menu_normal, grub_color_menu_highlight);
+  grub_setcolorstate (highlight
+		      ? GRUB_TERM_COLOR_HIGHLIGHT
+		      : GRUB_TERM_COLOR_NORMAL);
+
+  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
+
+  for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
+       x < GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH - GRUB_TERM_MARGIN;
+       i++)
+    {
+      if (i < len
+	  && x <= (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
+		   - GRUB_TERM_MARGIN - 1))
+	{
+	  grub_ssize_t width;
+
+	  width = grub_getcharwidth (unicode_title[i]);
+
+	  if (x + width > (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
+			   - GRUB_TERM_MARGIN - 1))
+	    grub_putcode (GRUB_TERM_DISP_RIGHT);
+	  else
+	    grub_putcode (unicode_title[i]);
+
+	  x += width;
+	}
+      else
+	{
+	  grub_putchar (' ');
+	  x++;
+	}
+    }
+  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
+  grub_putchar (' ');
+
+  grub_gotoxy (GRUB_TERM_CURSOR_X, y);
+
+  grub_setcolor (old_color_normal, old_color_highlight);
+  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
+  grub_free (unicode_title);
+}
+
+static void
+print_entries (grub_menu_t menu, int first, int offset)
+{
+  grub_menu_entry_t e;
+  int i;
+
+  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
+	       GRUB_TERM_FIRST_ENTRY_Y);
+
+  if (first)
+    grub_putcode (GRUB_TERM_DISP_UP);
+  else
+    grub_putchar (' ');
+
+  e = grub_menu_get_entry (menu, first);
+
+  for (i = 0; i < GRUB_TERM_NUM_ENTRIES; i++)
+    {
+      print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e);
+      if (e)
+	e = e->next;
+    }
+
+  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
+	       GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES);
+
+  if (e)
+    grub_putcode (GRUB_TERM_DISP_DOWN);
+  else
+    grub_putchar (' ');
+
+  grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
+}
+
+/* Initialize the screen.  If NESTED is non-zero, assume that this menu
+   is run from another menu or a command-line. If EDIT is non-zero, show
+   a message for the menu entry editor.  */
+void
+grub_menu_init_page (int nested, int edit)
+{
+  grub_uint8_t old_color_normal, old_color_highlight;
+
+  grub_getcolor (&old_color_normal, &old_color_highlight);
+
+  /* By default, use the same colors for the menu.  */
+  grub_color_menu_normal = old_color_normal;
+  grub_color_menu_highlight = old_color_highlight;
+
+  /* Then give user a chance to replace them.  */
+  grub_parse_color_name_pair (&grub_color_menu_normal, grub_env_get ("menu_color_normal"));
+  grub_parse_color_name_pair (&grub_color_menu_highlight, grub_env_get ("menu_color_highlight"));
+
+  grub_normal_init_page ();
+
+  grub_setcolor (grub_color_menu_normal, grub_color_menu_highlight);
+  draw_border ();
+  grub_setcolor (old_color_normal, old_color_highlight);
+  print_message (nested, edit);
+}
+
+/* Get the entry number from the variable NAME.  */
+static int
+get_entry_number (const char *name)
+{
+  char *val;
+  int entry;
+
+  val = grub_env_get (name);
+  if (! val)
+    return -1;
+
+  grub_error_push ();
+
+  entry = (int) grub_strtoul (val, 0, 0);
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_errno = GRUB_ERR_NONE;
+      entry = -1;
+    }
+
+  grub_error_pop ();
+
+  return entry;
+}
+
+static void
+print_timeout (int timeout, int offset, int second_stage)
+{
+  /* NOTE: Do not remove the trailing space characters.
+     They are required to clear the line.  */
+  char *msg = "   The highlighted entry will be booted automatically in %ds.    ";
+  char *msg_end = grub_strchr (msg, '%');
+
+  grub_gotoxy (second_stage ? (msg_end - msg) : 0, GRUB_TERM_HEIGHT - 3);
+  grub_printf (second_stage ? msg_end : msg, timeout);
+  grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
+  grub_refresh ();
+};
+
+/* Show the menu and handle menu entry selection.  Returns the menu entry
+   index that should be executed or -1 if no entry should be executed (e.g.,
+   Esc pressed to exit a sub-menu or switching menu viewers).
+   If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
+   entry to be executed is a result of an automatic default selection because
+   of the timeout.  */
+static int
+run_menu (grub_menu_t menu, int nested, int *auto_boot)
+{
+  int first, offset;
+  grub_uint64_t saved_time;
+  int default_entry;
+  int timeout;
+
+  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;
+
+  /* If timeout is 0, drawing is pointless (and ugly).  */
+  if (grub_menu_get_timeout () == 0)
+    {
+      *auto_boot = 1;
+      return default_entry;
+    }
+
+  offset = default_entry;
+  if (offset > GRUB_TERM_NUM_ENTRIES - 1)
+    {
+      first = offset - (GRUB_TERM_NUM_ENTRIES - 1);
+      offset = GRUB_TERM_NUM_ENTRIES - 1;
+    }
+
+  /* Initialize the time.  */
+  saved_time = grub_get_time_ms ();
+
+ refresh:
+  grub_setcursor (0);
+  grub_menu_init_page (nested, 0);
+  print_entries (menu, first, offset);
+  grub_refresh ();
+
+  timeout = grub_menu_get_timeout ();
+
+  if (timeout > 0)
+    print_timeout (timeout, offset, 0);
+
+  while (1)
+    {
+      int c;
+      timeout = grub_menu_get_timeout ();
+
+      if (timeout > 0)
+	{
+	  grub_uint64_t current_time;
+
+	  current_time = grub_get_time_ms ();
+	  if (current_time - saved_time >= 1000)
+	    {
+	      timeout--;
+	      grub_menu_set_timeout (timeout);
+	      saved_time = current_time;
+	      print_timeout (timeout, offset, 1);
+	    }
+	}
+
+      if (timeout == 0)
+	{
+	  grub_env_unset ("timeout");
+	  *auto_boot = 1;
+	  return default_entry;
+	}
+
+      if (grub_checkkey () >= 0 || timeout < 0)
+	{
+	  c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
+
+	  if (timeout >= 0)
+	    {
+	      grub_gotoxy (0, GRUB_TERM_HEIGHT - 3);
+	      grub_printf ("\
+									");
+	      grub_env_unset ("timeout");
+	      grub_env_unset ("fallback");
+	      grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
+	    }
+
+	  switch (c)
+	    {
+	    case GRUB_TERM_HOME:
+	      first = 0;
+	      offset = 0;
+	      print_entries (menu, first, offset);
+	      break;
+
+	    case GRUB_TERM_END:
+	      offset = menu->size - 1;
+	      if (offset > GRUB_TERM_NUM_ENTRIES - 1)
+		{
+		  first = offset - (GRUB_TERM_NUM_ENTRIES - 1);
+		  offset = GRUB_TERM_NUM_ENTRIES - 1;
+		}
+		print_entries (menu, first, offset);
+	      break;
+
+	    case GRUB_TERM_UP:
+	    case '^':
+	      if (offset > 0)
+		{
+		  print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
+			       grub_menu_get_entry (menu, first + offset));
+		  offset--;
+		  print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
+			       grub_menu_get_entry (menu, first + offset));
+		}
+	      else if (first > 0)
+		{
+		  first--;
+		  print_entries (menu, first, offset);
+		}
+	      break;
+
+	    case GRUB_TERM_DOWN:
+	    case 'v':
+	      if (menu->size > first + offset + 1)
+		{
+		  if (offset < GRUB_TERM_NUM_ENTRIES - 1)
+		    {
+		      print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
+				   grub_menu_get_entry (menu, first + offset));
+		      offset++;
+		      print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
+				   grub_menu_get_entry (menu, first + offset));
+		    }
+		  else
+		    {
+		      first++;
+		      print_entries (menu, first, offset);
+		    }
+		}
+	      break;
+
+	    case GRUB_TERM_PPAGE:
+	      if (first == 0)
+		{
+		  offset = 0;
+		}
+	      else
+		{
+		  first -= GRUB_TERM_NUM_ENTRIES;
+
+		  if (first < 0)
+		    {
+		      offset += first;
+		      first = 0;
+		    }
+		}
+	      print_entries (menu, first, offset);
+	      break;
+
+	    case GRUB_TERM_NPAGE:
+	      if (offset == 0)
+		{
+		  offset += GRUB_TERM_NUM_ENTRIES - 1;
+		  if (first + offset >= menu->size)
+		    {
+		      offset = menu->size - first - 1;
+		    }
+		}
+	      else
+		{
+		  first += GRUB_TERM_NUM_ENTRIES;
+
+		  if (first + offset >= menu->size)
+		    {
+		      first -= GRUB_TERM_NUM_ENTRIES;
+		      offset += GRUB_TERM_NUM_ENTRIES;
+
+		      if (offset > menu->size - 1 ||
+			  offset > GRUB_TERM_NUM_ENTRIES - 1)
+			{
+			  offset = menu->size - first - 1;
+			}
+		      if (offset > GRUB_TERM_NUM_ENTRIES)
+			{
+			  first += offset - GRUB_TERM_NUM_ENTRIES + 1;
+			  offset = GRUB_TERM_NUM_ENTRIES - 1;
+			}
+		    }
+		}
+	      print_entries (menu, first, offset);
+	      break;
+
+	    case '\n':
+	    case '\r':
+	    case 6:
+	      grub_setcursor (1);
+	      *auto_boot = 0;
+	      return first + offset;
+
+	    case '\e':
+	      if (nested)
+		{
+		  grub_setcursor (1);
+		  return -1;
+		}
+	      break;
+
+	    case 'c':
+	      grub_cmdline_run (nested);
+	      goto refresh;
+
+	    case 'e':
+		{
+		  grub_menu_entry_t e = grub_menu_get_entry (menu, first + offset);
+		  if (e)
+		    grub_menu_entry_run (e);
+		}
+	      goto refresh;
+
+	    default:
+	      break;
+	    }
+
+	  grub_refresh ();
+	}
+    }
+
+  /* Never reach here.  */
+  return -1;
+}
+
+/* Callback invoked immediately before a menu entry is executed.  */
+static void
+notify_booting (grub_menu_entry_t entry,
+		void *userdata __attribute__((unused)))
+{
+  grub_printf ("  Booting \'%s\'\n\n", entry->title);
+}
+
+/* Callback invoked when a default menu entry executed because of a timeout
+   has failed and an attempt will be made to execute the next fallback
+   entry, ENTRY.  */
+static void
+notify_fallback (grub_menu_entry_t entry,
+		 void *userdata __attribute__((unused)))
+{
+  grub_printf ("\n  Falling back to \'%s\'\n\n", entry->title);
+  grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
+}
+
+/* Callback invoked when a menu entry has failed and there is no remaining
+   fallback entry to attempt.  */
+static void
+notify_execution_failure (void *userdata __attribute__((unused)))
+{
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+    }
+  grub_printf ("\n  Failed to boot default entries.\n");
+  grub_wait_after_message ();
+}
+
+/* Callbacks used by the text menu to provide user feedback when menu entries
+   are executed.  */
+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_text_menu (grub_menu_t menu, int nested)
+{
+  while (1)
+    {
+      int boot_entry;
+      grub_menu_entry_t e;
+      int auto_boot;
+
+      boot_entry = run_menu (menu, nested, &auto_boot);
+      if (boot_entry < 0)
+	break;
+
+      e = grub_menu_get_entry (menu, boot_entry);
+      if (! e)
+	continue; /* Menu is empty.  */
+
+      grub_cls ();
+      grub_setcursor (1);
+
+      if (auto_boot)
+	{
+	  grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
+	}
+      else
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  grub_menu_execute_entry (e);
+	  if (grub_errno != GRUB_ERR_NONE)
+	    {
+	      grub_print_error ();
+	      grub_errno = GRUB_ERR_NONE;
+	      grub_wait_after_message ();
+	    }
+	}
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+struct grub_menu_viewer grub_text_menu_viewer =
+{
+  .name = "text",
+  .show_menu = show_text_menu
+};
+
+GRUB_MOD_INIT(textmenu)
+{
+  (void) mod;
+
+  grub_menu_viewer_register ("text", &grub_text_menu_viewer);
+}
+
+GRUB_MOD_FINI(textmenu)
+{
+  grub_menu_viewer_unregister (&grub_text_menu_viewer);
+}
diff --git a/normal/main.c b/normal/main.c
index 2df53be..b9b6f2c 100644
--- a/normal/main.c
+++ b/normal/main.c
@@ -537,8 +537,6 @@ GRUB_MOD_INIT(normal)
   if (mod)
     grub_dl_ref (mod);
 
-  grub_menu_viewer_register (&grub_normal_text_menu_viewer);
-
   grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
 
   grub_reader_register ("normal", &grub_normal_reader);
diff --git a/normal/menu_text.c b/normal/menu_text.c
deleted file mode 100644
index 0c4461f..0000000
--- a/normal/menu_text.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/* menu_text.c - Basic text menu implementation.  */
-/*
- *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
- *
- *  GRUB is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  GRUB is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <grub/normal.h>
-#include <grub/term.h>
-#include <grub/misc.h>
-#include <grub/loader.h>
-#include <grub/mm.h>
-#include <grub/time.h>
-#include <grub/env.h>
-#include <grub/menu_viewer.h>
-
-/* Time to delay after displaying an error message about a default/fallback
-   entry failing to boot.  */
-#define DEFAULT_ENTRY_ERROR_DELAY_MS  2500
-
-static grub_uint8_t grub_color_menu_normal;
-static grub_uint8_t grub_color_menu_highlight;
-
-/* Wait until the user pushes any key so that the user
-   can see what happened.  */
-void
-grub_wait_after_message (void)
-{
-  grub_printf ("\nPress any key to continue...");
-  (void) grub_getkey ();
-}
-
-static void
-draw_border (void)
-{
-  unsigned i;
-
-  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
-
-  grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
-  grub_putcode (GRUB_TERM_DISP_UL);
-  for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
-    grub_putcode (GRUB_TERM_DISP_HLINE);
-  grub_putcode (GRUB_TERM_DISP_UR);
-
-  for (i = 0; i < (unsigned) GRUB_TERM_NUM_ENTRIES; i++)
-    {
-      grub_gotoxy (GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
-      grub_putcode (GRUB_TERM_DISP_VLINE);
-      grub_gotoxy (GRUB_TERM_MARGIN + GRUB_TERM_BORDER_WIDTH - 1,
-		   GRUB_TERM_TOP_BORDER_Y + i + 1);
-      grub_putcode (GRUB_TERM_DISP_VLINE);
-    }
-
-  grub_gotoxy (GRUB_TERM_MARGIN,
-	       GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES + 1);
-  grub_putcode (GRUB_TERM_DISP_LL);
-  for (i = 0; i < (unsigned) GRUB_TERM_BORDER_WIDTH - 2; i++)
-    grub_putcode (GRUB_TERM_DISP_HLINE);
-  grub_putcode (GRUB_TERM_DISP_LR);
-
-  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
-
-  grub_gotoxy (GRUB_TERM_MARGIN,
-	       (GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES
-		+ GRUB_TERM_MARGIN + 1));
-}
-
-static void
-print_message (int nested, int edit)
-{
-  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
-
-  if (edit)
-    {
-      grub_printf ("\n\
-      Minimum Emacs-like screen editing is supported. TAB lists\n\
-      completions. Press Ctrl-x to boot, Ctrl-c for a command-line\n\
-      or ESC to return menu.");
-    }
-  else
-    {
-      grub_printf ("\n\
-      Use the %C and %C keys to select which entry is highlighted.\n",
-		   (grub_uint32_t) GRUB_TERM_DISP_UP, (grub_uint32_t) GRUB_TERM_DISP_DOWN);
-      grub_printf ("\
-      Press enter to boot the selected OS, \'e\' to edit the\n\
-      commands before booting or \'c\' for a command-line.");
-      if (nested)
-	grub_printf ("\n\
-      ESC to return previous menu.");
-    }
-}
-
-static void
-print_entry (int y, int highlight, grub_menu_entry_t entry)
-{
-  int x;
-  const char *title;
-  grub_size_t title_len;
-  grub_ssize_t len;
-  grub_uint32_t *unicode_title;
-  grub_ssize_t i;
-  grub_uint8_t old_color_normal, old_color_highlight;
-
-  title = entry ? entry->title : "";
-  title_len = grub_strlen (title);
-  unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
-  if (! unicode_title)
-    /* XXX How to show this error?  */
-    return;
-
-  len = grub_utf8_to_ucs4 (unicode_title, title_len,
-                           (grub_uint8_t *) title, -1, 0);
-  if (len < 0)
-    {
-      /* It is an invalid sequence.  */
-      grub_free (unicode_title);
-      return;
-    }
-
-  grub_getcolor (&old_color_normal, &old_color_highlight);
-  grub_setcolor (grub_color_menu_normal, grub_color_menu_highlight);
-  grub_setcolorstate (highlight
-		      ? GRUB_TERM_COLOR_HIGHLIGHT
-		      : GRUB_TERM_COLOR_NORMAL);
-
-  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
-
-  for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
-       x < GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH - GRUB_TERM_MARGIN;
-       i++)
-    {
-      if (i < len
-	  && x <= (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
-		   - GRUB_TERM_MARGIN - 1))
-	{
-	  grub_ssize_t width;
-
-	  width = grub_getcharwidth (unicode_title[i]);
-
-	  if (x + width > (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH
-			   - GRUB_TERM_MARGIN - 1))
-	    grub_putcode (GRUB_TERM_DISP_RIGHT);
-	  else
-	    grub_putcode (unicode_title[i]);
-
-	  x += width;
-	}
-      else
-	{
-	  grub_putchar (' ');
-	  x++;
-	}
-    }
-  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
-  grub_putchar (' ');
-
-  grub_gotoxy (GRUB_TERM_CURSOR_X, y);
-
-  grub_setcolor (old_color_normal, old_color_highlight);
-  grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
-  grub_free (unicode_title);
-}
-
-static void
-print_entries (grub_menu_t menu, int first, int offset)
-{
-  grub_menu_entry_t e;
-  int i;
-
-  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
-	       GRUB_TERM_FIRST_ENTRY_Y);
-
-  if (first)
-    grub_putcode (GRUB_TERM_DISP_UP);
-  else
-    grub_putchar (' ');
-
-  e = grub_menu_get_entry (menu, first);
-
-  for (i = 0; i < GRUB_TERM_NUM_ENTRIES; i++)
-    {
-      print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e);
-      if (e)
-	e = e->next;
-    }
-
-  grub_gotoxy (GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_BORDER_WIDTH,
-	       GRUB_TERM_TOP_BORDER_Y + GRUB_TERM_NUM_ENTRIES);
-
-  if (e)
-    grub_putcode (GRUB_TERM_DISP_DOWN);
-  else
-    grub_putchar (' ');
-
-  grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
-}
-
-/* Initialize the screen.  If NESTED is non-zero, assume that this menu
-   is run from another menu or a command-line. If EDIT is non-zero, show
-   a message for the menu entry editor.  */
-void
-grub_menu_init_page (int nested, int edit)
-{
-  grub_uint8_t old_color_normal, old_color_highlight;
-
-  grub_getcolor (&old_color_normal, &old_color_highlight);
-
-  /* By default, use the same colors for the menu.  */
-  grub_color_menu_normal = old_color_normal;
-  grub_color_menu_highlight = old_color_highlight;
-
-  /* Then give user a chance to replace them.  */
-  grub_parse_color_name_pair (&grub_color_menu_normal, grub_env_get ("menu_color_normal"));
-  grub_parse_color_name_pair (&grub_color_menu_highlight, grub_env_get ("menu_color_highlight"));
-
-  grub_normal_init_page ();
-  grub_setcolor (grub_color_menu_normal, grub_color_menu_highlight);
-  draw_border ();
-  grub_setcolor (old_color_normal, old_color_highlight);
-  print_message (nested, edit);
-}
-
-/* Get the entry number from the variable NAME.  */
-static int
-get_entry_number (const char *name)
-{
-  char *val;
-  int entry;
-
-  val = grub_env_get (name);
-  if (! val)
-    return -1;
-
-  grub_error_push ();
-
-  entry = (int) grub_strtoul (val, 0, 0);
-
-  if (grub_errno != GRUB_ERR_NONE)
-    {
-      grub_errno = GRUB_ERR_NONE;
-      entry = -1;
-    }
-
-  grub_error_pop ();
-
-  return entry;
-}
-
-static void
-print_timeout (int timeout, int offset, int second_stage)
-{
-  /* NOTE: Do not remove the trailing space characters.
-     They are required to clear the line.  */
-  char *msg = "   The highlighted entry will be booted automatically in %ds.    ";
-  char *msg_end = grub_strchr (msg, '%');
-
-  grub_gotoxy (second_stage ? (msg_end - msg) : 0, GRUB_TERM_HEIGHT - 3);
-  grub_printf (second_stage ? msg_end : msg, timeout);
-  grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
-  grub_refresh ();
-};
-
-/* Show the menu and handle menu entry selection.  Returns the menu entry
-   index that should be executed or -1 if no entry should be executed (e.g.,
-   Esc pressed to exit a sub-menu or switching menu viewers).
-   If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
-   entry to be executed is a result of an automatic default selection because
-   of the timeout.  */
-static int
-run_menu (grub_menu_t menu, int nested, int *auto_boot)
-{
-  int first, offset;
-  grub_uint64_t saved_time;
-  int default_entry;
-  int timeout;
-
-  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;
-
-  /* If timeout is 0, drawing is pointless (and ugly).  */
-  if (grub_menu_get_timeout () == 0)
-    {
-      *auto_boot = 1;
-      return default_entry;
-    }
-
-  offset = default_entry;
-  if (offset > GRUB_TERM_NUM_ENTRIES - 1)
-    {
-      first = offset - (GRUB_TERM_NUM_ENTRIES - 1);
-      offset = GRUB_TERM_NUM_ENTRIES - 1;
-    }
-
-  /* Initialize the time.  */
-  saved_time = grub_get_time_ms ();
-
- refresh:
-  grub_setcursor (0);
-  grub_menu_init_page (nested, 0);
-  print_entries (menu, first, offset);
-  grub_refresh ();
-
-  timeout = grub_menu_get_timeout ();
-
-  if (timeout > 0)
-    print_timeout (timeout, offset, 0);
-
-  while (1)
-    {
-      int c;
-      timeout = grub_menu_get_timeout ();
-
-      if (timeout > 0)
-	{
-	  grub_uint64_t current_time;
-
-	  current_time = grub_get_time_ms ();
-	  if (current_time - saved_time >= 1000)
-	    {
-	      timeout--;
-	      grub_menu_set_timeout (timeout);
-	      saved_time = current_time;
-	      print_timeout (timeout, offset, 1);
-	    }
-	}
-
-      if (timeout == 0)
-	{
-	  grub_env_unset ("timeout");
-          *auto_boot = 1;
-	  return default_entry;
-	}
-
-      if (grub_checkkey () >= 0 || timeout < 0)
-	{
-	  c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
-
-	  if (timeout >= 0)
-	    {
-	      grub_gotoxy (0, GRUB_TERM_HEIGHT - 3);
-              grub_printf ("\
-                                                                        ");
-	      grub_env_unset ("timeout");
-	      grub_env_unset ("fallback");
-	      grub_gotoxy (GRUB_TERM_CURSOR_X, GRUB_TERM_FIRST_ENTRY_Y + offset);
-	    }
-
-	  switch (c)
-	    {
-	    case GRUB_TERM_HOME:
-	      first = 0;
-	      offset = 0;
-	      print_entries (menu, first, offset);
-	      break;
-
-	    case GRUB_TERM_END:
-	      offset = menu->size - 1;
-	      if (offset > GRUB_TERM_NUM_ENTRIES - 1)
-		{
-		  first = offset - (GRUB_TERM_NUM_ENTRIES - 1);
-		  offset = GRUB_TERM_NUM_ENTRIES - 1;
-		}
-		print_entries (menu, first, offset);
-	      break;
-
-	    case GRUB_TERM_UP:
-	    case '^':
-	      if (offset > 0)
-		{
-		  print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
-			       grub_menu_get_entry (menu, first + offset));
-		  offset--;
-		  print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
-			       grub_menu_get_entry (menu, first + offset));
-		}
-	      else if (first > 0)
-		{
-		  first--;
-		  print_entries (menu, first, offset);
-		}
-	      break;
-
-	    case GRUB_TERM_DOWN:
-	    case 'v':
-	      if (menu->size > first + offset + 1)
-		{
-		  if (offset < GRUB_TERM_NUM_ENTRIES - 1)
-		    {
-		      print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 0,
-				   grub_menu_get_entry (menu, first + offset));
-		      offset++;
-		      print_entry (GRUB_TERM_FIRST_ENTRY_Y + offset, 1,
-				   grub_menu_get_entry (menu, first + offset));
-		    }
-		  else
-		    {
-		      first++;
-		      print_entries (menu, first, offset);
-		    }
-		}
-	      break;
-
-	    case GRUB_TERM_PPAGE:
-	      if (first == 0)
-		{
-		  offset = 0;
-		}
-	      else
-		{
-		  first -= GRUB_TERM_NUM_ENTRIES;
-
-		  if (first < 0)
-		    {
-		      offset += first;
-		      first = 0;
-		    }
-		}
-	      print_entries (menu, first, offset);
-	      break;
-
-	    case GRUB_TERM_NPAGE:
-	      if (offset == 0)
-		{
-		  offset += GRUB_TERM_NUM_ENTRIES - 1;
-		  if (first + offset >= menu->size)
-		    {
-		      offset = menu->size - first - 1;
-		    }
-		}
-	      else
-		{
-		  first += GRUB_TERM_NUM_ENTRIES;
-
-		  if (first + offset >= menu->size)
-		    {
-		      first -= GRUB_TERM_NUM_ENTRIES;
-		      offset += GRUB_TERM_NUM_ENTRIES;
-
-		      if (offset > menu->size - 1 ||
-			  offset > GRUB_TERM_NUM_ENTRIES - 1)
-			{
-			  offset = menu->size - first - 1;
-			}
-		      if (offset > GRUB_TERM_NUM_ENTRIES)
-			{
-			  first += offset - GRUB_TERM_NUM_ENTRIES + 1;
-			  offset = GRUB_TERM_NUM_ENTRIES - 1;
-			}
-		    }
-		}
-	      print_entries (menu, first, offset);
-	      break;
-
-	    case '\n':
-	    case '\r':
-	    case 6:
-	      grub_setcursor (1);
-              *auto_boot = 0;
-	      return first + offset;
-
-	    case '\e':
-	      if (nested)
-		{
-		  grub_setcursor (1);
-		  return -1;
-		}
-	      break;
-
-	    case 'c':
-	      grub_cmdline_run (1);
-	      goto refresh;
-
-	    case 'e':
-		{
-		  grub_menu_entry_t e = grub_menu_get_entry (menu, first + offset);
-		  if (e)
-		    grub_menu_entry_run (e);
-		}
-	      goto refresh;
-
-	    default:
-	      break;
-	    }
-
-	  grub_refresh ();
-	}
-    }
-
-  /* Never reach here.  */
-  return -1;
-}
-
-/* Callback invoked immediately before a menu entry is executed.  */
-static void
-notify_booting (grub_menu_entry_t entry,
-		void *userdata __attribute__((unused)))
-{
-  grub_printf ("  Booting \'%s\'\n\n", entry->title);
-}
-
-/* Callback invoked when a default menu entry executed because of a timeout
-   has failed and an attempt will be made to execute the next fallback
-   entry, ENTRY.  */
-static void
-notify_fallback (grub_menu_entry_t entry,
-		 void *userdata __attribute__((unused)))
-{
-  grub_printf ("\n  Falling back to \'%s\'\n\n", entry->title);
-  grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
-}
-
-/* Callback invoked when a menu entry has failed and there is no remaining
-   fallback entry to attempt.  */
-static void
-notify_execution_failure (void *userdata __attribute__((unused)))
-{
-  if (grub_errno != GRUB_ERR_NONE)
-    {
-      grub_print_error ();
-      grub_errno = GRUB_ERR_NONE;
-    }
-  grub_printf ("\n  Failed to boot default entries.\n");
-  grub_wait_after_message ();
-}
-
-/* Callbacks used by the text menu to provide user feedback when menu entries
-   are executed.  */
-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_text_menu (grub_menu_t menu, int nested)
-{
-  while (1)
-    {
-      int boot_entry;
-      grub_menu_entry_t e;
-      int auto_boot;
-
-      boot_entry = run_menu (menu, nested, &auto_boot);
-      if (boot_entry < 0)
-	break;
-
-      e = grub_menu_get_entry (menu, boot_entry);
-      if (! e)
-	continue; /* Menu is empty.  */
-
-      grub_cls ();
-      grub_setcursor (1);
-
-      if (auto_boot)
-        {
-          grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
-        }
-      else
-        {
-          grub_errno = GRUB_ERR_NONE;
-          grub_menu_execute_entry (e);
-          if (grub_errno != GRUB_ERR_NONE)
-            {
-              grub_print_error ();
-              grub_errno = GRUB_ERR_NONE;
-              grub_wait_after_message ();
-            }
-        }
-    }
-
-  return GRUB_ERR_NONE;
-}
-
-struct grub_menu_viewer grub_normal_text_menu_viewer =
-{
-  .name = "text",
-  .show_menu = show_text_menu
-};
diff --git a/normal/menu_viewer.c b/normal/menu_viewer.c
index f7047c7..9d0d414 100644
--- a/normal/menu_viewer.c
+++ b/normal/menu_viewer.c
@@ -16,48 +16,24 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <grub/mm.h>
-#include <grub/misc.h>
-#include <grub/env.h>
+#include <grub/err.h>
 #include <grub/menu_viewer.h>
-#include <grub/menu.h>
 
-/* The list of menu viewers.  */
-static grub_menu_viewer_t menu_viewer_list;
-
-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;
-}
+struct grub_handler_class grub_menu_viewer_class =
+  {
+    .name = "menu_viewer"
+  };
 
 grub_err_t
 grub_menu_viewer_show_menu (grub_menu_t menu, int nested)
 {
-  grub_menu_viewer_t cur = get_current_menu_viewer ();
+  grub_menu_viewer_t cur = grub_menu_viewer_get_current ();
   if (!cur) 
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available.");
 
-  return cur->show_menu (menu, nested);
-}
+  grub_err_t err;
+
+  err = cur->show_menu (menu, nested);
 
+  return err;
+}
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to