summaryrefslogtreecommitdiffstats
path: root/qemu/roms/ipxe/src/hci/tui
diff options
context:
space:
mode:
authorYang Zhang <yang.z.zhang@intel.com>2015-08-28 09:58:54 +0800
committerYang Zhang <yang.z.zhang@intel.com>2015-09-01 12:44:00 +0800
commite44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch)
tree66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/hci/tui
parent9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff)
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5 Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/hci/tui')
-rw-r--r--qemu/roms/ipxe/src/hci/tui/login_ui.c133
-rw-r--r--qemu/roms/ipxe/src/hci/tui/menu_ui.c363
-rw-r--r--qemu/roms/ipxe/src/hci/tui/settings_ui.c603
3 files changed, 1099 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/hci/tui/login_ui.c b/qemu/roms/ipxe/src/hci/tui/login_ui.c
new file mode 100644
index 000000000..996b68a0a
--- /dev/null
+++ b/qemu/roms/ipxe/src/hci/tui/login_ui.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2009 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
+ *
+ * Login UI
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <curses.h>
+#include <ipxe/console.h>
+#include <ipxe/settings.h>
+#include <ipxe/editbox.h>
+#include <ipxe/keys.h>
+#include <ipxe/ansicol.h>
+#include <ipxe/login_ui.h>
+
+/* Screen layout */
+#define USERNAME_LABEL_ROW ( ( LINES / 2U ) - 4U )
+#define USERNAME_ROW ( ( LINES / 2U ) - 2U )
+#define PASSWORD_LABEL_ROW ( ( LINES / 2U ) + 2U )
+#define PASSWORD_ROW ( ( LINES / 2U ) + 4U )
+#define LABEL_COL ( ( COLS / 2U ) - 4U )
+#define EDITBOX_COL ( ( COLS / 2U ) - 10U )
+#define EDITBOX_WIDTH 20U
+
+int login_ui ( void ) {
+ char username[64];
+ char password[64];
+ struct edit_box username_box;
+ struct edit_box password_box;
+ struct edit_box *current_box = &username_box;
+ int key;
+ int rc = -EINPROGRESS;
+
+ /* Fetch current setting values */
+ fetch_string_setting ( NULL, &username_setting, username,
+ sizeof ( username ) );
+ fetch_string_setting ( NULL, &password_setting, password,
+ sizeof ( password ) );
+
+ /* Initialise UI */
+ initscr();
+ start_color();
+ init_editbox ( &username_box, username, sizeof ( username ), NULL,
+ USERNAME_ROW, EDITBOX_COL, EDITBOX_WIDTH, 0 );
+ init_editbox ( &password_box, password, sizeof ( password ), NULL,
+ PASSWORD_ROW, EDITBOX_COL, EDITBOX_WIDTH,
+ EDITBOX_STARS );
+
+ /* Draw initial UI */
+ color_set ( CPAIR_NORMAL, NULL );
+ erase();
+ attron ( A_BOLD );
+ mvprintw ( USERNAME_LABEL_ROW, LABEL_COL, "Username:" );
+ mvprintw ( PASSWORD_LABEL_ROW, LABEL_COL, "Password:" );
+ attroff ( A_BOLD );
+ color_set ( CPAIR_EDIT, NULL );
+ draw_editbox ( &username_box );
+ draw_editbox ( &password_box );
+
+ /* Main loop */
+ while ( rc == -EINPROGRESS ) {
+
+ draw_editbox ( current_box );
+
+ key = getkey ( 0 );
+ switch ( key ) {
+ case KEY_DOWN:
+ current_box = &password_box;
+ break;
+ case KEY_UP:
+ current_box = &username_box;
+ break;
+ case TAB:
+ current_box = ( ( current_box == &username_box ) ?
+ &password_box : &username_box );
+ break;
+ case KEY_ENTER:
+ if ( current_box == &username_box ) {
+ current_box = &password_box;
+ } else {
+ rc = 0;
+ }
+ break;
+ case CTRL_C:
+ case ESC:
+ rc = -ECANCELED;
+ break;
+ default:
+ edit_editbox ( current_box, key );
+ break;
+ }
+ }
+
+ /* Terminate UI */
+ color_set ( CPAIR_NORMAL, NULL );
+ erase();
+ endwin();
+
+ if ( rc != 0 )
+ return rc;
+
+ /* Store settings */
+ if ( ( rc = store_setting ( NULL, &username_setting, username,
+ strlen ( username ) ) ) != 0 )
+ return rc;
+ if ( ( rc = store_setting ( NULL, &password_setting, password,
+ strlen ( password ) ) ) != 0 )
+ return rc;
+
+ return 0;
+}
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
index 000000000..0a9566def
--- /dev/null
+++ b/qemu/roms/ipxe/src/hci/tui/menu_ui.c
@@ -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;
+}
diff --git a/qemu/roms/ipxe/src/hci/tui/settings_ui.c b/qemu/roms/ipxe/src/hci/tui/settings_ui.c
new file mode 100644
index 000000000..221839730
--- /dev/null
+++ b/qemu/roms/ipxe/src/hci/tui/settings_ui.c
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2006 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 );
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <curses.h>
+#include <ipxe/console.h>
+#include <ipxe/settings.h>
+#include <ipxe/editbox.h>
+#include <ipxe/keys.h>
+#include <ipxe/ansicol.h>
+#include <ipxe/settings_ui.h>
+
+/** @file
+ *
+ * Option configuration console
+ *
+ */
+
+/* Screen layout */
+#define TITLE_ROW 1U
+#define SETTINGS_LIST_ROW 3U
+#define SETTINGS_LIST_COL 1U
+#define SETTINGS_LIST_ROWS ( LINES - 6U - SETTINGS_LIST_ROW )
+#define INFO_ROW ( LINES - 5U )
+#define ALERT_ROW ( LINES - 2U )
+#define INSTRUCTION_ROW ( LINES - 2U )
+#define INSTRUCTION_PAD " "
+
+/** Layout of text within a setting widget */
+#define SETTING_ROW_TEXT( cols ) struct { \
+ char start[0]; \
+ char pad1[1]; \
+ union { \
+ char settings[ cols - 1 - 1 - 1 - 1 ]; \
+ struct { \
+ char name[15]; \
+ char pad2[1]; \
+ char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ]; \
+ } setting; \
+ } u; \
+ char pad3[1]; \
+ char nul; \
+} __attribute__ (( packed ))
+
+/** A setting row widget */
+struct setting_row_widget {
+ /** Target configuration settings block
+ *
+ * Valid only for rows that lead to new settings blocks.
+ */
+ struct settings *settings;
+ /** Configuration setting origin
+ *
+ * Valid only for rows that represent individual settings.
+ */
+ struct settings *origin;
+ /** Configuration setting
+ *
+ * Valid only for rows that represent individual settings.
+ */
+ struct setting setting;
+ /** Screen row */
+ unsigned int row;
+ /** Screen column */
+ unsigned int col;
+ /** Edit box widget used for editing setting */
+ struct edit_box editbox;
+ /** Editing in progress flag */
+ int editing;
+ /** Buffer for setting's value */
+ char value[256]; /* enough size for a DHCP string */
+};
+
+/** A settings widget */
+struct setting_widget {
+ /** Settings block */
+ struct settings *settings;
+ /** Number of rows */
+ unsigned int num_rows;
+ /** Current row index */
+ unsigned int current;
+ /** Index of the first visible row, for scrolling. */
+ unsigned int first_visible;
+ /** Active row */
+ struct setting_row_widget row;
+};
+
+/**
+ * Select a setting row
+ *
+ * @v widget Setting widget
+ * @v index Index of setting row
+ * @ret count Number of settings rows
+ */
+static unsigned int select_setting_row ( struct setting_widget *widget,
+ unsigned int index ) {
+ SETTING_ROW_TEXT ( COLS ) *text;
+ struct settings *settings;
+ struct setting *setting;
+ struct setting *previous = NULL;
+ unsigned int count = 0;
+
+ /* Initialise structure */
+ memset ( &widget->row, 0, sizeof ( widget->row ) );
+ widget->current = index;
+ widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
+ widget->row.col = SETTINGS_LIST_COL;
+
+ /* Include parent settings block, if applicable */
+ if ( widget->settings->parent && ( count++ == index ) ) {
+ widget->row.settings = widget->settings->parent;
+ snprintf ( widget->row.value, sizeof ( widget->row.value ),
+ "../" );
+ }
+
+ /* Include any child settings blocks, if applicable */
+ list_for_each_entry ( settings, &widget->settings->children, siblings ){
+ if ( count++ == index ) {
+ widget->row.settings = settings;
+ snprintf ( widget->row.value,
+ sizeof ( widget->row.value ), "%s/",
+ settings->name );
+ }
+ }
+
+ /* Include any applicable settings */
+ for_each_table_entry ( setting, SETTINGS ) {
+
+ /* Skip inapplicable settings */
+ if ( ! setting_applies ( widget->settings, setting ) )
+ continue;
+
+ /* Skip duplicate settings */
+ if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
+ continue;
+ previous = setting;
+
+ /* Read current setting value and origin */
+ if ( count++ == index ) {
+ fetchf_setting ( widget->settings, setting,
+ &widget->row.origin,
+ &widget->row.setting,
+ widget->row.value,
+ sizeof ( widget->row.value ) );
+ }
+ }
+
+ /* Initialise edit box */
+ init_editbox ( &widget->row.editbox, widget->row.value,
+ sizeof ( widget->row.value ), NULL, widget->row.row,
+ ( widget->row.col +
+ offsetof ( typeof ( *text ), u.setting.value ) ),
+ sizeof ( text->u.setting.value ), 0 );
+
+ return count;
+}
+
+/**
+ * Copy string without NUL termination
+ *
+ * @v dest Destination
+ * @v src Source
+ * @v len Maximum length of destination
+ * @ret len Length of (unterminated) string
+ */
+static size_t string_copy ( char *dest, const char *src, size_t len ) {
+ size_t src_len;
+
+ src_len = strlen ( src );
+ if ( len > src_len )
+ len = src_len;
+ memcpy ( dest, src, len );
+ return len;
+}
+
+/**
+ * Draw setting row
+ *
+ * @v widget Setting widget
+ */
+static void draw_setting_row ( struct setting_widget *widget ) {
+ SETTING_ROW_TEXT ( COLS ) text;
+ unsigned int curs_offset;
+ char *value;
+
+ /* Fill row with spaces */
+ memset ( &text, ' ', sizeof ( text ) );
+ text.nul = '\0';
+
+ /* Construct row content */
+ if ( widget->row.settings ) {
+
+ /* Construct space-padded name */
+ curs_offset = ( offsetof ( typeof ( text ), u.settings ) +
+ string_copy ( text.u.settings,
+ widget->row.value,
+ sizeof ( text.u.settings ) ) );
+
+ } else {
+
+ /* Construct dot-padded name */
+ memset ( text.u.setting.name, '.',
+ sizeof ( text.u.setting.name ) );
+ string_copy ( text.u.setting.name, widget->row.setting.name,
+ sizeof ( text.u.setting.name ) );
+
+ /* Construct space-padded value */
+ value = widget->row.value;
+ if ( ! *value )
+ value = "<not specified>";
+ curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) +
+ string_copy ( text.u.setting.value, value,
+ sizeof ( text.u.setting.value )));
+ }
+
+ /* Print row */
+ if ( ( widget->row.origin == widget->settings ) ||
+ ( widget->row.settings != NULL ) ) {
+ attron ( A_BOLD );
+ }
+ mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
+ attroff ( A_BOLD );
+ move ( widget->row.row, widget->row.col + curs_offset );
+}
+
+/**
+ * Edit setting widget
+ *
+ * @v widget Setting widget
+ * @v key Key pressed by user
+ * @ret key Key returned to application, or zero
+ */
+static int edit_setting ( struct setting_widget *widget, int key ) {
+ assert ( widget->row.setting.name != NULL );
+ widget->row.editing = 1;
+ return edit_editbox ( &widget->row.editbox, key );
+}
+
+/**
+ * Save setting widget value back to configuration settings
+ *
+ * @v widget Setting widget
+ */
+static int save_setting ( struct setting_widget *widget ) {
+ assert ( widget->row.setting.name != NULL );
+ return storef_setting ( widget->settings, &widget->row.setting,
+ widget->row.value );
+}
+
+/**
+ * Print message centred on specified row
+ *
+ * @v row Row
+ * @v fmt printf() format string
+ * @v args printf() argument list
+ */
+static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
+ char buf[COLS];
+ size_t len;
+
+ len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
+ mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
+}
+
+/**
+ * Print message centred on specified row
+ *
+ * @v row Row
+ * @v fmt printf() format string
+ * @v .. printf() arguments
+ */
+static void msg ( unsigned int row, const char *fmt, ... ) {
+ va_list args;
+
+ va_start ( args, fmt );
+ vmsg ( row, fmt, args );
+ va_end ( args );
+}
+
+/**
+ * Clear message on specified row
+ *
+ * @v row Row
+ */
+static void clearmsg ( unsigned int row ) {
+ move ( row, 0 );
+ clrtoeol();
+}
+
+/**
+ * Print alert message
+ *
+ * @v fmt printf() format string
+ * @v args printf() argument list
+ */
+static void valert ( const char *fmt, va_list args ) {
+ clearmsg ( ALERT_ROW );
+ color_set ( CPAIR_ALERT, NULL );
+ vmsg ( ALERT_ROW, fmt, args );
+ sleep ( 2 );
+ color_set ( CPAIR_NORMAL, NULL );
+ clearmsg ( ALERT_ROW );
+}
+
+/**
+ * Print alert message
+ *
+ * @v fmt printf() format string
+ * @v ... printf() arguments
+ */
+static void alert ( const char *fmt, ... ) {
+ va_list args;
+
+ va_start ( args, fmt );
+ valert ( fmt, args );
+ va_end ( args );
+}
+
+/**
+ * Draw title row
+ *
+ * @v widget Setting widget
+ */
+static void draw_title_row ( struct setting_widget *widget ) {
+ const char *name;
+
+ clearmsg ( TITLE_ROW );
+ name = settings_name ( widget->settings );
+ attron ( A_BOLD );
+ msg ( TITLE_ROW, "iPXE configuration settings%s%s",
+ ( name[0] ? " - " : "" ), name );
+ attroff ( A_BOLD );
+}
+
+/**
+ * Draw information row
+ *
+ * @v widget Setting widget
+ */
+static void draw_info_row ( struct setting_widget *widget ) {
+ char buf[32];
+
+ /* Draw nothing unless this row represents a setting */
+ clearmsg ( INFO_ROW );
+ clearmsg ( INFO_ROW + 1 );
+ if ( ! widget->row.setting.name )
+ return;
+
+ /* Determine a suitable setting name */
+ setting_name ( ( widget->row.origin ?
+ widget->row.origin : widget->settings ),
+ &widget->row.setting, buf, sizeof ( buf ) );
+
+ /* Draw row */
+ attron ( A_BOLD );
+ msg ( INFO_ROW, "%s - %s", buf, widget->row.setting.description );
+ attroff ( A_BOLD );
+ color_set ( CPAIR_URL, NULL );
+ msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
+ widget->row.setting.name );
+ color_set ( CPAIR_NORMAL, NULL );
+}
+
+/**
+ * Draw instruction row
+ *
+ * @v widget Setting widget
+ */
+static void draw_instruction_row ( struct setting_widget *widget ) {
+
+ clearmsg ( INSTRUCTION_ROW );
+ if ( widget->row.editing ) {
+ msg ( INSTRUCTION_ROW,
+ "Enter - accept changes" INSTRUCTION_PAD
+ "Ctrl-C - discard changes" );
+ } else {
+ msg ( INSTRUCTION_ROW,
+ "%sCtrl-X - exit configuration utility",
+ ( ( widget->row.origin == widget->settings ) ?
+ "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
+ }
+}
+
+/**
+ * Reveal setting row
+ *
+ * @v widget Setting widget
+ * @v index Index of setting row
+ */
+static void reveal_setting_row ( struct setting_widget *widget,
+ unsigned int index ) {
+ unsigned int i;
+
+ /* Simply return if setting N is already on-screen. */
+ if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
+ return;
+
+ /* Jump scroll to make the specified setting row visible. */
+ while ( widget->first_visible < index )
+ widget->first_visible += SETTINGS_LIST_ROWS;
+ while ( widget->first_visible > index )
+ widget->first_visible -= SETTINGS_LIST_ROWS;
+
+ /* Draw ellipses before and/or after the settings list to
+ * represent any invisible settings.
+ */
+ mvaddstr ( SETTINGS_LIST_ROW - 1,
+ SETTINGS_LIST_COL + 1,
+ widget->first_visible > 0 ? "..." : " " );
+ mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
+ SETTINGS_LIST_COL + 1,
+ ( ( widget->first_visible + SETTINGS_LIST_ROWS )
+ < widget->num_rows ? "..." : " " ) );
+
+ /* Draw visible settings. */
+ for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
+ if ( ( widget->first_visible + i ) < widget->num_rows ) {
+ select_setting_row ( widget,
+ widget->first_visible + i );
+ draw_setting_row ( widget );
+ } else {
+ clearmsg ( SETTINGS_LIST_ROW + i );
+ }
+ }
+}
+
+/**
+ * Reveal setting row
+ *
+ * @v widget Setting widget
+ * @v settings Settings block
+ */
+static void init_widget ( struct setting_widget *widget,
+ struct settings *settings ) {
+
+ widget->settings = settings_target ( settings );
+ widget->num_rows = select_setting_row ( widget, 0 );
+ widget->first_visible = SETTINGS_LIST_ROWS;
+ draw_title_row ( widget );
+ reveal_setting_row ( widget, 0 );
+ select_setting_row ( widget, 0 );
+}
+
+static int main_loop ( struct settings *settings ) {
+ struct setting_widget widget;
+ int redraw = 1;
+ int move;
+ unsigned int next;
+ int key;
+ int rc;
+
+ /* Print initial screen content */
+ color_set ( CPAIR_NORMAL, NULL );
+ memset ( &widget, 0, sizeof ( widget ) );
+ init_widget ( &widget, settings );
+
+ while ( 1 ) {
+
+ /* Redraw rows if necessary */
+ if ( redraw ) {
+ draw_info_row ( &widget );
+ draw_instruction_row ( &widget );
+ color_set ( ( widget.row.editing ?
+ CPAIR_EDIT : CPAIR_SELECT ), NULL );
+ draw_setting_row ( &widget );
+ color_set ( CPAIR_NORMAL, NULL );
+ curs_set ( widget.row.editing );
+ redraw = 0;
+ }
+
+ if ( widget.row.editing ) {
+
+ /* Sanity check */
+ assert ( widget.row.setting.name != NULL );
+
+ /* Redraw edit box */
+ color_set ( CPAIR_EDIT, NULL );
+ draw_editbox ( &widget.row.editbox );
+ color_set ( CPAIR_NORMAL, NULL );
+
+ /* Process keypress */
+ key = edit_setting ( &widget, getkey ( 0 ) );
+ switch ( key ) {
+ case CR:
+ case LF:
+ if ( ( rc = save_setting ( &widget ) ) != 0 )
+ alert ( " %s ", strerror ( rc ) );
+ /* Fall through */
+ case CTRL_C:
+ select_setting_row ( &widget, widget.current );
+ redraw = 1;
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ } else {
+
+ /* Process keypress */
+ key = getkey ( 0 );
+ move = 0;
+ switch ( key ) {
+ case KEY_UP:
+ move = -1;
+ break;
+ case KEY_DOWN:
+ move = +1;
+ break;
+ case KEY_PPAGE:
+ move = ( widget.first_visible -
+ widget.current - 1 );
+ break;
+ case KEY_NPAGE:
+ move = ( widget.first_visible - widget.current
+ + SETTINGS_LIST_ROWS );
+ break;
+ case KEY_HOME:
+ move = -widget.num_rows;
+ break;
+ case KEY_END:
+ move = +widget.num_rows;
+ break;
+ case CTRL_D:
+ if ( ! widget.row.setting.name )
+ break;
+ if ( ( rc = delete_setting ( widget.settings,
+ &widget.row.setting ) ) != 0 ) {
+ alert ( " %s ", strerror ( rc ) );
+ }
+ select_setting_row ( &widget, widget.current );
+ redraw = 1;
+ break;
+ case CTRL_X:
+ return 0;
+ case CR:
+ case LF:
+ if ( widget.row.settings ) {
+ init_widget ( &widget,
+ widget.row.settings );
+ redraw = 1;
+ }
+ /* Fall through */
+ default:
+ if ( widget.row.setting.name ) {
+ edit_setting ( &widget, key );
+ redraw = 1;
+ }
+ break;
+ }
+ if ( move ) {
+ next = ( widget.current + move );
+ if ( ( int ) next < 0 )
+ next = 0;
+ if ( next >= widget.num_rows )
+ next = ( widget.num_rows - 1 );
+ if ( next != widget.current ) {
+ draw_setting_row ( &widget );
+ redraw = 1;
+ reveal_setting_row ( &widget, next );
+ select_setting_row ( &widget, next );
+ }
+ }
+ }
+ }
+}
+
+int settings_ui ( struct settings *settings ) {
+ int rc;
+
+ initscr();
+ start_color();
+ color_set ( CPAIR_NORMAL, NULL );
+ curs_set ( 0 );
+ erase();
+
+ rc = main_loop ( settings );
+
+ endwin();
+
+ return rc;
+}