Alert when saving settings fails
[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 <unistd.h>
20 #include <string.h>
21 #include <curses.h>
22 #include <console.h>
23 #include <gpxe/settings.h>
24 #include <gpxe/editbox.h>
25
26 /** @file
27  *
28  * Configuration settings UI
29  *
30  */
31
32 #include <gpxe/nvo.h>
33 extern struct nvo_block *ugly_nvo_hack;
34
35 /* Colour pairs */
36 #define CPAIR_NORMAL    1
37 #define CPAIR_SELECT    2
38 #define CPAIR_EDIT      3
39 #define CPAIR_ALERT     4
40
41 /* Screen layout */
42 #define SETTINGS_LIST_ROW       3
43 #define SETTINGS_LIST_COL       1
44 #define ALERT_ROW               20
45
46 /** Layout of text within a setting widget */
47 struct setting_row {
48         char start[0];
49         char pad1[1];
50         char name[15];
51         char pad2[1];
52         char value[60];
53         char pad3[1];
54         char nul;
55 } __attribute__ (( packed ));
56
57 /** A setting widget */
58 struct setting_widget {
59         /** Configuration context */
60         struct config_context *context;
61         /** Configuration setting */
62         struct config_setting *setting;
63         /** Screen row */
64         unsigned int row;
65         /** Screen column */
66         unsigned int col;
67         /** Edit box widget used for editing setting */
68         struct edit_box editbox;
69         /** Editing active flag */
70         int editing;
71         /** Buffer for setting's value */
72         char value[256]; /* enough size for a DHCP string */
73 };
74
75 /** Registered configuration settings */
76 static struct config_setting
77 config_settings[0] __table_start ( config_settings );
78 static struct config_setting
79 config_settings_end[0] __table_end ( config_settings );
80 #define NUM_SETTINGS ( ( unsigned ) ( config_settings_end - config_settings ) )
81
82 /**
83  * Load setting widget value from configuration context
84  *
85  * @v widget            Setting widget
86  *
87  */
88 static void load_setting ( struct setting_widget *widget ) {
89
90         /* Mark as not editing */
91         widget->editing = 0;
92
93         /* Read current setting value */
94         if ( widget->setting->type->show ( widget->context, widget->setting,
95                                            widget->value,
96                                            sizeof ( widget->value ) ) != 0 ) {
97                 widget->value[0] = '\0';
98         }       
99
100         /* Initialise edit box */
101         init_editbox ( &widget->editbox, widget->value,
102                        sizeof ( widget->value ), NULL, widget->row,
103                        ( widget->col + offsetof ( struct setting_row, value )),
104                        sizeof ( ( ( struct setting_row * ) NULL )->value ) );
105 }
106
107 /**
108  * Save setting widget value back to configuration context
109  *
110  * @v widget            Setting widget
111  */
112 static int save_setting ( struct setting_widget *widget ) {
113         return widget->setting->type->set ( widget->context, widget->setting,
114                                             widget->value );
115 }
116
117 /**
118  * Initialise setting widget
119  *
120  * @v widget            Setting widget
121  * @v context           Configuration context
122  * @v setting           Configuration setting
123  * @v row               Screen row
124  * @v col               Screen column
125  */
126 static void init_setting ( struct setting_widget *widget,
127                            struct config_context *context,
128                            struct config_setting *setting,
129                            unsigned int row, unsigned int col ) {
130
131         /* Initialise widget structure */
132         memset ( widget, 0, sizeof ( *widget ) );
133         widget->context = context;
134         widget->setting = setting;
135         widget->row = row;
136         widget->col = col;
137
138         /* Read current setting value */
139         load_setting ( widget );
140 }
141
142 /**
143  * Draw setting widget
144  *
145  * @v widget            Setting widget
146  */
147 static void draw_setting ( struct setting_widget *widget ) {
148         struct setting_row row;
149         unsigned int len;
150         unsigned int curs_col;
151         char *value;
152
153         /* Fill row with spaces */
154         memset ( &row, ' ', sizeof ( row ) );
155         row.nul = '\0';
156
157         /* Construct dot-padded name */
158         memset ( row.name, '.', sizeof ( row.name ) );
159         len = strlen ( widget->setting->name );
160         if ( len > sizeof ( row.name ) )
161                 len = sizeof ( row.name );
162         memcpy ( row.name, widget->setting->name, len );
163
164         /* Construct space-padded value */
165         value = widget->value;
166         if ( ! *value )
167                 value = "<not specified>";
168         len = strlen ( value );
169         if ( len > sizeof ( row.value ) )
170                 len = sizeof ( row.value );
171         memcpy ( row.value, value, len );
172         curs_col = ( widget->col + offsetof ( typeof ( row ), value )
173                      + len );
174
175         /* Print row */
176         mvprintw ( widget->row, widget->col, "%s", row.start );
177         move ( widget->row, curs_col );
178         if ( widget->editing )
179                 draw_editbox ( &widget->editbox );
180 }
181
182 /**
183  * Edit setting widget
184  *
185  * @v widget            Setting widget
186  * @v key               Key pressed by user
187  * @ret key             Key returned to application, or zero
188  */
189 static int edit_setting ( struct setting_widget *widget, int key ) {
190         widget->editing = 1;
191         return edit_editbox ( &widget->editbox, key );
192 }
193
194 /**
195  * Initialise setting widget by index
196  *
197  * @v widget            Setting widget
198  * @v context           Configuration context
199  * @v index             Index of setting with settings list
200  */
201 static void init_setting_index ( struct setting_widget *widget,
202                                  struct config_context *context,
203                                  unsigned int index ) {
204         init_setting ( widget, context, &config_settings[index],
205                        ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
206 }
207
208 static void alert ( const char *fmt, ... ) {
209         char buf[COLS];
210         va_list args;
211         size_t len;
212
213         va_start ( args, fmt );
214         len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
215         va_end ( args );
216
217         color_set ( CPAIR_ALERT, NULL );
218         mvprintw ( ALERT_ROW, ( ( COLS - len ) / 2 ), "%s", buf );
219         sleep ( 2 );
220         color_set ( CPAIR_NORMAL, NULL );
221         move ( ALERT_ROW, 0 );
222         clrtoeol();
223 }
224
225 static void main_loop ( struct config_context *context ) {
226         struct setting_widget widget;
227         unsigned int current = 0;
228         unsigned int next;
229         int i;
230         int key;
231         int rc;
232
233         /* Print initial screen content */
234         color_set ( CPAIR_NORMAL, NULL );
235         for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
236                 init_setting_index ( &widget, context, i );
237                 draw_setting ( &widget );
238         }
239
240         while ( 1 ) {
241                 /* Redraw current setting */
242                 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
243                             NULL );
244                 draw_setting ( &widget );
245                 color_set ( CPAIR_NORMAL, NULL );
246
247                 key = getchar();
248                 if ( widget.editing ) {
249                         key = edit_setting ( &widget, key );
250                         switch ( key ) {
251                         case 0x0a: /* Enter */
252                                 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
253                                         alert ( " Could not set %s: %s ",
254                                                 widget.setting->name,
255                                                 strerror ( -rc ) );
256                                 }
257                                 /* Fall through */
258                         case 0x03: /* Ctrl-C */
259                                 load_setting ( &widget );
260                                 break;
261                         default:
262                                 /* Do nothing */
263                                 break;
264                         }
265                 } else {
266                         next = current;
267                         switch ( key ) {
268                         case '+':
269                                 if ( next < ( NUM_SETTINGS - 1 ) )
270                                         next++;
271                                 break;
272                         case '-':
273                                 if ( next > 0 )
274                                         next--;
275                                 break;
276                         default:
277                                 edit_setting ( &widget, key );
278                                 break;
279                         }       
280                         if ( next != current ) {
281                                 draw_setting ( &widget );
282                                 init_setting_index ( &widget, context, next );
283                                 current = next;
284                         }
285                 }
286         }
287         
288 }
289
290 void uitest ( void ) {
291         struct config_context dummy_context;
292
293         dummy_context.options = ugly_nvo_hack->options;
294
295         initscr();
296         start_color();
297         init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
298         init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE );
299         init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
300         init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
301         color_set ( CPAIR_NORMAL, NULL );
302         erase();
303         
304         main_loop ( &dummy_context );
305
306         endwin();
307 }