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