Annotation of qemu/roms/ipxe/src/hci/tui/settings_ui.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Copyright (C) 2006 Michael Brown <[email protected]>.
                      3:  *
                      4:  * This program is free software; you can redistribute it and/or
                      5:  * modify it under the terms of the GNU General Public License as
                      6:  * published by the Free Software Foundation; either version 2 of the
                      7:  * License, or any later version.
                      8:  *
                      9:  * This program is distributed in the hope that it will be useful, but
                     10:  * WITHOUT ANY WARRANTY; without even the implied warranty of
                     11:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
                     12:  * General Public License for more details.
                     13:  *
                     14:  * You should have received a copy of the GNU General Public License
                     15:  * along with this program; if not, write to the Free Software
                     16:  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
                     17:  */
                     18: 
                     19: FILE_LICENCE ( GPL2_OR_LATER );
                     20: 
                     21: #include <stdio.h>
                     22: #include <stdarg.h>
                     23: #include <unistd.h>
                     24: #include <string.h>
                     25: #include <curses.h>
                     26: #include <ipxe/console.h>
                     27: #include <ipxe/settings.h>
                     28: #include <ipxe/editbox.h>
                     29: #include <ipxe/keys.h>
                     30: #include <ipxe/settings_ui.h>
                     31: 
                     32: /** @file
                     33:  *
                     34:  * Option configuration console
                     35:  *
                     36:  */
                     37: 
                     38: /* Colour pairs */
                     39: #define CPAIR_NORMAL   1
                     40: #define CPAIR_SELECT   2
                     41: #define CPAIR_EDIT     3
                     42: #define CPAIR_ALERT    4
                     43: #define CPAIR_URL      5
                     44: 
                     45: /* Screen layout */
                     46: #define TITLE_ROW              1
                     47: #define SETTINGS_LIST_ROW      3
                     48: #define SETTINGS_LIST_COL      1
                     49: #define SETTINGS_LIST_ROWS     15
                     50: #define INFO_ROW               19
                     51: #define ALERT_ROW              22
                     52: #define INSTRUCTION_ROW                22
                     53: #define INSTRUCTION_PAD "     "
                     54: 
                     55: /** Layout of text within a setting widget */
                     56: struct setting_row_text {
                     57:        char start[0];
                     58:        char pad1[1];
                     59:        char name[15];
                     60:        char pad2[1];
                     61:        char value[60];
                     62:        char pad3[1];
                     63:        char nul;
                     64: } __attribute__ (( packed ));
                     65: 
                     66: /** A setting row widget */
                     67: struct setting_row_widget {
                     68:        /** Target configuration settings block
                     69:         *
                     70:         * Valid only for rows that lead to new settings blocks.
                     71:         */
                     72:        struct settings *settings;
                     73:        /** Configuration setting
                     74:         *
                     75:         * Valid only for rows that represent individual settings.
                     76:         */
                     77:        struct setting *setting;
                     78:        /** Screen row */
                     79:        unsigned int row;
                     80:        /** Screen column */
                     81:        unsigned int col;
                     82:        /** Edit box widget used for editing setting */
                     83:        struct edit_box editbox;
                     84:        /** Editing in progress flag */
                     85:        int editing;
                     86:        /** Setting originates from this block flag */
                     87:        int originates_here;
                     88:        /** Buffer for setting's value */
                     89:        char value[256]; /* enough size for a DHCP string */
                     90: };
                     91: 
                     92: /** A settings widget */
                     93: struct setting_widget {
                     94:        /** Settings block */
                     95:        struct settings *settings;
                     96:        /** Number of rows */
                     97:        unsigned int num_rows;
                     98:        /** Current row index */
                     99:        unsigned int current;
                    100:         /** Index of the first visible row, for scrolling. */
                    101:        unsigned int first_visible;
                    102:        /** Active row */
                    103:        struct setting_row_widget row;
                    104: };
                    105: 
                    106: /**
                    107:  * Select a setting row
                    108:  *
                    109:  * @v widget           Setting widget
                    110:  * @v index            Index of setting row
                    111:  * @ret count          Number of settings rows
                    112:  */
                    113: static unsigned int select_setting_row ( struct setting_widget *widget,
                    114:                                         unsigned int index ) {
                    115:        struct settings *settings;
                    116:        struct settings *origin;
                    117:        struct setting *setting;
                    118:        unsigned int count = 0;
                    119: 
                    120:        /* Initialise structure */
                    121:        memset ( &widget->row, 0, sizeof ( widget->row ) );
                    122:        widget->current = index;
                    123:        widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
                    124:        widget->row.col = SETTINGS_LIST_COL;
                    125: 
                    126:        /* Include parent settings block, if applicable */
                    127:        if ( widget->settings->parent && ( count++ == index ) ) {
                    128:                widget->row.settings = widget->settings->parent;
                    129:                snprintf ( widget->row.value, sizeof ( widget->row.value ),
                    130:                           "../" );
                    131:        }
                    132: 
                    133:        /* Include any child settings blocks, if applicable */
                    134:        list_for_each_entry ( settings, &widget->settings->children, siblings ){
                    135:                if ( count++ == index ) {
                    136:                        widget->row.settings = settings;
                    137:                        snprintf ( widget->row.value,
                    138:                                   sizeof ( widget->row.value ), "%s/",
                    139:                                   settings->name );
                    140:                }
                    141:        }
                    142: 
                    143:        /* Include any applicable settings */
                    144:        for_each_table_entry ( setting, SETTINGS ) {
                    145:                if ( ! setting_applies ( widget->settings, setting ) )
                    146:                        continue;
                    147:                if ( count++ == index ) {
                    148:                        widget->row.setting = setting;
                    149: 
                    150:                        /* Read current setting value */
                    151:                        fetchf_setting ( widget->settings, widget->row.setting,
                    152:                                         widget->row.value,
                    153:                                         sizeof ( widget->row.value ) );
                    154: 
                    155:                        /* Check setting's origin */
                    156:                        origin = fetch_setting_origin ( widget->settings,
                    157:                                                        widget->row.setting );
                    158:                        widget->row.originates_here =
                    159:                                ( origin == widget->settings );
                    160:                }
                    161:        }
                    162: 
                    163:        /* Initialise edit box */
                    164:        init_editbox ( &widget->row.editbox, widget->row.value,
                    165:                       sizeof ( widget->row.value ), NULL, widget->row.row,
                    166:                       ( widget->row.col +
                    167:                         offsetof ( struct setting_row_text, value ) ),
                    168:                       sizeof ( ( ( struct setting_row_text * ) NULL )->value ),
                    169:                       0 );
                    170: 
                    171:        return count;
                    172: }
                    173: 
                    174: static size_t string_copy ( char *dest, const char *src, size_t len ) {
                    175:        size_t src_len;
                    176: 
                    177:        src_len = strlen ( src );
                    178:        if ( len > src_len )
                    179:                len = src_len;
                    180:        memcpy ( dest, src, len );
                    181:        return len;
                    182: }
                    183: 
                    184: /**
                    185:  * Draw setting row
                    186:  *
                    187:  * @v widget           Setting widget
                    188:  */
                    189: static void draw_setting_row ( struct setting_widget *widget ) {
                    190:        struct setting_row_text text;
                    191:        unsigned int curs_offset;
                    192:        char *value;
                    193: 
                    194:        /* Fill row with spaces */
                    195:        memset ( &text, ' ', sizeof ( text ) );
                    196:        text.nul = '\0';
                    197: 
                    198:        /* Construct row content */
                    199:        if ( widget->row.settings ) {
                    200: 
                    201:                /* Construct space-padded name */
                    202:                curs_offset = ( offsetof ( typeof ( text ), name ) +
                    203:                                string_copy ( text.name, widget->row.value,
                    204:                                              sizeof ( text.name ) ) );
                    205: 
                    206:        } else {
                    207: 
                    208:                /* Construct dot-padded name */
                    209:                memset ( text.name, '.', sizeof ( text.name ) );
                    210:                string_copy ( text.name, widget->row.setting->name,
                    211:                              sizeof ( text.name ) );
                    212: 
                    213:                /* Construct space-padded value */
                    214:                value = widget->row.value;
                    215:                if ( ! *value )
                    216:                        value = "<not specified>";
                    217:                curs_offset = ( offsetof ( typeof ( text ), value ) +
                    218:                                string_copy ( text.value, value,
                    219:                                              sizeof ( text.value ) ) );
                    220:        }
                    221: 
                    222:        /* Print row */
                    223:        if ( widget->row.originates_here || widget->row.settings )
                    224:                attron ( A_BOLD );
                    225:        mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
                    226:        attroff ( A_BOLD );
                    227:        move ( widget->row.row, widget->row.col + curs_offset );
                    228: }
                    229: 
                    230: /**
                    231:  * Edit setting widget
                    232:  *
                    233:  * @v widget           Setting widget
                    234:  * @v key              Key pressed by user
                    235:  * @ret key            Key returned to application, or zero
                    236:  */
                    237: static int edit_setting ( struct setting_widget *widget, int key ) {
                    238:        assert ( widget->row.setting != NULL );
                    239:        widget->row.editing = 1;
                    240:        return edit_editbox ( &widget->row.editbox, key );
                    241: }
                    242: 
                    243: /**
                    244:  * Save setting widget value back to configuration settings
                    245:  *
                    246:  * @v widget           Setting widget
                    247:  */
                    248: static int save_setting ( struct setting_widget *widget ) {
                    249:        assert ( widget->row.setting != NULL );
                    250:        return storef_setting ( widget->settings, widget->row.setting,
                    251:                                widget->row.value );
                    252: }
                    253: 
                    254: /**
                    255:  * Print message centred on specified row
                    256:  *
                    257:  * @v row              Row
                    258:  * @v fmt              printf() format string
                    259:  * @v args             printf() argument list
                    260:  */
                    261: static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
                    262:        char buf[COLS];
                    263:        size_t len;
                    264: 
                    265:        len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
                    266:        mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
                    267: }
                    268: 
                    269: /**
                    270:  * Print message centred on specified row
                    271:  *
                    272:  * @v row              Row
                    273:  * @v fmt              printf() format string
                    274:  * @v ..               printf() arguments
                    275:  */
                    276: static void msg ( unsigned int row, const char *fmt, ... ) {
                    277:        va_list args;
                    278: 
                    279:        va_start ( args, fmt );
                    280:        vmsg ( row, fmt, args );
                    281:        va_end ( args );
                    282: }
                    283: 
                    284: /**
                    285:  * Clear message on specified row
                    286:  *
                    287:  * @v row              Row
                    288:  */
                    289: static void clearmsg ( unsigned int row ) {
                    290:        move ( row, 0 );
                    291:        clrtoeol();
                    292: }
                    293: 
                    294: /**
                    295:  * Print alert message
                    296:  *
                    297:  * @v fmt              printf() format string
                    298:  * @v args             printf() argument list
                    299:  */
                    300: static void valert ( const char *fmt, va_list args ) {
                    301:        clearmsg ( ALERT_ROW );
                    302:        color_set ( CPAIR_ALERT, NULL );
                    303:        vmsg ( ALERT_ROW, fmt, args );
                    304:        sleep ( 2 );
                    305:        color_set ( CPAIR_NORMAL, NULL );
                    306:        clearmsg ( ALERT_ROW );
                    307: }
                    308: 
                    309: /**
                    310:  * Print alert message
                    311:  *
                    312:  * @v fmt              printf() format string
                    313:  * @v ...              printf() arguments
                    314:  */
                    315: static void alert ( const char *fmt, ... ) {
                    316:        va_list args;
                    317: 
                    318:        va_start ( args, fmt );
                    319:        valert ( fmt, args );
                    320:        va_end ( args );
                    321: }
                    322: 
                    323: /**
                    324:  * Draw title row
                    325:  *
                    326:  * @v widget           Setting widget
                    327:  */
                    328: static void draw_title_row ( struct setting_widget *widget ) {
                    329:        const char *name;
                    330: 
                    331:        clearmsg ( TITLE_ROW );
                    332:        name = settings_name ( widget->settings );
                    333:        attron ( A_BOLD );
                    334:        msg ( TITLE_ROW, "iPXE configuration settings%s%s",
                    335:              ( name[0] ? " - " : "" ), name );
                    336:        attroff ( A_BOLD );
                    337: }
                    338: 
                    339: /**
                    340:  * Draw information row
                    341:  *
                    342:  * @v widget           Setting widget
                    343:  */
                    344: static void draw_info_row ( struct setting_widget *widget ) {
                    345:        struct settings *origin;
                    346:        char buf[32];
                    347: 
                    348:        /* Draw nothing unless this row represents a setting */
                    349:        clearmsg ( INFO_ROW );
                    350:        clearmsg ( INFO_ROW + 1 );
                    351:        if ( ! widget->row.setting )
                    352:                return;
                    353: 
                    354:        /* Determine a suitable setting name */
                    355:        origin = fetch_setting_origin ( widget->settings, widget->row.setting );
                    356:        if ( ! origin )
                    357:                origin = widget->settings;
                    358:        setting_name ( origin, widget->row.setting, buf, sizeof ( buf ) );
                    359: 
                    360:        /* Draw row */
                    361:        attron ( A_BOLD );
                    362:        msg ( INFO_ROW, "%s - %s", buf, widget->row.setting->description );
                    363:        attroff ( A_BOLD );
                    364:        color_set ( CPAIR_URL, NULL );
                    365:        msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
                    366:              widget->row.setting->name );
                    367:        color_set ( CPAIR_NORMAL, NULL );
                    368: }
                    369: 
                    370: /**
                    371:  * Draw instruction row
                    372:  *
                    373:  * @v widget           Setting widget
                    374:  */
                    375: static void draw_instruction_row ( struct setting_widget *widget ) {
                    376: 
                    377:        clearmsg ( INSTRUCTION_ROW );
                    378:        if ( widget->row.editing ) {
                    379:                msg ( INSTRUCTION_ROW,
                    380:                      "Enter - accept changes" INSTRUCTION_PAD
                    381:                      "Ctrl-C - discard changes" );
                    382:        } else {
                    383:                msg ( INSTRUCTION_ROW,
                    384:                      "%sCtrl-X - exit configuration utility",
                    385:                      ( widget->row.originates_here ?
                    386:                        "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
                    387:        }
                    388: }
                    389: 
                    390: /**
                    391:  * Reveal setting row
                    392:  *
                    393:  * @v widget           Setting widget
                    394:  * @v index            Index of setting row
                    395:  */
                    396: static void reveal_setting_row ( struct setting_widget *widget,
                    397:                                 unsigned int index ) {
                    398:        unsigned int i;
                    399: 
                    400:        /* Simply return if setting N is already on-screen. */
                    401:        if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
                    402:                return;
                    403: 
                    404:        /* Jump scroll to make the specified setting row visible. */
                    405:        while ( widget->first_visible < index )
                    406:                widget->first_visible += SETTINGS_LIST_ROWS;
                    407:        while ( widget->first_visible > index )
                    408:                widget->first_visible -= SETTINGS_LIST_ROWS;
                    409: 
                    410:        /* Draw ellipses before and/or after the settings list to
                    411:         * represent any invisible settings.
                    412:         */
                    413:        mvaddstr ( SETTINGS_LIST_ROW - 1,
                    414:                   SETTINGS_LIST_COL + 1,
                    415:                   widget->first_visible > 0 ? "..." : "   " );
                    416:        mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
                    417:                   SETTINGS_LIST_COL + 1,
                    418:                   ( ( widget->first_visible + SETTINGS_LIST_ROWS )
                    419:                     < widget->num_rows ? "..." : "   " ) );
                    420: 
                    421:        /* Draw visible settings. */
                    422:        for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
                    423:                if ( ( widget->first_visible + i ) < widget->num_rows ) {
                    424:                        select_setting_row ( widget,
                    425:                                             widget->first_visible + i );
                    426:                        draw_setting_row ( widget );
                    427:                } else {
                    428:                        clearmsg ( SETTINGS_LIST_ROW + i );
                    429:                }
                    430:        }
                    431: }
                    432: 
                    433: /**
                    434:  * Reveal setting row
                    435:  *
                    436:  * @v widget           Setting widget
                    437:  * @v settings         Settings block
                    438:  */
                    439: static void init_widget ( struct setting_widget *widget,
                    440:                          struct settings *settings ) {
                    441: 
                    442:        widget->settings = settings;
                    443:        widget->num_rows = select_setting_row ( widget, 0 );
                    444:        widget->first_visible = SETTINGS_LIST_ROWS;
                    445:        draw_title_row ( widget );
                    446:        reveal_setting_row ( widget, 0 );
                    447:        select_setting_row ( widget, 0 );
                    448: }
                    449: 
                    450: static int main_loop ( struct settings *settings ) {
                    451:        struct setting_widget widget;
                    452:        int redraw = 1;
                    453:        int move;
                    454:        unsigned int next;
                    455:        int key;
                    456:        int rc;
                    457: 
                    458:        /* Print initial screen content */
                    459:        color_set ( CPAIR_NORMAL, NULL );
                    460:        memset ( &widget, 0, sizeof ( widget ) );
                    461:        init_widget ( &widget, settings );
                    462: 
                    463:        while ( 1 ) {
                    464: 
                    465:                /* Redraw rows if necessary */
                    466:                if ( redraw ) {
                    467:                        draw_info_row ( &widget );
                    468:                        draw_instruction_row ( &widget );
                    469:                        color_set ( ( widget.row.editing ?
                    470:                                      CPAIR_EDIT : CPAIR_SELECT ), NULL );
                    471:                        draw_setting_row ( &widget );
                    472:                        color_set ( CPAIR_NORMAL, NULL );
                    473:                        redraw = 0;
                    474:                }
                    475: 
                    476:                if ( widget.row.editing ) {
                    477: 
                    478:                        /* Sanity check */
                    479:                        assert ( widget.row.setting != NULL );
                    480: 
                    481:                        /* Redraw edit box */
                    482:                        color_set ( CPAIR_EDIT, NULL );
                    483:                        draw_editbox ( &widget.row.editbox );
                    484:                        color_set ( CPAIR_NORMAL, NULL );
                    485: 
                    486:                        /* Process keypress */
                    487:                        key = edit_setting ( &widget, getkey ( 0 ) );
                    488:                        switch ( key ) {
                    489:                        case CR:
                    490:                        case LF:
                    491:                                if ( ( rc = save_setting ( &widget ) ) != 0 )
                    492:                                        alert ( " %s ", strerror ( rc ) );
                    493:                                /* Fall through */
                    494:                        case CTRL_C:
                    495:                                select_setting_row ( &widget, widget.current );
                    496:                                redraw = 1;
                    497:                                break;
                    498:                        default:
                    499:                                /* Do nothing */
                    500:                                break;
                    501:                        }
                    502: 
                    503:                } else {
                    504: 
                    505:                        /* Process keypress */
                    506:                        key = getkey ( 0 );
                    507:                        move = 0;
                    508:                        switch ( key ) {
                    509:                        case KEY_DOWN:
                    510:                                if ( widget.current < ( widget.num_rows - 1 ) )
                    511:                                        move = +1;
                    512:                                break;
                    513:                        case KEY_UP:
                    514:                                if ( widget.current > 0 )
                    515:                                        move = -1;
                    516:                                break;
                    517:                        case CTRL_D:
                    518:                                if ( ! widget.row.setting )
                    519:                                        break;
                    520:                                if ( ( rc = delete_setting ( widget.settings,
                    521:                                                widget.row.setting ) ) != 0 ) {
                    522:                                        alert ( " %s ", strerror ( rc ) );
                    523:                                }
                    524:                                select_setting_row ( &widget, widget.current );
                    525:                                redraw = 1;
                    526:                                break;
                    527:                        case CTRL_X:
                    528:                                return 0;
                    529:                        case CR:
                    530:                        case LF:
                    531:                                if ( widget.row.settings ) {
                    532:                                        init_widget ( &widget,
                    533:                                                      widget.row.settings );
                    534:                                        redraw = 1;
                    535:                                }
                    536:                                /* Fall through */
                    537:                        default:
                    538:                                if ( widget.row.setting ) {
                    539:                                        edit_setting ( &widget, key );
                    540:                                        redraw = 1;
                    541:                                }
                    542:                                break;
                    543:                        }
                    544:                        if ( move ) {
                    545:                                next = ( widget.current + move );
                    546:                                draw_setting_row ( &widget );
                    547:                                redraw = 1;
                    548:                                reveal_setting_row ( &widget, next );
                    549:                                select_setting_row ( &widget, next );
                    550:                        }
                    551:                }
                    552:        }
                    553: }
                    554: 
                    555: int settings_ui ( struct settings *settings ) {
                    556:        int rc;
                    557: 
                    558:        initscr();
                    559:        start_color();
                    560:        init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
                    561:        init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
                    562:        init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
                    563:        init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
                    564:        init_pair ( CPAIR_URL, COLOR_CYAN, COLOR_BLUE );
                    565:        color_set ( CPAIR_NORMAL, NULL );
                    566:        erase();
                    567:        
                    568:        rc = main_loop ( settings );
                    569: 
                    570:        endwin();
                    571: 
                    572:        return rc;
                    573: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.