[Settings] Add settings hierarchy
[people/dverkamp/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 <stdio.h>
20 #include <stdarg.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <curses.h>
24 #include <console.h>
25 #include <gpxe/settings.h>
26 #include <gpxe/editbox.h>
27 #include <gpxe/keys.h>
28 #include <gpxe/settings_ui.h>
29
30 /** @file
31  *
32  * Option configuration console
33  *
34  */
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 TITLE_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 #define INSTRUCTION_ROW         22
49 #define INSTRUCTION_PAD "     "
50
51 /** Layout of text within a setting widget */
52 struct setting_row {
53         char start[0];
54         char pad1[1];
55         char name[15];
56         char pad2[1];
57         char value[60];
58         char pad3[1];
59         char nul;
60 } __attribute__ (( packed ));
61
62 /** A setting widget */
63 struct setting_widget {
64         /** Settings block */
65         struct settings *settings;
66         /** Configuration setting */
67         struct named_setting *setting;
68         /** Screen row */
69         unsigned int row;
70         /** Screen column */
71         unsigned int col;
72         /** Edit box widget used for editing setting */
73         struct edit_box editbox;
74         /** Editing in progress flag */
75         int editing;
76         /** Buffer for setting's value */
77         char value[256]; /* enough size for a DHCP string */
78 };
79
80 /** Registered configuration settings */
81 static struct named_setting named_settings[0]
82         __table_start ( struct named_setting, named_settings );
83 static struct named_setting named_settings_end[0]
84         __table_end ( struct named_setting, named_settings );
85 #define NUM_SETTINGS ( ( unsigned ) ( named_settings_end - named_settings ) )
86
87 static void load_setting ( struct setting_widget *widget ) __nonnull;
88 static int save_setting ( struct setting_widget *widget ) __nonnull;
89 static void init_setting ( struct setting_widget *widget,
90                            struct settings *settings,
91                            struct named_setting *setting,
92                            unsigned int row, unsigned int col ) __nonnull;
93 static void draw_setting ( struct setting_widget *widget ) __nonnull;
94 static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
95 static void init_setting_index ( struct setting_widget *widget,
96                                  struct settings *settings,
97                                  unsigned int index ) __nonnull;
98 static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
99 static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
100 static void valert ( const char *fmt, va_list args ) __nonnull;
101 static void alert ( const char *fmt, ... ) __nonnull;
102 static void draw_info_row ( struct named_setting *setting ) __nonnull;
103 static int main_loop ( struct settings *settings ) __nonnull;
104
105 /**
106  * Load setting widget value from configuration settings
107  *
108  * @v widget            Setting widget
109  *
110  */
111 static void load_setting ( struct setting_widget *widget ) {
112
113         /* Mark as not editing */
114         widget->editing = 0;
115
116         /* Read current setting value */
117         if ( fetch_typed_setting ( widget->settings, widget->setting->tag,
118                                    widget->setting->type, widget->value,
119                                    sizeof ( widget->value ) ) < 0 ) {
120                 widget->value[0] = '\0';
121         }       
122
123         /* Initialise edit box */
124         init_editbox ( &widget->editbox, widget->value,
125                        sizeof ( widget->value ), NULL, widget->row,
126                        ( widget->col + offsetof ( struct setting_row, value )),
127                        sizeof ( ( ( struct setting_row * ) NULL )->value ) );
128 }
129
130 /**
131  * Save setting widget value back to configuration settings
132  *
133  * @v widget            Setting widget
134  */
135 static int save_setting ( struct setting_widget *widget ) {
136         return store_typed_setting ( widget->settings, widget->setting->tag,
137                                      widget->setting->type, widget->value );
138 }
139
140 /**
141  * Initialise setting widget
142  *
143  * @v widget            Setting widget
144  * @v settings          Settings block
145  * @v setting           Configuration setting
146  * @v row               Screen row
147  * @v col               Screen column
148  */
149 static void init_setting ( struct setting_widget *widget,
150                            struct settings *settings,
151                            struct named_setting *setting,
152                            unsigned int row, unsigned int col ) {
153
154         /* Initialise widget structure */
155         memset ( widget, 0, sizeof ( *widget ) );
156         widget->settings = settings;
157         widget->setting = setting;
158         widget->row = row;
159         widget->col = col;
160
161         /* Read current setting value */
162         load_setting ( widget );
163 }
164
165 /**
166  * Draw setting widget
167  *
168  * @v widget            Setting widget
169  */
170 static void draw_setting ( struct setting_widget *widget ) {
171         struct setting_row row;
172         unsigned int len;
173         unsigned int curs_col;
174         char *value;
175
176         /* Fill row with spaces */
177         memset ( &row, ' ', sizeof ( row ) );
178         row.nul = '\0';
179
180         /* Construct dot-padded name */
181         memset ( row.name, '.', sizeof ( row.name ) );
182         len = strlen ( widget->setting->name );
183         if ( len > sizeof ( row.name ) )
184                 len = sizeof ( row.name );
185         memcpy ( row.name, widget->setting->name, len );
186
187         /* Construct space-padded value */
188         value = widget->value;
189         if ( ! *value )
190                 value = "<not specified>";
191         len = strlen ( value );
192         if ( len > sizeof ( row.value ) )
193                 len = sizeof ( row.value );
194         memcpy ( row.value, value, len );
195         curs_col = ( widget->col + offsetof ( typeof ( row ), value )
196                      + len );
197
198         /* Print row */
199         mvprintw ( widget->row, widget->col, "%s", row.start );
200         move ( widget->row, curs_col );
201         if ( widget->editing )
202                 draw_editbox ( &widget->editbox );
203 }
204
205 /**
206  * Edit setting widget
207  *
208  * @v widget            Setting widget
209  * @v key               Key pressed by user
210  * @ret key             Key returned to application, or zero
211  */
212 static int edit_setting ( struct setting_widget *widget, int key ) {
213         widget->editing = 1;
214         return edit_editbox ( &widget->editbox, key );
215 }
216
217 /**
218  * Initialise setting widget by index
219  *
220  * @v widget            Setting widget
221  * @v settings          Settings block
222  * @v index             Index of setting with settings list
223  */
224 static void init_setting_index ( struct setting_widget *widget,
225                                  struct settings *settings,
226                                  unsigned int index ) {
227         init_setting ( widget, settings, &named_settings[index],
228                        ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
229 }
230
231 /**
232  * Print message centred on specified row
233  *
234  * @v row               Row
235  * @v fmt               printf() format string
236  * @v args              printf() argument list
237  */
238 static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
239         char buf[COLS];
240         size_t len;
241
242         len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
243         mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
244 }
245
246 /**
247  * Print message centred on specified row
248  *
249  * @v row               Row
250  * @v fmt               printf() format string
251  * @v ..                printf() arguments
252  */
253 static void msg ( unsigned int row, const char *fmt, ... ) {
254         va_list args;
255
256         va_start ( args, fmt );
257         vmsg ( row, fmt, args );
258         va_end ( args );
259 }
260
261 /**
262  * Clear message on specified row
263  *
264  * @v row               Row
265  */
266 static void clearmsg ( unsigned int row ) {
267         move ( row, 0 );
268         clrtoeol();
269 }
270
271 /**
272  * Print alert message
273  *
274  * @v fmt               printf() format string
275  * @v args              printf() argument list
276  */
277 static void valert ( const char *fmt, va_list args ) {
278         clearmsg ( ALERT_ROW );
279         color_set ( CPAIR_ALERT, NULL );
280         vmsg ( ALERT_ROW, fmt, args );
281         sleep ( 2 );
282         color_set ( CPAIR_NORMAL, NULL );
283         clearmsg ( ALERT_ROW );
284 }
285
286 /**
287  * Print alert message
288  *
289  * @v fmt               printf() format string
290  * @v ...               printf() arguments
291  */
292 static void alert ( const char *fmt, ... ) {
293         va_list args;
294
295         va_start ( args, fmt );
296         valert ( fmt, args );
297         va_end ( args );
298 }
299
300 /**
301  * Draw title row
302  */
303 static void draw_title_row ( void ) {
304         attron ( A_BOLD );
305         msg ( TITLE_ROW, "gPXE option configuration console" );
306         attroff ( A_BOLD );
307 }
308
309 /**
310  * Draw information row
311  *
312  * @v setting           Current configuration setting
313  */
314 static void draw_info_row ( struct named_setting *setting ) {
315         clearmsg ( INFO_ROW );
316         attron ( A_BOLD );
317         msg ( INFO_ROW, "%s - %s", setting->name, setting->description );
318         attroff ( A_BOLD );
319 }
320
321 /**
322  * Draw instruction row
323  *
324  * @v editing           Editing in progress flag
325  */
326 static void draw_instruction_row ( int editing ) {
327         clearmsg ( INSTRUCTION_ROW );
328         if ( editing ) {
329                 msg ( INSTRUCTION_ROW,
330                       "Enter - accept changes" INSTRUCTION_PAD
331                       "Ctrl-C - discard changes" );
332         } else {
333                 msg ( INSTRUCTION_ROW,
334                       "Ctrl-X - exit configuration utility" );
335         }
336 }
337
338 static int main_loop ( struct settings *settings ) {
339         struct setting_widget widget;
340         unsigned int current = 0;
341         unsigned int next;
342         int i;
343         int key;
344         int rc;
345
346         /* Print initial screen content */
347         draw_title_row();
348         color_set ( CPAIR_NORMAL, NULL );
349         for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
350                 init_setting_index ( &widget, settings, i );
351                 draw_setting ( &widget );
352         }
353
354         while ( 1 ) {
355                 /* Redraw information and instruction rows */
356                 draw_info_row ( widget.setting );
357                 draw_instruction_row ( widget.editing );
358
359                 /* Redraw current setting */
360                 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
361                             NULL );
362                 draw_setting ( &widget );
363                 color_set ( CPAIR_NORMAL, NULL );
364
365                 key = getkey();
366                 if ( widget.editing ) {
367                         key = edit_setting ( &widget, key );
368                         switch ( key ) {
369                         case CR:
370                         case LF:
371                                 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
372                                         alert ( " Could not set %s: %s ",
373                                                 widget.setting->name,
374                                                 strerror ( rc ) );
375                                 }
376                                 /* Fall through */
377                         case CTRL_C:
378                                 load_setting ( &widget );
379                                 break;
380                         default:
381                                 /* Do nothing */
382                                 break;
383                         }
384                 } else {
385                         next = current;
386                         switch ( key ) {
387                         case KEY_DOWN:
388                                 if ( next < ( NUM_SETTINGS - 1 ) )
389                                         next++;
390                                 break;
391                         case KEY_UP:
392                                 if ( next > 0 )
393                                         next--;
394                                 break;
395                         case CTRL_X:
396                                 return 0;
397                         default:
398                                 edit_setting ( &widget, key );
399                                 break;
400                         }       
401                         if ( next != current ) {
402                                 draw_setting ( &widget );
403                                 init_setting_index ( &widget, settings, next );
404                                 current = next;
405                         }
406                 }
407         }
408         
409 }
410
411 int settings_ui ( struct settings *settings ) {
412         int rc;
413
414         initscr();
415         start_color();
416         init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
417         init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
418         init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
419         init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
420         color_set ( CPAIR_NORMAL, NULL );
421         erase();
422         
423         rc = main_loop ( settings );
424
425         endwin();
426
427         return rc;
428 }