1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@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 19FILE_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 */ 54struct 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 */ 65struct 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 85static void load_setting ( struct setting_widget *widget ) __nonnull; 86static int save_setting ( struct setting_widget *widget ) __nonnull; 87static void init_setting ( struct setting_widget *widget, 88 struct settings *settings, 89 struct setting *setting, 90 unsigned int row, unsigned int col ) __nonnull; 91static void draw_setting ( struct setting_widget *widget ) __nonnull; 92static int edit_setting ( struct setting_widget *widget, int key ) __nonnull; 93static void init_setting_index ( struct setting_widget *widget, 94 struct settings *settings, 95 unsigned int index ) __nonnull; 96static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull; 97static void msg ( unsigned int row, const char *fmt, ... ) __nonnull; 98static void valert ( const char *fmt, va_list args ) __nonnull; 99static void alert ( const char *fmt, ... ) __nonnull; 100static void draw_info_row ( struct setting *setting ) __nonnull; 101static 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 */ 109static 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 */ 132static 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 */ 146static 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 */ 167static 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 */ 209static 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 */ 221static 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 */ 237static 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 */ 252static 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 */ 265static 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 */ 276static 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 */ 291static 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 */ 302static 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 */ 313static 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 */ 325static 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 337static 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 410int 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