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