[pxe] Display the "Press F8" prompt rather than displaying menu with timeout
authorMichael Brown <mcb30@etherboot.org>
Thu, 5 Feb 2009 19:29:16 +0000 (19:29 +0000)
committerMichael Brown <mcb30@etherboot.org>
Thu, 5 Feb 2009 19:29:16 +0000 (19:29 +0000)
The PXE spec dictates the rather ugly feature that we have to present
a DHCP-specified prompt string to the user, then wait to see if they
press F8 before displaying the menu.

This seems to me to be a significant retrograde step from the current
situation of displaying the menu with the timeout counting down
against the default selected boot option, but apparently the lack of
the "Press F8" prompt causes some confusion.

src/usr/pxemenu.c

index 4cc40a6..911d4e9 100644 (file)
@@ -26,9 +26,9 @@
 #include <curses.h>
 #include <console.h>
 #include <gpxe/dhcp.h>
-#include <gpxe/vsprintf.h>
 #include <gpxe/keys.h>
 #include <gpxe/timer.h>
+#include <gpxe/process.h>
 #include <usr/dhcpmgmt.h>
 #include <usr/autoboot.h>
 
@@ -57,6 +57,8 @@ struct pxe_menu_item {
  * options.
  */
 struct pxe_menu {
+       /** Prompt string (optional) */
+       const char *prompt;
        /** Timeout (in seconds)
         *
         * Negative indicates no timeout (i.e. wait indefinitely)
@@ -80,22 +82,24 @@ struct pxe_menu {
  * boot menu.
  */
 static int pxe_menu_parse ( struct pxe_menu **menu ) {
-       struct setting tmp_setting = { .name = NULL };
-       struct dhcp_pxe_boot_menu_prompt prompt = { .timeout = 0 };
+       struct setting pxe_boot_menu_prompt_setting =
+               { .tag = DHCP_PXE_BOOT_MENU_PROMPT };
+       struct setting pxe_boot_menu_setting =
+               { .tag = DHCP_PXE_BOOT_MENU };
        uint8_t raw_menu[256];
+       int raw_prompt_len;
        int raw_menu_len;
        struct dhcp_pxe_boot_menu *raw_menu_item;
+       struct dhcp_pxe_boot_menu_prompt *raw_menu_prompt;
        void *raw_menu_end;
        unsigned int num_menu_items;
        unsigned int i;
        int rc;
 
-       /* Fetch relevant settings */
-       tmp_setting.tag = DHCP_PXE_BOOT_MENU_PROMPT;
-       fetch_setting ( NULL, &tmp_setting, &prompt, sizeof ( prompt ) );
-       tmp_setting.tag = DHCP_PXE_BOOT_MENU;
+       /* Fetch raw menu */
        memset ( raw_menu, 0, sizeof ( raw_menu ) );
-       if ( ( raw_menu_len = fetch_setting ( NULL, &tmp_setting, raw_menu,
+       if ( ( raw_menu_len = fetch_setting ( NULL, &pxe_boot_menu_setting,
+                                             raw_menu,
                                              sizeof ( raw_menu ) ) ) < 0 ) {
                rc = raw_menu_len;
                DBG ( "Could not retrieve raw PXE boot menu: %s\n",
@@ -108,6 +112,12 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
        }
        raw_menu_end = ( raw_menu + raw_menu_len );
 
+       /* Fetch raw prompt length */
+       raw_prompt_len = fetch_setting_len ( NULL,
+                                            &pxe_boot_menu_prompt_setting );
+       if ( raw_prompt_len < 0 )
+               raw_prompt_len = 0;
+
        /* Count menu items */
        num_menu_items = 0;
        raw_menu_item = ( ( void * ) raw_menu );
@@ -128,15 +138,14 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
        /* Allocate space for parsed menu */
        *menu = zalloc ( sizeof ( **menu ) +
                         ( num_menu_items * sizeof ( (*menu)->items[0] ) ) +
-                        raw_menu_len + 1 /* NUL */ );
+                        raw_menu_len + 1 /* NUL */ +
+                        raw_prompt_len + 1 /* NUL */ );
        if ( ! *menu ) {
                DBG ( "Could not allocate PXE boot menu\n" );
                return -ENOMEM;
        }
 
        /* Fill in parsed menu */
-       (*menu)->timeout =
-               ( ( prompt.timeout == 0xff ) ? -1 : prompt.timeout );
        (*menu)->num_items = num_menu_items;
        raw_menu_item = ( ( ( void * ) (*menu) ) + sizeof ( **menu ) +
                          ( num_menu_items * sizeof ( (*menu)->items[0] ) ) );
@@ -153,6 +162,18 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
                                  sizeof ( *raw_menu_item ) +
                                  raw_menu_item->desc_len );
        }
+       if ( raw_prompt_len ) {
+               raw_menu_prompt = ( ( ( void * ) raw_menu_item ) +
+                                   1 /* NUL */ );
+               fetch_setting ( NULL, &pxe_boot_menu_prompt_setting,
+                               raw_menu_prompt, raw_prompt_len );
+               (*menu)->timeout =
+                       ( ( raw_menu_prompt->timeout == 0xff ) ?
+                         -1 : raw_menu_prompt->timeout );
+               (*menu)->prompt = raw_menu_prompt->prompt;
+       } else {
+               (*menu)->timeout = -1;
+       }
 
        return 0;
 }
@@ -162,29 +183,20 @@ static int pxe_menu_parse ( struct pxe_menu **menu ) {
  *
  * @v menu             PXE boot menu
  * @v index            Index of item to draw
+ * @v selected         Item is selected
  */
 static void pxe_menu_draw_item ( struct pxe_menu *menu,
-                                unsigned int index ) {
-       int selected = ( menu->selection == index );
+                                unsigned int index, int selected ) {
        char buf[COLS+1];
-       char *tmp = buf;
-       ssize_t remaining = sizeof ( buf );
        size_t len;
        unsigned int row;
 
        /* Prepare space-padded row content */
-       len = ssnprintf ( tmp, remaining, " %c. %s",
-                         ( 'A' + index ), menu->items[index].desc );
-       tmp += len;
-       remaining -= len;
-       if ( selected && ( menu->timeout > 0 ) ) {
-               len = ssnprintf ( tmp, remaining, " (%d)", menu->timeout );
-               tmp += len;
-               remaining -= len;
-       }
-       for ( ; remaining > 1 ; tmp++, remaining-- )
-               *tmp = ' ';
-       *tmp = '\0';
+       len = snprintf ( buf, sizeof ( buf ), " %c. %s",
+                        ( 'A' + index ), menu->items[index].desc );
+       while ( len < ( sizeof ( buf ) - 1 ) )
+               buf[len++] = ' ';
+       buf[ sizeof ( buf ) - 1 ] = '\0';
 
        /* Draw row */
        row = ( LINES - menu->num_items + index );
@@ -199,11 +211,7 @@ static void pxe_menu_draw_item ( struct pxe_menu *menu,
  * @v menu             PXE boot menu
  * @ret rc             Return status code
  */
-int pxe_menu_select ( struct pxe_menu *menu ) {
-       unsigned long start = currticks();
-       unsigned long now;
-       unsigned long elapsed;
-       unsigned int old_selection;
+static int pxe_menu_select ( struct pxe_menu *menu ) {
        int key;
        unsigned int key_selection;
        unsigned int i;
@@ -220,37 +228,24 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
        for ( i = 0 ; i < menu->num_items ; i++ )
                printf ( "\n" );
        for ( i = 0 ; i < menu->num_items ; i++ )
-               pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ) );
+               pxe_menu_draw_item ( menu, ( menu->num_items - i - 1 ), 0 );
 
        while ( 1 ) {
 
-               /* Decrease timeout if necessary */
-               if ( menu->timeout > 0 ) {
-                       now = currticks();
-                       elapsed = ( now - start );
-                       if ( elapsed >= TICKS_PER_SEC ) {
-                               start = now;
-                               menu->timeout--;
-                               pxe_menu_draw_item ( menu, menu->selection );
-                       }
-               }
-
-               /* Select current item if we have timed out */
-               if ( menu->timeout == 0 )
-                       break;
+               /* Highlight currently selected item */
+               pxe_menu_draw_item ( menu, menu->selection, 1 );
 
-               /* Check for keyboard input */
-               if ( ! iskey() )
-                       continue;
+               /* Wait for keyboard input */
+               while ( ! iskey() )
+                       step();
                key = getkey();
 
-               /* Any keyboard input cancels the timeout */
-               menu->timeout = -1;
-               pxe_menu_draw_item ( menu, menu->selection );
+               /* Unhighlight currently selected item */
+               pxe_menu_draw_item ( menu, menu->selection, 0 );
 
                /* Act upon key */
-               old_selection = menu->selection;
                if ( ( key == CR ) || ( key == LF ) ) {
+                       pxe_menu_draw_item ( menu, menu->selection, 1 );
                        break;
                } else if ( key == CTRL_C ) {
                        rc = -ECANCELED;
@@ -265,10 +260,9 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
                            ( ( key_selection = ( toupper ( key ) - 'A' ) )
                              < menu->num_items ) ) {
                        menu->selection = key_selection;
-                       menu->timeout = 0;
+                       pxe_menu_draw_item ( menu, menu->selection, 1 );
+                       break;
                }
-               pxe_menu_draw_item ( menu, old_selection );
-               pxe_menu_draw_item ( menu, menu->selection );
        }
 
        /* Shut down UI */
@@ -277,6 +271,66 @@ int pxe_menu_select ( struct pxe_menu *menu ) {
        return rc;
 }
 
+/**
+ * Prompt for (and make selection from) PXE boot menu
+ *
+ * @v menu             PXE boot menu
+ * @ret rc             Return status code
+ */
+static int pxe_menu_prompt_and_select ( struct pxe_menu *menu ) {
+       unsigned long start = currticks();
+       unsigned long now;
+       unsigned long elapsed;
+       size_t len = 0;
+       int key;
+       int rc = 0;
+
+       /* Display menu immediately, if specified to do so */
+       if ( menu->timeout < 0 ) {
+               if ( menu->prompt )
+                       printf ( "%s\n", menu->prompt );
+               return pxe_menu_select ( menu );
+       }
+
+       /* Display prompt, if specified */
+       if ( menu->prompt )
+               printf ( "%s", menu->prompt );
+
+       /* Wait for timeout, if specified */
+       while ( menu->timeout > 0 ) {
+               if ( ! len )
+                       len = printf ( " (%d)", menu->timeout );
+               if ( iskey() ) {
+                       key = getkey();
+                       if ( key == KEY_F8 ) {
+                               /* Display menu */
+                               printf ( "\n" );
+                               return pxe_menu_select ( menu );
+                       } else if ( key == CTRL_C ) {
+                               /* Abort */
+                               rc = -ECANCELED;
+                               break;
+                       } else {
+                               /* Stop waiting */
+                               break;
+                       }
+               }
+               now = currticks();
+               elapsed = ( now - start );
+               if ( elapsed >= TICKS_PER_SEC ) {
+                       menu->timeout -= 1;
+                       do {
+                               printf ( "\b \b" );
+                       } while ( --len );
+                       start = now;
+               }
+       }
+
+       /* Return with default option selected */
+       printf ( "\n" );
+       return rc;
+}
+
 /**
  * Boot using PXE boot menu
  *
@@ -299,7 +353,7 @@ int pxe_menu_boot ( struct net_device *netdev ) {
                return rc;
 
        /* Make selection from boot menu */
-       if ( ( rc = pxe_menu_select ( menu ) ) != 0 ) {
+       if ( ( rc = pxe_menu_prompt_and_select ( menu ) ) != 0 ) {
                free ( menu );
                return rc;
        }