Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / hci / tui / menu_ui.c
diff --git a/qemu/roms/ipxe/src/hci/tui/menu_ui.c b/qemu/roms/ipxe/src/hci/tui/menu_ui.c
new file mode 100644 (file)
index 0000000..0a9566d
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program 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 2 of the
+ * License, or any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/** @file
+ *
+ * Menu interface
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <curses.h>
+#include <ipxe/keys.h>
+#include <ipxe/timer.h>
+#include <ipxe/console.h>
+#include <ipxe/ansicol.h>
+#include <ipxe/menu.h>
+
+/* Screen layout */
+#define TITLE_ROW      1U
+#define MENU_ROW       3U
+#define MENU_COL       1U
+#define MENU_ROWS      ( LINES - 2U - MENU_ROW )
+#define MENU_COLS      ( COLS - 2U )
+#define MENU_PAD       2U
+
+/** A menu user interface */
+struct menu_ui {
+       /** Menu */
+       struct menu *menu;
+       /** Number of menu items */
+       unsigned int count;
+       /** Currently selected item */
+       int selected;
+       /** First visible item */
+       int first_visible;
+       /** Timeout (0=indefinite) */
+       unsigned long timeout;
+};
+
+/**
+ * Return a numbered menu item
+ *
+ * @v menu             Menu
+ * @v index            Index
+ * @ret item           Menu item, or NULL
+ */
+static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) {
+       struct menu_item *item;
+
+       list_for_each_entry ( item, &menu->items, list ) {
+               if ( index-- == 0 )
+                       return item;
+       }
+
+       return NULL;
+}
+
+/**
+ * Draw a numbered menu item
+ *
+ * @v ui               Menu user interface
+ * @v index            Index
+ */
+static void draw_menu_item ( struct menu_ui *ui, int index ) {
+       struct menu_item *item;
+       unsigned int row_offset;
+       char buf[ MENU_COLS + 1 /* NUL */ ];
+       char timeout_buf[6]; /* "(xxx)" + NUL */
+       size_t timeout_len;
+       size_t max_len;
+       size_t len;
+
+       /* Move to start of row */
+       row_offset = ( index - ui->first_visible );
+       move ( ( MENU_ROW + row_offset ), MENU_COL );
+
+       /* Get menu item */
+       item = menu_item ( ui->menu, index );
+       if ( item ) {
+
+               /* Draw separators in a different colour */
+               if ( ! item->label )
+                       color_set ( CPAIR_SEPARATOR, NULL );
+
+               /* Highlight if this is the selected item */
+               if ( index == ui->selected ) {
+                       color_set ( CPAIR_SELECT, NULL );
+                       attron ( A_BOLD );
+               }
+
+               /* Construct row */
+               memset ( buf, ' ', ( sizeof ( buf ) - 1 ) );
+               buf[ sizeof ( buf ) -1 ] = '\0';
+               len = strlen ( item->text );
+               max_len = ( sizeof ( buf ) - 1 /* NUL */ - ( 2 * MENU_PAD ) );
+               if ( len > max_len )
+                       len = max_len;
+               memcpy ( ( buf + MENU_PAD ), item->text, len );
+
+               /* Add timeout if applicable */
+               timeout_len =
+                       snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
+                                  ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
+                                    TICKS_PER_SEC ) );
+               if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) {
+                       memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
+                                timeout_buf, timeout_len );
+               }
+
+               /* Print row */
+               printw ( "%s", buf );
+
+               /* Reset attributes */
+               color_set ( CPAIR_NORMAL, NULL );
+               attroff ( A_BOLD );
+
+       } else {
+               /* Clear row if there is no corresponding menu item */
+               clrtoeol();
+       }
+
+       /* Move cursor back to start of row */
+       move ( ( MENU_ROW + row_offset ), MENU_COL );
+}
+
+/**
+ * Draw the current block of menu items
+ *
+ * @v ui               Menu user interface
+ */
+static void draw_menu_items ( struct menu_ui *ui ) {
+       unsigned int i;
+
+       /* Jump scroll to correct point in list */
+       while ( ui->first_visible < ui->selected )
+               ui->first_visible += MENU_ROWS;
+       while ( ui->first_visible > ui->selected )
+               ui->first_visible -= MENU_ROWS;
+
+       /* Draw ellipses before and/or after the list as necessary */
+       color_set ( CPAIR_SEPARATOR, NULL );
+       mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
+                  ( ( ui->first_visible > 0 ) ? "..." : "   " ) );
+       mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ),
+                  ( ( ( ui->first_visible + MENU_ROWS ) < ui->count ) ?
+                    "..." : "   " ) );
+       color_set ( CPAIR_NORMAL, NULL );
+
+       /* Draw visible items */
+       for ( i = 0 ; i < MENU_ROWS ; i++ )
+               draw_menu_item ( ui, ( ui->first_visible + i ) );
+}
+
+/**
+ * Menu main loop
+ *
+ * @v ui               Menu user interface
+ * @ret selected       Selected item
+ * @ret rc             Return status code
+ */
+static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
+       struct menu_item *item;
+       unsigned long timeout;
+       unsigned int delta;
+       int current;
+       int key;
+       int i;
+       int move;
+       int chosen = 0;
+       int rc = 0;
+
+       do {
+               /* Record current selection */
+               current = ui->selected;
+
+               /* Calculate timeout as remainder of current second */
+               timeout = ( ui->timeout % TICKS_PER_SEC );
+               if ( ( timeout == 0 ) && ( ui->timeout != 0 ) )
+                       timeout = TICKS_PER_SEC;
+               ui->timeout -= timeout;
+
+               /* Get key */
+               move = 0;
+               key = getkey ( timeout );
+               if ( key < 0 ) {
+                       /* Choose default if we finally time out */
+                       if ( ui->timeout == 0 )
+                               chosen = 1;
+               } else {
+                       /* Cancel any timeout */
+                       ui->timeout = 0;
+
+                       /* Handle key */
+                       switch ( key ) {
+                       case KEY_UP:
+                               move = -1;
+                               break;
+                       case KEY_DOWN:
+                               move = +1;
+                               break;
+                       case KEY_PPAGE:
+                               move = ( ui->first_visible - ui->selected - 1 );
+                               break;
+                       case KEY_NPAGE:
+                               move = ( ui->first_visible - ui->selected
+                                        + MENU_ROWS );
+                               break;
+                       case KEY_HOME:
+                               move = -ui->count;
+                               break;
+                       case KEY_END:
+                               move = +ui->count;
+                               break;
+                       case ESC:
+                       case CTRL_C:
+                               rc = -ECANCELED;
+                               break;
+                       case CR:
+                       case LF:
+                               chosen = 1;
+                               break;
+                       default:
+                               i = 0;
+                               list_for_each_entry ( item, &ui->menu->items,
+                                                     list ) {
+                                       if ( ! ( item->shortcut &&
+                                                ( item->shortcut == key ) ) ) {
+                                               i++;
+                                               continue;
+                                       }
+                                       ui->selected = i;
+                                       if ( item->label ) {
+                                               chosen = 1;
+                                       } else {
+                                               move = +1;
+                                       }
+                               }
+                               break;
+                       }
+               }
+
+               /* Move selection, if applicable */
+               while ( move ) {
+                       ui->selected += move;
+                       if ( ui->selected < 0 ) {
+                               ui->selected = 0;
+                               move = +1;
+                       } else if ( ui->selected >= ( int ) ui->count ) {
+                               ui->selected = ( ui->count - 1 );
+                               move = -1;
+                       }
+                       item = menu_item ( ui->menu, ui->selected );
+                       if ( item->label )
+                               break;
+                       move = ( ( move > 0 ) ? +1 : -1 );
+               }
+
+               /* Redraw selection if necessary */
+               if ( ( ui->selected != current ) || ( timeout != 0 ) ) {
+                       draw_menu_item ( ui, current );
+                       delta = ( ui->selected - ui->first_visible );
+                       if ( delta >= MENU_ROWS )
+                               draw_menu_items ( ui );
+                       draw_menu_item ( ui, ui->selected );
+               }
+
+               /* Record selection */
+               item = menu_item ( ui->menu, ui->selected );
+               assert ( item != NULL );
+               assert ( item->label != NULL );
+               *selected = item;
+
+       } while ( ( rc == 0 ) && ! chosen );
+
+       return rc;
+}
+
+/**
+ * Show menu
+ *
+ * @v menu             Menu
+ * @v timeout          Timeout period, in ticks (0=indefinite)
+ * @ret selected       Selected item
+ * @ret rc             Return status code
+ */
+int show_menu ( struct menu *menu, unsigned long timeout,
+               const char *select, struct menu_item **selected ) {
+       struct menu_item *item;
+       struct menu_ui ui;
+       char buf[ MENU_COLS + 1 /* NUL */ ];
+       int labelled_count = 0;
+       int rc;
+
+       /* Initialise UI */
+       memset ( &ui, 0, sizeof ( ui ) );
+       ui.menu = menu;
+       ui.timeout = timeout;
+       list_for_each_entry ( item, &menu->items, list ) {
+               if ( item->label ) {
+                       if ( ! labelled_count )
+                               ui.selected = ui.count;
+                       labelled_count++;
+                       if ( select ) {
+                               if ( strcmp ( select, item->label ) == 0 )
+                                       ui.selected = ui.count;
+                       } else {
+                               if ( item->is_default )
+                                       ui.selected = ui.count;
+                       }
+               }
+               ui.count++;
+       }
+       if ( ! labelled_count ) {
+               /* Menus with no labelled items cannot be selected
+                * from, and will seriously confuse the navigation
+                * logic.  Refuse to display any such menus.
+                */
+               return -ENOENT;
+       }
+
+       /* Initialise screen */
+       initscr();
+       start_color();
+       color_set ( CPAIR_NORMAL, NULL );
+       curs_set ( 0 );
+       erase();
+
+       /* Draw initial content */
+       attron ( A_BOLD );
+       snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title );
+       mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
+       attroff ( A_BOLD );
+       draw_menu_items ( &ui );
+       draw_menu_item ( &ui, ui.selected );
+
+       /* Enter main loop */
+       rc = menu_loop ( &ui, selected );
+       assert ( *selected );
+
+       /* Clear screen */
+       endwin();
+
+       return rc;
+}