|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.