Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / hci / tui / settings_ui.c
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <curses.h>
27 #include <ipxe/console.h>
28 #include <ipxe/settings.h>
29 #include <ipxe/editbox.h>
30 #include <ipxe/keys.h>
31 #include <ipxe/ansicol.h>
32 #include <ipxe/settings_ui.h>
33
34 /** @file
35  *
36  * Option configuration console
37  *
38  */
39
40 /* Screen layout */
41 #define TITLE_ROW               1U
42 #define SETTINGS_LIST_ROW       3U
43 #define SETTINGS_LIST_COL       1U
44 #define SETTINGS_LIST_ROWS      ( LINES - 6U - SETTINGS_LIST_ROW )
45 #define INFO_ROW                ( LINES - 5U )
46 #define ALERT_ROW               ( LINES - 2U )
47 #define INSTRUCTION_ROW         ( LINES - 2U )
48 #define INSTRUCTION_PAD "     "
49
50 /** Layout of text within a setting widget */
51 #define SETTING_ROW_TEXT( cols ) struct {                               \
52         char start[0];                                                  \
53         char pad1[1];                                                   \
54         union {                                                         \
55                 char settings[ cols - 1 - 1 - 1 - 1 ];                  \
56                 struct {                                                \
57                         char name[15];                                  \
58                         char pad2[1];                                   \
59                         char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ];    \
60                 } setting;                                              \
61         } u;                                                            \
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 origin
74          *
75          * Valid only for rows that represent individual settings.
76          */
77         struct settings *origin;
78         /** Configuration setting
79          *
80          * Valid only for rows that represent individual settings.
81          */
82         struct setting setting;
83         /** Screen row */
84         unsigned int row;
85         /** Screen column */
86         unsigned int col;
87         /** Edit box widget used for editing setting */
88         struct edit_box editbox;
89         /** Editing in progress flag */
90         int editing;
91         /** Buffer for setting's value */
92         char value[256]; /* enough size for a DHCP string */
93 };
94
95 /** A settings widget */
96 struct setting_widget {
97         /** Settings block */
98         struct settings *settings;
99         /** Number of rows */
100         unsigned int num_rows;
101         /** Current row index */
102         unsigned int current;
103         /** Index of the first visible row, for scrolling. */
104         unsigned int first_visible;
105         /** Active row */
106         struct setting_row_widget row;
107 };
108
109 /**
110  * Select a setting row
111  *
112  * @v widget            Setting widget
113  * @v index             Index of setting row
114  * @ret count           Number of settings rows
115  */
116 static unsigned int select_setting_row ( struct setting_widget *widget,
117                                          unsigned int index ) {
118         SETTING_ROW_TEXT ( COLS ) *text;
119         struct settings *settings;
120         struct setting *setting;
121         struct setting *previous = NULL;
122         unsigned int count = 0;
123
124         /* Initialise structure */
125         memset ( &widget->row, 0, sizeof ( widget->row ) );
126         widget->current = index;
127         widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
128         widget->row.col = SETTINGS_LIST_COL;
129
130         /* Include parent settings block, if applicable */
131         if ( widget->settings->parent && ( count++ == index ) ) {
132                 widget->row.settings = widget->settings->parent;
133                 snprintf ( widget->row.value, sizeof ( widget->row.value ),
134                            "../" );
135         }
136
137         /* Include any child settings blocks, if applicable */
138         list_for_each_entry ( settings, &widget->settings->children, siblings ){
139                 if ( count++ == index ) {
140                         widget->row.settings = settings;
141                         snprintf ( widget->row.value,
142                                    sizeof ( widget->row.value ), "%s/",
143                                    settings->name );
144                 }
145         }
146
147         /* Include any applicable settings */
148         for_each_table_entry ( setting, SETTINGS ) {
149
150                 /* Skip inapplicable settings */
151                 if ( ! setting_applies ( widget->settings, setting ) )
152                         continue;
153
154                 /* Skip duplicate settings */
155                 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
156                         continue;
157                 previous = setting;
158
159                 /* Read current setting value and origin */
160                 if ( count++ == index ) {
161                         fetchf_setting ( widget->settings, setting,
162                                          &widget->row.origin,
163                                          &widget->row.setting,
164                                          widget->row.value,
165                                          sizeof ( widget->row.value ) );
166                 }
167         }
168
169         /* Initialise edit box */
170         init_editbox ( &widget->row.editbox, widget->row.value,
171                        sizeof ( widget->row.value ), NULL, widget->row.row,
172                        ( widget->row.col +
173                          offsetof ( typeof ( *text ), u.setting.value ) ),
174                        sizeof ( text->u.setting.value ), 0 );
175
176         return count;
177 }
178
179 /**
180  * Copy string without NUL termination
181  *
182  * @v dest              Destination
183  * @v src               Source
184  * @v len               Maximum length of destination
185  * @ret len             Length of (unterminated) string
186  */
187 static size_t string_copy ( char *dest, const char *src, size_t len ) {
188         size_t src_len;
189
190         src_len = strlen ( src );
191         if ( len > src_len )
192                 len = src_len;
193         memcpy ( dest, src, len );
194         return len;
195 }
196
197 /**
198  * Draw setting row
199  *
200  * @v widget            Setting widget
201  */
202 static void draw_setting_row ( struct setting_widget *widget ) {
203         SETTING_ROW_TEXT ( COLS ) text;
204         unsigned int curs_offset;
205         char *value;
206
207         /* Fill row with spaces */
208         memset ( &text, ' ', sizeof ( text ) );
209         text.nul = '\0';
210
211         /* Construct row content */
212         if ( widget->row.settings ) {
213
214                 /* Construct space-padded name */
215                 curs_offset = ( offsetof ( typeof ( text ), u.settings ) +
216                                 string_copy ( text.u.settings,
217                                               widget->row.value,
218                                               sizeof ( text.u.settings ) ) );
219
220         } else {
221
222                 /* Construct dot-padded name */
223                 memset ( text.u.setting.name, '.',
224                          sizeof ( text.u.setting.name ) );
225                 string_copy ( text.u.setting.name, widget->row.setting.name,
226                               sizeof ( text.u.setting.name ) );
227
228                 /* Construct space-padded value */
229                 value = widget->row.value;
230                 if ( ! *value )
231                         value = "<not specified>";
232                 curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) +
233                                 string_copy ( text.u.setting.value, value,
234                                               sizeof ( text.u.setting.value )));
235         }
236
237         /* Print row */
238         if ( ( widget->row.origin == widget->settings ) ||
239              ( widget->row.settings != NULL ) ) {
240                 attron ( A_BOLD );
241         }
242         mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
243         attroff ( A_BOLD );
244         move ( widget->row.row, widget->row.col + curs_offset );
245 }
246
247 /**
248  * Edit setting widget
249  *
250  * @v widget            Setting widget
251  * @v key               Key pressed by user
252  * @ret key             Key returned to application, or zero
253  */
254 static int edit_setting ( struct setting_widget *widget, int key ) {
255         assert ( widget->row.setting.name != NULL );
256         widget->row.editing = 1;
257         return edit_editbox ( &widget->row.editbox, key );
258 }
259
260 /**
261  * Save setting widget value back to configuration settings
262  *
263  * @v widget            Setting widget
264  */
265 static int save_setting ( struct setting_widget *widget ) {
266         assert ( widget->row.setting.name != NULL );
267         return storef_setting ( widget->settings, &widget->row.setting,
268                                 widget->row.value );
269 }
270
271 /**
272  * Print message centred on specified row
273  *
274  * @v row               Row
275  * @v fmt               printf() format string
276  * @v args              printf() argument list
277  */
278 static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
279         char buf[COLS];
280         size_t len;
281
282         len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
283         mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
284 }
285
286 /**
287  * Print message centred on specified row
288  *
289  * @v row               Row
290  * @v fmt               printf() format string
291  * @v ..                printf() arguments
292  */
293 static void msg ( unsigned int row, const char *fmt, ... ) {
294         va_list args;
295
296         va_start ( args, fmt );
297         vmsg ( row, fmt, args );
298         va_end ( args );
299 }
300
301 /**
302  * Clear message on specified row
303  *
304  * @v row               Row
305  */
306 static void clearmsg ( unsigned int row ) {
307         move ( row, 0 );
308         clrtoeol();
309 }
310
311 /**
312  * Print alert message
313  *
314  * @v fmt               printf() format string
315  * @v args              printf() argument list
316  */
317 static void valert ( const char *fmt, va_list args ) {
318         clearmsg ( ALERT_ROW );
319         color_set ( CPAIR_ALERT, NULL );
320         vmsg ( ALERT_ROW, fmt, args );
321         sleep ( 2 );
322         color_set ( CPAIR_NORMAL, NULL );
323         clearmsg ( ALERT_ROW );
324 }
325
326 /**
327  * Print alert message
328  *
329  * @v fmt               printf() format string
330  * @v ...               printf() arguments
331  */
332 static void alert ( const char *fmt, ... ) {
333         va_list args;
334
335         va_start ( args, fmt );
336         valert ( fmt, args );
337         va_end ( args );
338 }
339
340 /**
341  * Draw title row
342  *
343  * @v widget            Setting widget
344  */
345 static void draw_title_row ( struct setting_widget *widget ) {
346         const char *name;
347
348         clearmsg ( TITLE_ROW );
349         name = settings_name ( widget->settings );
350         attron ( A_BOLD );
351         msg ( TITLE_ROW, "iPXE configuration settings%s%s",
352               ( name[0] ? " - " : "" ), name );
353         attroff ( A_BOLD );
354 }
355
356 /**
357  * Draw information row
358  *
359  * @v widget            Setting widget
360  */
361 static void draw_info_row ( struct setting_widget *widget ) {
362         char buf[32];
363
364         /* Draw nothing unless this row represents a setting */
365         clearmsg ( INFO_ROW );
366         clearmsg ( INFO_ROW + 1 );
367         if ( ! widget->row.setting.name )
368                 return;
369
370         /* Determine a suitable setting name */
371         setting_name ( ( widget->row.origin ?
372                          widget->row.origin : widget->settings ),
373                        &widget->row.setting, buf, sizeof ( buf ) );
374
375         /* Draw row */
376         attron ( A_BOLD );
377         msg ( INFO_ROW, "%s - %s", buf, widget->row.setting.description );
378         attroff ( A_BOLD );
379         color_set ( CPAIR_URL, NULL );
380         msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
381               widget->row.setting.name );
382         color_set ( CPAIR_NORMAL, NULL );
383 }
384
385 /**
386  * Draw instruction row
387  *
388  * @v widget            Setting widget
389  */
390 static void draw_instruction_row ( struct setting_widget *widget ) {
391
392         clearmsg ( INSTRUCTION_ROW );
393         if ( widget->row.editing ) {
394                 msg ( INSTRUCTION_ROW,
395                       "Enter - accept changes" INSTRUCTION_PAD
396                       "Ctrl-C - discard changes" );
397         } else {
398                 msg ( INSTRUCTION_ROW,
399                       "%sCtrl-X - exit configuration utility",
400                       ( ( widget->row.origin == widget->settings ) ?
401                         "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
402         }
403 }
404
405 /**
406  * Reveal setting row
407  *
408  * @v widget            Setting widget
409  * @v index             Index of setting row
410  */
411 static void reveal_setting_row ( struct setting_widget *widget,
412                                  unsigned int index ) {
413         unsigned int i;
414
415         /* Simply return if setting N is already on-screen. */
416         if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
417                 return;
418
419         /* Jump scroll to make the specified setting row visible. */
420         while ( widget->first_visible < index )
421                 widget->first_visible += SETTINGS_LIST_ROWS;
422         while ( widget->first_visible > index )
423                 widget->first_visible -= SETTINGS_LIST_ROWS;
424
425         /* Draw ellipses before and/or after the settings list to
426          * represent any invisible settings.
427          */
428         mvaddstr ( SETTINGS_LIST_ROW - 1,
429                    SETTINGS_LIST_COL + 1,
430                    widget->first_visible > 0 ? "..." : "   " );
431         mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
432                    SETTINGS_LIST_COL + 1,
433                    ( ( widget->first_visible + SETTINGS_LIST_ROWS )
434                      < widget->num_rows ? "..." : "   " ) );
435
436         /* Draw visible settings. */
437         for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
438                 if ( ( widget->first_visible + i ) < widget->num_rows ) {
439                         select_setting_row ( widget,
440                                              widget->first_visible + i );
441                         draw_setting_row ( widget );
442                 } else {
443                         clearmsg ( SETTINGS_LIST_ROW + i );
444                 }
445         }
446 }
447
448 /**
449  * Reveal setting row
450  *
451  * @v widget            Setting widget
452  * @v settings          Settings block
453  */
454 static void init_widget ( struct setting_widget *widget,
455                           struct settings *settings ) {
456
457         widget->settings = settings_target ( settings );
458         widget->num_rows = select_setting_row ( widget, 0 );
459         widget->first_visible = SETTINGS_LIST_ROWS;
460         draw_title_row ( widget );
461         reveal_setting_row ( widget, 0 );
462         select_setting_row ( widget, 0 );
463 }
464
465 static int main_loop ( struct settings *settings ) {
466         struct setting_widget widget;
467         int redraw = 1;
468         int move;
469         unsigned int next;
470         int key;
471         int rc;
472
473         /* Print initial screen content */
474         color_set ( CPAIR_NORMAL, NULL );
475         memset ( &widget, 0, sizeof ( widget ) );
476         init_widget ( &widget, settings );
477
478         while ( 1 ) {
479
480                 /* Redraw rows if necessary */
481                 if ( redraw ) {
482                         draw_info_row ( &widget );
483                         draw_instruction_row ( &widget );
484                         color_set ( ( widget.row.editing ?
485                                       CPAIR_EDIT : CPAIR_SELECT ), NULL );
486                         draw_setting_row ( &widget );
487                         color_set ( CPAIR_NORMAL, NULL );
488                         curs_set ( widget.row.editing );
489                         redraw = 0;
490                 }
491
492                 if ( widget.row.editing ) {
493
494                         /* Sanity check */
495                         assert ( widget.row.setting.name != NULL );
496
497                         /* Redraw edit box */
498                         color_set ( CPAIR_EDIT, NULL );
499                         draw_editbox ( &widget.row.editbox );
500                         color_set ( CPAIR_NORMAL, NULL );
501
502                         /* Process keypress */
503                         key = edit_setting ( &widget, getkey ( 0 ) );
504                         switch ( key ) {
505                         case CR:
506                         case LF:
507                                 if ( ( rc = save_setting ( &widget ) ) != 0 )
508                                         alert ( " %s ", strerror ( rc ) );
509                                 /* Fall through */
510                         case CTRL_C:
511                                 select_setting_row ( &widget, widget.current );
512                                 redraw = 1;
513                                 break;
514                         default:
515                                 /* Do nothing */
516                                 break;
517                         }
518
519                 } else {
520
521                         /* Process keypress */
522                         key = getkey ( 0 );
523                         move = 0;
524                         switch ( key ) {
525                         case KEY_UP:
526                                 move = -1;
527                                 break;
528                         case KEY_DOWN:
529                                 move = +1;
530                                 break;
531                         case KEY_PPAGE:
532                                 move = ( widget.first_visible -
533                                          widget.current - 1 );
534                                 break;
535                         case KEY_NPAGE:
536                                 move = ( widget.first_visible - widget.current
537                                          + SETTINGS_LIST_ROWS );
538                                 break;
539                         case KEY_HOME:
540                                 move = -widget.num_rows;
541                                 break;
542                         case KEY_END:
543                                 move = +widget.num_rows;
544                                 break;
545                         case CTRL_D:
546                                 if ( ! widget.row.setting.name )
547                                         break;
548                                 if ( ( rc = delete_setting ( widget.settings,
549                                                 &widget.row.setting ) ) != 0 ) {
550                                         alert ( " %s ", strerror ( rc ) );
551                                 }
552                                 select_setting_row ( &widget, widget.current );
553                                 redraw = 1;
554                                 break;
555                         case CTRL_X:
556                                 return 0;
557                         case CR:
558                         case LF:
559                                 if ( widget.row.settings ) {
560                                         init_widget ( &widget,
561                                                       widget.row.settings );
562                                         redraw = 1;
563                                 }
564                                 /* Fall through */
565                         default:
566                                 if ( widget.row.setting.name ) {
567                                         edit_setting ( &widget, key );
568                                         redraw = 1;
569                                 }
570                                 break;
571                         }
572                         if ( move ) {
573                                 next = ( widget.current + move );
574                                 if ( ( int ) next < 0 )
575                                         next = 0;
576                                 if ( next >= widget.num_rows )
577                                         next = ( widget.num_rows - 1 );
578                                 if ( next != widget.current ) {
579                                         draw_setting_row ( &widget );
580                                         redraw = 1;
581                                         reveal_setting_row ( &widget, next );
582                                         select_setting_row ( &widget, next );
583                                 }
584                         }
585                 }
586         }
587 }
588
589 int settings_ui ( struct settings *settings ) {
590         int rc;
591
592         initscr();
593         start_color();
594         color_set ( CPAIR_NORMAL, NULL );
595         curs_set ( 0 );
596         erase();
597         
598         rc = main_loop ( settings );
599
600         endwin();
601
602         return rc;
603 }