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