Added descriptive text for settings and setting types, and display it in
[people/mcb30/gpxe.git] / 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., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdarg.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <curses.h>
23 #include <console.h>
24 #include <gpxe/settings.h>
25 #include <gpxe/editbox.h>
26
27 /** @file
28  *
29  * Configuration settings UI
30  *
31  */
32
33 #include <gpxe/nvo.h>
34 extern struct nvo_block *ugly_nvo_hack;
35
36 /* Colour pairs */
37 #define CPAIR_NORMAL    1
38 #define CPAIR_SELECT    2
39 #define CPAIR_EDIT      3
40 #define CPAIR_ALERT     4
41
42 /* Screen layout */
43 #define BANNER_ROW              1
44 #define SETTINGS_LIST_ROW       3
45 #define SETTINGS_LIST_COL       1
46 #define INFO_ROW                20
47 #define ALERT_ROW               20
48
49 /** Layout of text within a setting widget */
50 struct setting_row {
51         char start[0];
52         char pad1[1];
53         char name[15];
54         char pad2[1];
55         char value[60];
56         char pad3[1];
57         char nul;
58 } __attribute__ (( packed ));
59
60 /** A setting widget */
61 struct setting_widget {
62         /** Configuration context */
63         struct config_context *context;
64         /** Configuration setting */
65         struct config_setting *setting;
66         /** Screen row */
67         unsigned int row;
68         /** Screen column */
69         unsigned int col;
70         /** Edit box widget used for editing setting */
71         struct edit_box editbox;
72         /** Editing active flag */
73         int editing;
74         /** Buffer for setting's value */
75         char value[256]; /* enough size for a DHCP string */
76 };
77
78 /** Registered configuration settings */
79 static struct config_setting
80 config_settings[0] __table_start ( config_settings );
81 static struct config_setting
82 config_settings_end[0] __table_end ( config_settings );
83 #define NUM_SETTINGS ( ( unsigned ) ( config_settings_end - config_settings ) )
84
85 /**
86  * Load setting widget value from configuration context
87  *
88  * @v widget            Setting widget
89  *
90  */
91 static void load_setting ( struct setting_widget *widget ) {
92
93         /* Mark as not editing */
94         widget->editing = 0;
95
96         /* Read current setting value */
97         if ( show_setting ( widget->context, widget->setting,
98                             widget->value, sizeof ( widget->value ) ) != 0 ) {
99                 widget->value[0] = '\0';
100         }       
101
102         /* Initialise edit box */
103         init_editbox ( &widget->editbox, widget->value,
104                        sizeof ( widget->value ), NULL, widget->row,
105                        ( widget->col + offsetof ( struct setting_row, value )),
106                        sizeof ( ( ( struct setting_row * ) NULL )->value ) );
107 }
108
109 /**
110  * Save setting widget value back to configuration context
111  *
112  * @v widget            Setting widget
113  */
114 static int save_setting ( struct setting_widget *widget ) {
115         return set_setting ( widget->context, widget->setting, widget->value );
116 }
117
118 /**
119  * Initialise setting widget
120  *
121  * @v widget            Setting widget
122  * @v context           Configuration context
123  * @v setting           Configuration setting
124  * @v row               Screen row
125  * @v col               Screen column
126  */
127 static void init_setting ( struct setting_widget *widget,
128                            struct config_context *context,
129                            struct config_setting *setting,
130                            unsigned int row, unsigned int col ) {
131
132         /* Initialise widget structure */
133         memset ( widget, 0, sizeof ( *widget ) );
134         widget->context = context;
135         widget->setting = setting;
136         widget->row = row;
137         widget->col = col;
138
139         /* Read current setting value */
140         load_setting ( widget );
141 }
142
143 /**
144  * Draw setting widget
145  *
146  * @v widget            Setting widget
147  */
148 static void draw_setting ( struct setting_widget *widget ) {
149         struct setting_row row;
150         unsigned int len;
151         unsigned int curs_col;
152         char *value;
153
154         /* Fill row with spaces */
155         memset ( &row, ' ', sizeof ( row ) );
156         row.nul = '\0';
157
158         /* Construct dot-padded name */
159         memset ( row.name, '.', sizeof ( row.name ) );
160         len = strlen ( widget->setting->name );
161         if ( len > sizeof ( row.name ) )
162                 len = sizeof ( row.name );
163         memcpy ( row.name, widget->setting->name, len );
164
165         /* Construct space-padded value */
166         value = widget->value;
167         if ( ! *value )
168                 value = "<not specified>";
169         len = strlen ( value );
170         if ( len > sizeof ( row.value ) )
171                 len = sizeof ( row.value );
172         memcpy ( row.value, value, len );
173         curs_col = ( widget->col + offsetof ( typeof ( row ), value )
174                      + len );
175
176         /* Print row */
177         mvprintw ( widget->row, widget->col, "%s", row.start );
178         move ( widget->row, curs_col );
179         if ( widget->editing )
180                 draw_editbox ( &widget->editbox );
181 }
182
183 /**
184  * Edit setting widget
185  *
186  * @v widget            Setting widget
187  * @v key               Key pressed by user
188  * @ret key             Key returned to application, or zero
189  */
190 static int edit_setting ( struct setting_widget *widget, int key ) {
191         widget->editing = 1;
192         return edit_editbox ( &widget->editbox, key );
193 }
194
195 /**
196  * Initialise setting widget by index
197  *
198  * @v widget            Setting widget
199  * @v context           Configuration context
200  * @v index             Index of setting with settings list
201  */
202 static void init_setting_index ( struct setting_widget *widget,
203                                  struct config_context *context,
204                                  unsigned int index ) {
205         init_setting ( widget, context, &config_settings[index],
206                        ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
207 }
208
209 /**
210  * Print message centred on specified row
211  *
212  * @v row               Row
213  * @v fmt               printf() format string
214  * @v args              printf() argument list
215  */
216 static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
217         char buf[COLS];
218         size_t len;
219
220         len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
221         mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
222 }
223
224 /**
225  * Print message centred on specified row
226  *
227  * @v row               Row
228  * @v fmt               printf() format string
229  * @v ..                printf() arguments
230  */
231 static void msg ( unsigned int row, const char *fmt, ... ) {
232         va_list args;
233
234         va_start ( args, fmt );
235         vmsg ( row, fmt, args );
236         va_end ( args );
237 }
238
239 /**
240  * Clear message on specified row
241  *
242  * @v row               Row
243  */
244 static void clearmsg ( unsigned int row ) {
245         move ( row, 0 );
246         clrtoeol();
247 }
248
249 /**
250  * Print alert message
251  *
252  * @v fmt               printf() format string
253  * @v args              printf() argument list
254  */
255 static void valert ( const char *fmt, va_list args ) {
256         color_set ( CPAIR_ALERT, NULL );
257         vmsg ( ALERT_ROW, fmt, args );
258         sleep ( 2 );
259         color_set ( CPAIR_NORMAL, NULL );
260         clearmsg ( ALERT_ROW );
261 }
262
263 /**
264  * Print alert message
265  *
266  * @v fmt               printf() format string
267  * @v ...               printf() arguments
268  */
269 static void alert ( const char *fmt, ... ) {
270         va_list args;
271
272         va_start ( args, fmt );
273         valert ( fmt, args );
274         va_end ( args );
275 }
276
277 static void main_loop ( struct config_context *context ) {
278         struct setting_widget widget;
279         unsigned int current = 0;
280         unsigned int next;
281         int i;
282         int key;
283         int rc;
284
285         /* Print initial screen content */
286         color_set ( CPAIR_NORMAL, NULL );
287         attron ( A_BOLD );
288         msg ( BANNER_ROW, "gPXE option configuration console" );
289         attroff ( A_BOLD );
290         for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
291                 init_setting_index ( &widget, context, i );
292                 draw_setting ( &widget );
293         }
294
295         while ( 1 ) {
296                 /* Redraw information row */
297                 clearmsg ( INFO_ROW );
298                 msg ( INFO_ROW, "%s (%s) - %s", widget.setting->name,
299                       widget.setting->type->description,
300                       widget.setting->description );
301
302                 /* Redraw current setting */
303                 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
304                             NULL );
305                 draw_setting ( &widget );
306                 color_set ( CPAIR_NORMAL, NULL );
307
308                 key = getchar();
309                 if ( widget.editing ) {
310                         key = edit_setting ( &widget, key );
311                         switch ( key ) {
312                         case 0x0a: /* Enter */
313                                 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
314                                         alert ( " Could not set %s: %s ",
315                                                 widget.setting->name,
316                                                 strerror ( rc ) );
317                                 }
318                                 /* Fall through */
319                         case 0x03: /* Ctrl-C */
320                                 load_setting ( &widget );
321                                 break;
322                         default:
323                                 /* Do nothing */
324                                 break;
325                         }
326                 } else {
327                         next = current;
328                         switch ( key ) {
329                         case '+':
330                                 if ( next < ( NUM_SETTINGS - 1 ) )
331                                         next++;
332                                 break;
333                         case '-':
334                                 if ( next > 0 )
335                                         next--;
336                                 break;
337                         default:
338                                 edit_setting ( &widget, key );
339                                 break;
340                         }       
341                         if ( next != current ) {
342                                 draw_setting ( &widget );
343                                 init_setting_index ( &widget, context, next );
344                                 current = next;
345                         }
346                 }
347         }
348         
349 }
350
351 void uitest ( void ) {
352         struct config_context dummy_context;
353
354         dummy_context.options = ugly_nvo_hack->options;
355
356         initscr();
357         start_color();
358         init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
359         init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
360         init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
361         init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
362         color_set ( CPAIR_NORMAL, NULL );
363         erase();
364         
365         main_loop ( &dummy_context );
366
367         endwin();
368 }