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