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