iPXE
Data Structures | Defines | Functions
menu_ui.c File Reference

Menu interface. More...

#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/jumpscroll.h>
#include <ipxe/menu.h>

Go to the source code of this file.

Data Structures

struct  menu_ui
 A menu user interface. More...

Defines

#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

Functions

 FILE_LICENCE (GPL2_OR_LATER_OR_UBDL)
static struct menu_itemmenu_item (struct menu *menu, unsigned int index)
 Return a numbered menu item.
static void draw_menu_item (struct menu_ui *ui, unsigned int index)
 Draw a numbered menu item.
static void draw_menu_items (struct menu_ui *ui)
 Draw the current block of menu items.
static int menu_loop (struct menu_ui *ui, struct menu_item **selected)
 Menu main loop.
int show_menu (struct menu *menu, unsigned long timeout, const char *select, struct menu_item **selected)
 Show menu.

Detailed Description

Menu interface.

Definition in file menu_ui.c.


Define Documentation

#define TITLE_ROW   1U

Definition at line 43 of file menu_ui.c.

Referenced by show_menu().

#define MENU_ROW   3U

Definition at line 44 of file menu_ui.c.

Referenced by draw_menu_item(), and draw_menu_items().

#define MENU_COL   1U

Definition at line 45 of file menu_ui.c.

Referenced by draw_menu_item(), and draw_menu_items().

#define MENU_ROWS   ( LINES - 2U - MENU_ROW )

Definition at line 46 of file menu_ui.c.

Referenced by draw_menu_items(), and show_menu().

#define MENU_COLS   ( COLS - 2U )

Definition at line 47 of file menu_ui.c.

Referenced by draw_menu_item(), and show_menu().

#define MENU_PAD   2U

Definition at line 48 of file menu_ui.c.

Referenced by draw_menu_item(), and draw_menu_items().


Function Documentation

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL  )
static struct menu_item* menu_item ( struct menu menu,
unsigned int  index 
) [static, read]

Return a numbered menu item.

Parameters:
menuMenu
indexIndex
Return values:
itemMenu item, or NULL

Definition at line 67 of file menu_ui.c.

References menu::items, menu_item::list, list_for_each_entry, and NULL.

Referenced by draw_menu_item(), and menu_loop().

                                                                              {
        struct menu_item *item;

        list_for_each_entry ( item, &menu->items, list ) {
                if ( index-- == 0 )
                        return item;
        }

        return NULL;
}
static void draw_menu_item ( struct menu_ui ui,
unsigned int  index 
) [static]

Draw a numbered menu item.

Parameters:
uiMenu user interface
indexIndex

Definition at line 84 of file menu_ui.c.

References A_BOLD, attroff(), attron(), clrtoeol(), color_set, CPAIR_NORMAL, CPAIR_SELECT, CPAIR_SEPARATOR, jump_scroller::current, jump_scroller::first, menu_item::label, len, max_len, memcpy(), memset(), menu_ui::menu, MENU_COL, MENU_COLS, menu_item(), MENU_PAD, MENU_ROW, move(), NULL, printw(), menu_ui::scroll, snprintf(), strlen(), menu_item::text, TICKS_PER_SEC, and menu_ui::timeout.

Referenced by draw_menu_items(), menu_loop(), and show_menu().

                                                                      {
        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->scroll.first );
        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->scroll.current ) {
                        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->scroll.current ) && ( 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 );
}
static void draw_menu_items ( struct menu_ui ui) [static]

Draw the current block of menu items.

Parameters:
uiMenu user interface

Definition at line 151 of file menu_ui.c.

References color_set, CPAIR_NORMAL, CPAIR_SEPARATOR, draw_menu_item(), jump_scroller::first, jump_scroll_is_first(), jump_scroll_is_last(), MENU_COL, MENU_PAD, MENU_ROW, MENU_ROWS, mvaddstr(), NULL, and menu_ui::scroll.

Referenced by menu_loop(), and show_menu().

                                                   {
        unsigned int i;

        /* Draw ellipses before and/or after the list as necessary */
        color_set ( CPAIR_SEPARATOR, NULL );
        mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
                   ( jump_scroll_is_first ( &ui->scroll ) ? "   " : "..." ) );
        mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ),
                   ( jump_scroll_is_last ( &ui->scroll ) ? "   " : "..." ) );
        color_set ( CPAIR_NORMAL, NULL );

        /* Draw visible items */
        for ( i = 0 ; i < MENU_ROWS ; i++ )
                draw_menu_item ( ui, ( ui->scroll.first + i ) );
}
static int menu_loop ( struct menu_ui ui,
struct menu_item **  selected 
) [static]

Menu main loop.

Parameters:
uiMenu user interface
Return values:
selectedSelected item
rcReturn status code

Definition at line 174 of file menu_ui.c.

References assert, CR, CTRL_C, jump_scroller::current, draw_menu_item(), draw_menu_items(), ECANCELED, ESC, getkey(), menu::items, jump_scroll(), jump_scroll_key(), jump_scroll_move(), key, menu_item::label, LF, menu_item::list, list_for_each_entry, menu_ui::menu, menu_item(), move(), NULL, rc, menu_ui::scroll, menu_item::shortcut, TICKS_PER_SEC, menu_ui::timeout, and timeout().

Referenced by show_menu().

                                                                         {
        struct menu_item *item;
        unsigned long timeout;
        unsigned int previous;
        int key;
        int i;
        int move;
        int chosen = 0;
        int rc = 0;

        do {
                /* Record current selection */
                previous = ui->scroll.current;

                /* 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 scroll keys */
                        move = jump_scroll_key ( &ui->scroll, key );

                        /* Handle other keys */
                        switch ( key ) {
                        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->scroll.current = i;
                                        if ( item->label ) {
                                                chosen = 1;
                                        } else {
                                                move = +1;
                                        }
                                }
                                break;
                        }
                }

                /* Move selection, if applicable */
                while ( move ) {
                        move = jump_scroll_move ( &ui->scroll, move );
                        item = menu_item ( ui->menu, ui->scroll.current );
                        if ( item->label )
                                break;
                }

                /* Redraw selection if necessary */
                if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) {
                        draw_menu_item ( ui, previous );
                        if ( jump_scroll ( &ui->scroll ) )
                                draw_menu_items ( ui );
                        draw_menu_item ( ui, ui->scroll.current );
                }

                /* Record selection */
                item = menu_item ( ui->menu, ui->scroll.current );
                assert ( item != NULL );
                assert ( item->label != NULL );
                *selected = item;

        } while ( ( rc == 0 ) && ! chosen );

        return rc;
}
int show_menu ( struct menu menu,
unsigned long  timeout,
const char *  select,
struct menu_item **  selected 
)

Show menu.

Parameters:
menuMenu
timeoutTimeout period, in ticks (0=indefinite)
Return values:
selectedSelected item
rcReturn status code

Definition at line 273 of file menu_ui.c.

References A_BOLD, assert, attroff(), attron(), color_set, COLS, jump_scroller::count, CPAIR_NORMAL, jump_scroller::current, curs_set(), draw_menu_item(), draw_menu_items(), endwin(), ENOENT, erase(), initscr(), menu_item::is_default, menu::items, jump_scroll(), menu_item::label, list_for_each_entry, memset(), menu_ui::menu, MENU_COLS, menu_loop(), MENU_ROWS, mvprintw, NULL, rc, jump_scroller::rows, menu_ui::scroll, snprintf(), start_color, strcmp(), strlen(), menu_ui::timeout, timeout(), menu::title, and TITLE_ROW.

Referenced by choose_exec().

                                                                  {
        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.scroll.rows = MENU_ROWS;
        ui.timeout = timeout;
        list_for_each_entry ( item, &menu->items, list ) {
                if ( item->label ) {
                        if ( ! labelled_count )
                                ui.scroll.current = ui.scroll.count;
                        labelled_count++;
                        if ( select ) {
                                if ( strcmp ( select, item->label ) == 0 )
                                        ui.scroll.current = ui.scroll.count;
                        } else {
                                if ( item->is_default )
                                        ui.scroll.current = ui.scroll.count;
                        }
                }
                ui.scroll.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 );
        jump_scroll ( &ui.scroll );
        draw_menu_items ( &ui );
        draw_menu_item ( &ui, ui.scroll.current );

        /* Enter main loop */
        rc = menu_loop ( &ui, selected );
        assert ( *selected );

        /* Clear screen */
        endwin();

        return rc;
}