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