Home | History | Annotate | Download | only in tui
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      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 <console.h>
     27 #include <gpxe/settings.h>
     28 #include <gpxe/editbox.h>
     29 #include <gpxe/keys.h>
     30 #include <gpxe/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 
     44 /* Screen layout */
     45 #define TITLE_ROW		1
     46 #define SETTINGS_LIST_ROW	3
     47 #define SETTINGS_LIST_COL	1
     48 #define INFO_ROW		20
     49 #define ALERT_ROW		20
     50 #define INSTRUCTION_ROW		22
     51 #define INSTRUCTION_PAD "     "
     52 
     53 /** Layout of text within a setting widget */
     54 struct setting_row {
     55 	char start[0];
     56 	char pad1[1];
     57 	char name[15];
     58 	char pad2[1];
     59 	char value[60];
     60 	char pad3[1];
     61 	char nul;
     62 } __attribute__ (( packed ));
     63 
     64 /** A setting widget */
     65 struct setting_widget {
     66 	/** Settings block */
     67 	struct settings *settings;
     68 	/** Configuration setting */
     69 	struct setting *setting;
     70 	/** Screen row */
     71 	unsigned int row;
     72 	/** Screen column */
     73 	unsigned int col;
     74 	/** Edit box widget used for editing setting */
     75 	struct edit_box editbox;
     76 	/** Editing in progress flag */
     77 	int editing;
     78 	/** Buffer for setting's value */
     79 	char value[256]; /* enough size for a DHCP string */
     80 };
     81 
     82 /** Number of registered configuration settings */
     83 #define NUM_SETTINGS table_num_entries ( SETTINGS )
     84 
     85 static void load_setting ( struct setting_widget *widget ) __nonnull;
     86 static int save_setting ( struct setting_widget *widget ) __nonnull;
     87 static void init_setting ( struct setting_widget *widget,
     88                            struct settings *settings,
     89                            struct setting *setting,
     90                            unsigned int row, unsigned int col ) __nonnull;
     91 static void draw_setting ( struct setting_widget *widget ) __nonnull;
     92 static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
     93 static void init_setting_index ( struct setting_widget *widget,
     94                                  struct settings *settings,
     95                                  unsigned int index ) __nonnull;
     96 static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
     97 static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
     98 static void valert ( const char *fmt, va_list args ) __nonnull;
     99 static void alert ( const char *fmt, ... ) __nonnull;
    100 static void draw_info_row ( struct setting *setting ) __nonnull;
    101 static int main_loop ( struct settings *settings ) __nonnull;
    102 
    103 /**
    104  * Load setting widget value from configuration settings
    105  *
    106  * @v widget		Setting widget
    107  *
    108  */
    109 static void load_setting ( struct setting_widget *widget ) {
    110 
    111 	/* Mark as not editing */
    112 	widget->editing = 0;
    113 
    114 	/* Read current setting value */
    115 	if ( fetchf_setting ( widget->settings, widget->setting,
    116 			      widget->value, sizeof ( widget->value ) ) < 0 ) {
    117 		widget->value[0] = '\0';
    118 	}
    119 
    120 	/* Initialise edit box */
    121 	init_editbox ( &widget->editbox, widget->value,
    122 		       sizeof ( widget->value ), NULL, widget->row,
    123 		       ( widget->col + offsetof ( struct setting_row, value )),
    124 		       sizeof ( ( ( struct setting_row * ) NULL )->value ), 0);
    125 }
    126 
    127 /**
    128  * Save setting widget value back to configuration settings
    129  *
    130  * @v widget		Setting widget
    131  */
    132 static int save_setting ( struct setting_widget *widget ) {
    133 	return storef_setting ( widget->settings, widget->setting,
    134 				widget->value );
    135 }
    136 
    137 /**
    138  * Initialise setting widget
    139  *
    140  * @v widget		Setting widget
    141  * @v settings		Settings block
    142  * @v setting		Configuration setting
    143  * @v row		Screen row
    144  * @v col		Screen column
    145  */
    146 static void init_setting ( struct setting_widget *widget,
    147 			   struct settings *settings,
    148 			   struct setting *setting,
    149 			   unsigned int row, unsigned int col ) {
    150 
    151 	/* Initialise widget structure */
    152 	memset ( widget, 0, sizeof ( *widget ) );
    153 	widget->settings = settings;
    154 	widget->setting = setting;
    155 	widget->row = row;
    156 	widget->col = col;
    157 
    158 	/* Read current setting value */
    159 	load_setting ( widget );
    160 }
    161 
    162 /**
    163  * Draw setting widget
    164  *
    165  * @v widget		Setting widget
    166  */
    167 static void draw_setting ( struct setting_widget *widget ) {
    168 	struct setting_row row;
    169 	unsigned int len;
    170 	unsigned int curs_col;
    171 	char *value;
    172 
    173 	/* Fill row with spaces */
    174 	memset ( &row, ' ', sizeof ( row ) );
    175 	row.nul = '\0';
    176 
    177 	/* Construct dot-padded name */
    178 	memset ( row.name, '.', sizeof ( row.name ) );
    179 	len = strlen ( widget->setting->name );
    180 	if ( len > sizeof ( row.name ) )
    181 		len = sizeof ( row.name );
    182 	memcpy ( row.name, widget->setting->name, len );
    183 
    184 	/* Construct space-padded value */
    185 	value = widget->value;
    186 	if ( ! *value )
    187 		value = "<not specified>";
    188 	len = strlen ( value );
    189 	if ( len > sizeof ( row.value ) )
    190 		len = sizeof ( row.value );
    191 	memcpy ( row.value, value, len );
    192 	curs_col = ( widget->col + offsetof ( typeof ( row ), value )
    193 		     + len );
    194 
    195 	/* Print row */
    196 	mvprintw ( widget->row, widget->col, "%s", row.start );
    197 	move ( widget->row, curs_col );
    198 	if ( widget->editing )
    199 		draw_editbox ( &widget->editbox );
    200 }
    201 
    202 /**
    203  * Edit setting widget
    204  *
    205  * @v widget		Setting widget
    206  * @v key		Key pressed by user
    207  * @ret key		Key returned to application, or zero
    208  */
    209 static int edit_setting ( struct setting_widget *widget, int key ) {
    210 	widget->editing = 1;
    211 	return edit_editbox ( &widget->editbox, key );
    212 }
    213 
    214 /**
    215  * Initialise setting widget by index
    216  *
    217  * @v widget		Setting widget
    218  * @v settings		Settings block
    219  * @v index		Index of setting with settings list
    220  */
    221 static void init_setting_index ( struct setting_widget *widget,
    222 				 struct settings *settings,
    223 				 unsigned int index ) {
    224 	struct setting *all_settings = table_start ( SETTINGS );
    225 
    226 	init_setting ( widget, settings, &all_settings[index],
    227 		       ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
    228 }
    229 
    230 /**
    231  * Print message centred on specified row
    232  *
    233  * @v row		Row
    234  * @v fmt		printf() format string
    235  * @v args		printf() argument list
    236  */
    237 static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
    238 	char buf[COLS];
    239 	size_t len;
    240 
    241 	len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
    242 	mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
    243 }
    244 
    245 /**
    246  * Print message centred on specified row
    247  *
    248  * @v row		Row
    249  * @v fmt		printf() format string
    250  * @v ..		printf() arguments
    251  */
    252 static void msg ( unsigned int row, const char *fmt, ... ) {
    253 	va_list args;
    254 
    255 	va_start ( args, fmt );
    256 	vmsg ( row, fmt, args );
    257 	va_end ( args );
    258 }
    259 
    260 /**
    261  * Clear message on specified row
    262  *
    263  * @v row		Row
    264  */
    265 static void clearmsg ( unsigned int row ) {
    266 	move ( row, 0 );
    267 	clrtoeol();
    268 }
    269 
    270 /**
    271  * Print alert message
    272  *
    273  * @v fmt		printf() format string
    274  * @v args		printf() argument list
    275  */
    276 static void valert ( const char *fmt, va_list args ) {
    277 	clearmsg ( ALERT_ROW );
    278 	color_set ( CPAIR_ALERT, NULL );
    279 	vmsg ( ALERT_ROW, fmt, args );
    280 	sleep ( 2 );
    281 	color_set ( CPAIR_NORMAL, NULL );
    282 	clearmsg ( ALERT_ROW );
    283 }
    284 
    285 /**
    286  * Print alert message
    287  *
    288  * @v fmt		printf() format string
    289  * @v ...		printf() arguments
    290  */
    291 static void alert ( const char *fmt, ... ) {
    292 	va_list args;
    293 
    294 	va_start ( args, fmt );
    295 	valert ( fmt, args );
    296 	va_end ( args );
    297 }
    298 
    299 /**
    300  * Draw title row
    301  */
    302 static void draw_title_row ( void ) {
    303 	attron ( A_BOLD );
    304 	msg ( TITLE_ROW, "gPXE option configuration console" );
    305 	attroff ( A_BOLD );
    306 }
    307 
    308 /**
    309  * Draw information row
    310  *
    311  * @v setting		Current configuration setting
    312  */
    313 static void draw_info_row ( struct setting *setting ) {
    314 	clearmsg ( INFO_ROW );
    315 	attron ( A_BOLD );
    316 	msg ( INFO_ROW, "%s - %s", setting->name, setting->description );
    317 	attroff ( A_BOLD );
    318 }
    319 
    320 /**
    321  * Draw instruction row
    322  *
    323  * @v editing		Editing in progress flag
    324  */
    325 static void draw_instruction_row ( int editing ) {
    326 	clearmsg ( INSTRUCTION_ROW );
    327 	if ( editing ) {
    328 		msg ( INSTRUCTION_ROW,
    329 		      "Enter - accept changes" INSTRUCTION_PAD
    330 		      "Ctrl-C - discard changes" );
    331 	} else {
    332 		msg ( INSTRUCTION_ROW,
    333 		      "Ctrl-X - exit configuration utility" );
    334 	}
    335 }
    336 
    337 static int main_loop ( struct settings *settings ) {
    338 	struct setting_widget widget;
    339 	unsigned int current = 0;
    340 	unsigned int next;
    341 	int i;
    342 	int key;
    343 	int rc;
    344 
    345 	/* Print initial screen content */
    346 	draw_title_row();
    347 	color_set ( CPAIR_NORMAL, NULL );
    348 	for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
    349 		init_setting_index ( &widget, settings, i );
    350 		draw_setting ( &widget );
    351 	}
    352 
    353 	while ( 1 ) {
    354 		/* Redraw information and instruction rows */
    355 		draw_info_row ( widget.setting );
    356 		draw_instruction_row ( widget.editing );
    357 
    358 		/* Redraw current setting */
    359 		color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
    360 			    NULL );
    361 		draw_setting ( &widget );
    362 		color_set ( CPAIR_NORMAL, NULL );
    363 
    364 		key = getkey();
    365 		if ( widget.editing ) {
    366 			key = edit_setting ( &widget, key );
    367 			switch ( key ) {
    368 			case CR:
    369 			case LF:
    370 				if ( ( rc = save_setting ( &widget ) ) != 0 ) {
    371 					alert ( " Could not set %s: %s ",
    372 						widget.setting->name,
    373 						strerror ( rc ) );
    374 				}
    375 				/* Fall through */
    376 			case CTRL_C:
    377 				load_setting ( &widget );
    378 				break;
    379 			default:
    380 				/* Do nothing */
    381 				break;
    382 			}
    383 		} else {
    384 			next = current;
    385 			switch ( key ) {
    386 			case KEY_DOWN:
    387 				if ( next < ( NUM_SETTINGS - 1 ) )
    388 					next++;
    389 				break;
    390 			case KEY_UP:
    391 				if ( next > 0 )
    392 					next--;
    393 				break;
    394 			case CTRL_X:
    395 				return 0;
    396 			default:
    397 				edit_setting ( &widget, key );
    398 				break;
    399 			}
    400 			if ( next != current ) {
    401 				draw_setting ( &widget );
    402 				init_setting_index ( &widget, settings, next );
    403 				current = next;
    404 			}
    405 		}
    406 	}
    407 
    408 }
    409 
    410 int settings_ui ( struct settings *settings ) {
    411 	int rc;
    412 
    413 	initscr();
    414 	start_color();
    415 	init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
    416 	init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
    417 	init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
    418 	init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
    419 	color_set ( CPAIR_NORMAL, NULL );
    420 	erase();
    421 
    422 	rc = main_loop ( settings );
    423 
    424 	endwin();
    425 
    426 	return rc;
    427 }
    428