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