[contrib] Allow Network Protocol header to display in rom-o-matic
[gpxe.git] / src / hci / tui / settings_ui.c
blob1915e583552fe5c1d0d052216d1af60184f5bb73
1 /*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 FILE_LICENCE ( GPL2_OR_LATER );
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>
32 /** @file
34 * Option configuration console
38 /* Colour pairs */
39 #define CPAIR_NORMAL 1
40 #define CPAIR_SELECT 2
41 #define CPAIR_EDIT 3
42 #define CPAIR_ALERT 4
44 /* Screen layout */
45 #define TITLE_ROW 1
46 #define SETTINGS_LIST_ROW 3
47 #define SETTINGS_LIST_COL 1
48 #define SETTINGS_LIST_ROWS 16
49 #define INFO_ROW 20
50 #define ALERT_ROW 20
51 #define INSTRUCTION_ROW 22
52 #define INSTRUCTION_PAD " "
54 /** Layout of text within a setting widget */
55 struct setting_row {
56 char start[0];
57 char pad1[1];
58 char name[15];
59 char pad2[1];
60 char value[60];
61 char pad3[1];
62 char nul;
63 } __attribute__ (( packed ));
65 /** A setting widget */
66 struct setting_widget {
67 /** Settings block */
68 struct settings *settings;
69 /** Index of the first visible setting, for scrolling. */
70 unsigned int first_visible;
71 /** Configuration setting */
72 struct setting *setting;
73 /** Screen row */
74 unsigned int row;
75 /** Screen column */
76 unsigned int col;
77 /** Edit box widget used for editing setting */
78 struct edit_box editbox;
79 /** Editing in progress flag */
80 int editing;
81 /** Buffer for setting's value */
82 char value[256]; /* enough size for a DHCP string */
85 /** Number of registered configuration settings */
86 #define NUM_SETTINGS table_num_entries ( SETTINGS )
88 static void load_setting ( struct setting_widget *widget ) __nonnull;
89 static int save_setting ( struct setting_widget *widget ) __nonnull;
90 static void init_widget ( struct setting_widget *widget,
91 struct settings *settings ) __nonnull;
92 static void draw_setting ( struct setting_widget *widget ) __nonnull;
93 static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
94 static void select_setting ( struct setting_widget *widget,
95 unsigned int index ) __nonnull;
96 static void reveal ( struct setting_widget *widget, unsigned int n) __nonnull;
97 static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
98 static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
99 static void valert ( const char *fmt, va_list args ) __nonnull;
100 static void alert ( const char *fmt, ... ) __nonnull;
101 static void draw_info_row ( struct setting *setting ) __nonnull;
102 static int main_loop ( struct settings *settings ) __nonnull;
105 * Load setting widget value from configuration settings
107 * @v widget Setting widget
110 static void load_setting ( struct setting_widget *widget ) {
112 /* Mark as not editing */
113 widget->editing = 0;
115 /* Read current setting value */
116 if ( fetchf_setting ( widget->settings, widget->setting,
117 widget->value, sizeof ( widget->value ) ) < 0 ) {
118 widget->value[0] = '\0';
121 /* Initialise edit box */
122 init_editbox ( &widget->editbox, widget->value,
123 sizeof ( widget->value ), NULL, widget->row,
124 ( widget->col + offsetof ( struct setting_row, value )),
125 sizeof ( ( ( struct setting_row * ) NULL )->value ), 0);
129 * Save setting widget value back to configuration settings
131 * @v widget Setting widget
133 static int save_setting ( struct setting_widget *widget ) {
134 return storef_setting ( widget->settings, widget->setting,
135 widget->value );
139 * Initialise the scrolling setting widget, drawing initial display.
141 * @v widget Setting widget
142 * @v settings Settings block
144 static void init_widget ( struct setting_widget *widget,
145 struct settings *settings ) {
146 memset ( widget, 0, sizeof ( *widget ) );
147 widget->settings = settings;
148 widget->first_visible = SETTINGS_LIST_ROWS;
149 reveal ( widget, 0 );
153 * Draw setting widget
155 * @v widget Setting widget
157 static void draw_setting ( struct setting_widget *widget ) {
158 struct setting_row row;
159 unsigned int len;
160 unsigned int curs_col;
161 char *value;
163 /* Fill row with spaces */
164 memset ( &row, ' ', sizeof ( row ) );
165 row.nul = '\0';
167 /* Construct dot-padded name */
168 memset ( row.name, '.', sizeof ( row.name ) );
169 len = strlen ( widget->setting->name );
170 if ( len > sizeof ( row.name ) )
171 len = sizeof ( row.name );
172 memcpy ( row.name, widget->setting->name, len );
174 /* Construct space-padded value */
175 value = widget->value;
176 if ( ! *value )
177 value = "<not specified>";
178 len = strlen ( value );
179 if ( len > sizeof ( row.value ) )
180 len = sizeof ( row.value );
181 memcpy ( row.value, value, len );
182 curs_col = ( widget->col + offsetof ( typeof ( row ), value )
183 + len );
185 /* Print row */
186 mvprintw ( widget->row, widget->col, "%s", row.start );
187 move ( widget->row, curs_col );
188 if ( widget->editing )
189 draw_editbox ( &widget->editbox );
193 * Edit setting widget
195 * @v widget Setting widget
196 * @v key Key pressed by user
197 * @ret key Key returned to application, or zero
199 static int edit_setting ( struct setting_widget *widget, int key ) {
200 widget->editing = 1;
201 return edit_editbox ( &widget->editbox, key );
205 * Select a setting for display updates, by index.
207 * @v widget Setting widget
208 * @v settings Settings block
209 * @v index Index of setting with settings list
211 static void select_setting ( struct setting_widget *widget,
212 unsigned int index ) {
213 struct setting *all_settings = table_start ( SETTINGS );
214 unsigned int skip = offsetof ( struct setting_widget, setting );
216 /* Reset the widget, preserving static state. */
217 memset ( ( char * ) widget + skip, 0, sizeof ( *widget ) - skip );
218 widget->setting = &all_settings[index];
219 widget->row = SETTINGS_LIST_ROW + index - widget->first_visible;
220 widget->col = SETTINGS_LIST_COL;
222 /* Read current setting value */
223 load_setting ( widget );
227 * Print message centred on specified row
229 * @v row Row
230 * @v fmt printf() format string
231 * @v args printf() argument list
233 static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
234 char buf[COLS];
235 size_t len;
237 len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
238 mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
242 * Print message centred on specified row
244 * @v row Row
245 * @v fmt printf() format string
246 * @v .. printf() arguments
248 static void msg ( unsigned int row, const char *fmt, ... ) {
249 va_list args;
251 va_start ( args, fmt );
252 vmsg ( row, fmt, args );
253 va_end ( args );
257 * Clear message on specified row
259 * @v row Row
261 static void clearmsg ( unsigned int row ) {
262 move ( row, 0 );
263 clrtoeol();
267 * Print alert message
269 * @v fmt printf() format string
270 * @v args printf() argument list
272 static void valert ( const char *fmt, va_list args ) {
273 clearmsg ( ALERT_ROW );
274 color_set ( CPAIR_ALERT, NULL );
275 vmsg ( ALERT_ROW, fmt, args );
276 sleep ( 2 );
277 color_set ( CPAIR_NORMAL, NULL );
278 clearmsg ( ALERT_ROW );
282 * Print alert message
284 * @v fmt printf() format string
285 * @v ... printf() arguments
287 static void alert ( const char *fmt, ... ) {
288 va_list args;
290 va_start ( args, fmt );
291 valert ( fmt, args );
292 va_end ( args );
296 * Draw title row
298 static void draw_title_row ( void ) {
299 attron ( A_BOLD );
300 msg ( TITLE_ROW, "gPXE option configuration console" );
301 attroff ( A_BOLD );
305 * Draw information row
307 * @v setting Current configuration setting
309 static void draw_info_row ( struct setting *setting ) {
310 clearmsg ( INFO_ROW );
311 attron ( A_BOLD );
312 msg ( INFO_ROW, "%s - %s", setting->name, setting->description );
313 attroff ( A_BOLD );
317 * Draw instruction row
319 * @v editing Editing in progress flag
321 static void draw_instruction_row ( int editing ) {
322 clearmsg ( INSTRUCTION_ROW );
323 if ( editing ) {
324 msg ( INSTRUCTION_ROW,
325 "Enter - accept changes" INSTRUCTION_PAD
326 "Ctrl-C - discard changes" );
327 } else {
328 msg ( INSTRUCTION_ROW,
329 "Ctrl-D - delete setting" INSTRUCTION_PAD
330 "Ctrl-X - exit configuration utility" );
335 * Reveal a setting by index: Scroll the setting list to reveal the
336 * specified setting.
338 * @widget The main loop's display widget.
339 * @n The index of the setting to reveal.
341 static void reveal ( struct setting_widget *widget, unsigned int n)
343 unsigned int i;
345 /* Simply return if setting N is already on-screen. */
346 if ( n - widget->first_visible < SETTINGS_LIST_ROWS )
347 return;
349 /* Jump scroll to make the specified setting visible. */
350 while ( widget->first_visible < n )
351 widget->first_visible += SETTINGS_LIST_ROWS;
352 while ( widget->first_visible > n )
353 widget->first_visible -= SETTINGS_LIST_ROWS;
355 /* Draw elipses before and/or after the settings list to
356 represent any invisible settings. */
357 mvaddstr ( SETTINGS_LIST_ROW - 1,
358 SETTINGS_LIST_COL + 1,
359 widget->first_visible > 0 ? "..." : " " );
360 mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
361 SETTINGS_LIST_COL + 1,
362 ( widget->first_visible + SETTINGS_LIST_ROWS < NUM_SETTINGS
363 ? "..."
364 : " " ) );
366 /* Draw visible settings. */
367 for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
368 if ( widget->first_visible + i < NUM_SETTINGS ) {
369 select_setting ( widget, widget->first_visible + i );
370 draw_setting ( widget );
371 } else {
372 clearmsg ( SETTINGS_LIST_ROW + i );
376 /* Set the widget to the current row, which will be redrawn
377 appropriately by the main loop. */
378 select_setting ( widget, n );
381 static int main_loop ( struct settings *settings ) {
382 struct setting_widget widget;
383 unsigned int current = 0;
384 unsigned int next;
385 int key;
386 int rc;
388 /* Print initial screen content */
389 draw_title_row();
390 color_set ( CPAIR_NORMAL, NULL );
391 init_widget ( &widget, settings );
393 while ( 1 ) {
394 /* Redraw information and instruction rows */
395 draw_info_row ( widget.setting );
396 draw_instruction_row ( widget.editing );
398 /* Redraw current setting */
399 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
400 NULL );
401 draw_setting ( &widget );
402 color_set ( CPAIR_NORMAL, NULL );
404 key = getkey();
405 if ( widget.editing ) {
406 key = edit_setting ( &widget, key );
407 switch ( key ) {
408 case CR:
409 case LF:
410 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
411 alert ( " Could not set %s: %s ",
412 widget.setting->name,
413 strerror ( rc ) );
415 /* Fall through */
416 case CTRL_C:
417 load_setting ( &widget );
418 break;
419 default:
420 /* Do nothing */
421 break;
423 } else {
424 next = current;
425 switch ( key ) {
426 case KEY_DOWN:
427 if ( next < ( NUM_SETTINGS - 1 ) )
428 reveal ( &widget, ++next );
429 break;
430 case KEY_UP:
431 if ( next > 0 )
432 reveal ( &widget, --next ) ;
433 break;
434 case CTRL_D:
435 delete_setting ( widget.settings,
436 widget.setting );
437 select_setting ( &widget, next );
438 draw_setting ( &widget );
439 break;
440 case CTRL_X:
441 return 0;
442 default:
443 edit_setting ( &widget, key );
444 break;
446 if ( next != current ) {
447 draw_setting ( &widget );
448 select_setting ( &widget, next );
449 current = next;
456 int settings_ui ( struct settings *settings ) {
457 int rc;
459 initscr();
460 start_color();
461 init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
462 init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
463 init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
464 init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
465 color_set ( CPAIR_NORMAL, NULL );
466 erase();
468 rc = main_loop ( settings );
470 endwin();
472 return rc;