7da9591077e809281206ea96359150915b57be5d
[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 <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 ( show_setting ( widget->context, widget->setting,
95                             widget->value, sizeof ( widget->value ) ) != 0 ) {
96                 widget->value[0] = '\0';
97         }       
98
99         /* Initialise edit box */
100         init_editbox ( &widget->editbox, widget->value,
101                        sizeof ( widget->value ), NULL, widget->row,
102                        ( widget->col + offsetof ( struct setting_row, value )),
103                        sizeof ( ( ( struct setting_row * ) NULL )->value ) );
104 }
105
106 /**
107  * Save setting widget value back to configuration context
108  *
109  * @v widget            Setting widget
110  */
111 static int save_setting ( struct setting_widget *widget ) {
112         return set_setting ( widget->context, widget->setting, widget->value );
113 }
114
115 /**
116  * Initialise setting widget
117  *
118  * @v widget            Setting widget
119  * @v context           Configuration context
120  * @v setting           Configuration setting
121  * @v row               Screen row
122  * @v col               Screen column
123  */
124 static void init_setting ( struct setting_widget *widget,
125                            struct config_context *context,
126                            struct config_setting *setting,
127                            unsigned int row, unsigned int col ) {
128
129         /* Initialise widget structure */
130         memset ( widget, 0, sizeof ( *widget ) );
131         widget->context = context;
132         widget->setting = setting;
133         widget->row = row;
134         widget->col = col;
135
136         /* Read current setting value */
137         load_setting ( widget );
138 }
139
140 /**
141  * Draw setting widget
142  *
143  * @v widget            Setting widget
144  */
145 static void draw_setting ( struct setting_widget *widget ) {
146         struct setting_row row;
147         unsigned int len;
148         unsigned int curs_col;
149         char *value;
150
151         /* Fill row with spaces */
152         memset ( &row, ' ', sizeof ( row ) );
153         row.nul = '\0';
154
155         /* Construct dot-padded name */
156         memset ( row.name, '.', sizeof ( row.name ) );
157         len = strlen ( widget->setting->name );
158         if ( len > sizeof ( row.name ) )
159                 len = sizeof ( row.name );
160         memcpy ( row.name, widget->setting->name, len );
161
162         /* Construct space-padded value */
163         value = widget->value;
164         if ( ! *value )
165                 value = "<not specified>";
166         len = strlen ( value );
167         if ( len > sizeof ( row.value ) )
168                 len = sizeof ( row.value );
169         memcpy ( row.value, value, len );
170         curs_col = ( widget->col + offsetof ( typeof ( row ), value )
171                      + len );
172
173         /* Print row */
174         mvprintw ( widget->row, widget->col, "%s", row.start );
175         move ( widget->row, curs_col );
176         if ( widget->editing )
177                 draw_editbox ( &widget->editbox );
178 }
179
180 /**
181  * Edit setting widget
182  *
183  * @v widget            Setting widget
184  * @v key               Key pressed by user
185  * @ret key             Key returned to application, or zero
186  */
187 static int edit_setting ( struct setting_widget *widget, int key ) {
188         widget->editing = 1;
189         return edit_editbox ( &widget->editbox, key );
190 }
191
192 /**
193  * Initialise setting widget by index
194  *
195  * @v widget            Setting widget
196  * @v context           Configuration context
197  * @v index             Index of setting with settings list
198  */
199 static void init_setting_index ( struct setting_widget *widget,
200                                  struct config_context *context,
201                                  unsigned int index ) {
202         init_setting ( widget, context, &config_settings[index],
203                        ( SETTINGS_LIST_ROW + index ), SETTINGS_LIST_COL );
204 }
205
206 static void alert ( const char *fmt, ... ) {
207         char buf[COLS];
208         va_list args;
209         size_t len;
210
211         va_start ( args, fmt );
212         len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
213         va_end ( args );
214
215         color_set ( CPAIR_ALERT, NULL );
216         mvprintw ( ALERT_ROW, ( ( COLS - len ) / 2 ), "%s", buf );
217         sleep ( 2 );
218         color_set ( CPAIR_NORMAL, NULL );
219         move ( ALERT_ROW, 0 );
220         clrtoeol();
221 }
222
223 static void main_loop ( struct config_context *context ) {
224         struct setting_widget widget;
225         unsigned int current = 0;
226         unsigned int next;
227         int i;
228         int key;
229         int rc;
230
231         /* Print initial screen content */
232         color_set ( CPAIR_NORMAL, NULL );
233         for ( i = ( NUM_SETTINGS - 1 ) ; i >= 0 ; i-- ) {
234                 init_setting_index ( &widget, context, i );
235                 draw_setting ( &widget );
236         }
237
238         while ( 1 ) {
239                 /* Redraw current setting */
240                 color_set ( ( widget.editing ? CPAIR_EDIT : CPAIR_SELECT ),
241                             NULL );
242                 draw_setting ( &widget );
243                 color_set ( CPAIR_NORMAL, NULL );
244
245                 key = getchar();
246                 if ( widget.editing ) {
247                         key = edit_setting ( &widget, key );
248                         switch ( key ) {
249                         case 0x0a: /* Enter */
250                                 if ( ( rc = save_setting ( &widget ) ) != 0 ) {
251                                         alert ( " Could not set %s: %s ",
252                                                 widget.setting->name,
253                                                 strerror ( rc ) );
254                                 }
255                                 /* Fall through */
256                         case 0x03: /* Ctrl-C */
257                                 load_setting ( &widget );
258                                 break;
259                         default:
260                                 /* Do nothing */
261                                 break;
262                         }
263                 } else {
264                         next = current;
265                         switch ( key ) {
266                         case '+':
267                                 if ( next < ( NUM_SETTINGS - 1 ) )
268                                         next++;
269                                 break;
270                         case '-':
271                                 if ( next > 0 )
272                                         next--;
273                                 break;
274                         default:
275                                 edit_setting ( &widget, key );
276                                 break;
277                         }       
278                         if ( next != current ) {
279                                 draw_setting ( &widget );
280                                 init_setting_index ( &widget, context, next );
281                                 current = next;
282                         }
283                 }
284         }
285         
286 }
287
288 void uitest ( void ) {
289         struct config_context dummy_context;
290
291         dummy_context.options = ugly_nvo_hack->options;
292
293         initscr();
294         start_color();
295         init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
296         init_pair ( CPAIR_SELECT, COLOR_BLACK, COLOR_WHITE );
297         init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
298         init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
299         color_set ( CPAIR_NORMAL, NULL );
300         erase();
301         
302         main_loop ( &dummy_context );
303
304         endwin();
305 }