• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 FILE_LICENCE ( GPL2_OR_LATER );
20 
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <curses.h>
26 #include <console.h>
27 #include <gpxe/settings.h>
28 #include <gpxe/editbox.h>
29 #include <gpxe/keys.h>
30 #include <gpxe/settings_ui.h>
31 
32 /** @file
33  *
34  * Option configuration console
35  *
36  */
37 
38 /* Colour pairs */
39 #define CPAIR_NORMAL	1
40 #define CPAIR_SELECT	2
41 #define CPAIR_EDIT	3
42 #define CPAIR_ALERT	4
43 
44 /* Screen layout */
45 #define TITLE_ROW		1
46 #define SETTINGS_LIST_ROW	3
47 #define SETTINGS_LIST_COL	1
48 #define INFO_ROW		20
49 #define ALERT_ROW		20
50 #define INSTRUCTION_ROW		22
51 #define INSTRUCTION_PAD "     "
52 
53 /** Layout of text within a setting widget */
54 struct setting_row {
55 	char start[0];
56 	char pad1[1];
57 	char name[15];
58 	char pad2[1];
59 	char value[60];
60 	char pad3[1];
61 	char nul;
62 } __attribute__ (( packed ));
63 
64 /** A setting widget */
65 struct setting_widget {
66 	/** Settings block */
67 	struct settings *settings;
68 	/** Configuration setting */
69 	struct setting *setting;
70 	/** Screen row */
71 	unsigned int row;
72 	/** Screen column */
73 	unsigned int col;
74 	/** Edit box widget used for editing setting */
75 	struct edit_box editbox;
76 	/** Editing in progress flag */
77 	int editing;
78 	/** Buffer for setting's value */
79 	char value[256]; /* enough size for a DHCP string */
80 };
81 
82 /** Number of registered configuration settings */
83 #define NUM_SETTINGS table_num_entries ( SETTINGS )
84 
85 static void load_setting ( struct setting_widget *widget ) __nonnull;
86 static int save_setting ( struct setting_widget *widget ) __nonnull;
87 static void init_setting ( struct setting_widget *widget,
88                            struct settings *settings,
89                            struct setting *setting,
90                            unsigned int row, unsigned int col ) __nonnull;
91 static void draw_setting ( struct setting_widget *widget ) __nonnull;
92 static int edit_setting ( struct setting_widget *widget, int key ) __nonnull;
93 static void init_setting_index ( struct setting_widget *widget,
94                                  struct settings *settings,
95                                  unsigned int index ) __nonnull;
96 static void vmsg ( unsigned int row, const char *fmt, va_list args ) __nonnull;
97 static void msg ( unsigned int row, const char *fmt, ... ) __nonnull;
98 static void valert ( const char *fmt, va_list args ) __nonnull;
99 static void alert ( const char *fmt, ... ) __nonnull;
100 static void draw_info_row ( struct setting *setting ) __nonnull;
101 static int main_loop ( struct settings *settings ) __nonnull;
102 
103 /**
104  * Load setting widget value from configuration settings
105  *
106  * @v widget		Setting widget
107  *
108  */
load_setting(struct setting_widget * widget)109 static void load_setting ( struct setting_widget *widget ) {
110 
111 	/* Mark as not editing */
112 	widget->editing = 0;
113 
114 	/* Read current setting value */
115 	if ( fetchf_setting ( widget->settings, widget->setting,
116 			      widget->value, sizeof ( widget->value ) ) < 0 ) {
117 		widget->value[0] = '\0';
118 	}
119 
120 	/* Initialise edit box */
121 	init_editbox ( &widget->editbox, widget->value,
122 		       sizeof ( widget->value ), NULL, widget->row,
123 		       ( widget->col + offsetof ( struct setting_row, value )),
124 		       sizeof ( ( ( struct setting_row * ) NULL )->value ), 0);
125 }
126 
127 /**
128  * Save setting widget value back to configuration settings
129  *
130  * @v widget		Setting widget
131  */
save_setting(struct setting_widget * widget)132 static int save_setting ( struct setting_widget *widget ) {
133 	return storef_setting ( widget->settings, widget->setting,
134 				widget->value );
135 }
136 
137 /**
138  * Initialise setting widget
139  *
140  * @v widget		Setting widget
141  * @v settings		Settings block
142  * @v setting		Configuration setting
143  * @v row		Screen row
144  * @v col		Screen column
145  */
init_setting(struct setting_widget * widget,struct settings * settings,struct setting * setting,unsigned int row,unsigned int col)146 static void init_setting ( struct setting_widget *widget,
147 			   struct settings *settings,
148 			   struct setting *setting,
149 			   unsigned int row, unsigned int col ) {
150 
151 	/* Initialise widget structure */
152 	memset ( widget, 0, sizeof ( *widget ) );
153 	widget->settings = settings;
154 	widget->setting = setting;
155 	widget->row = row;
156 	widget->col = col;
157 
158 	/* Read current setting value */
159 	load_setting ( widget );
160 }
161 
162 /**
163  * Draw setting widget
164  *
165  * @v widget		Setting widget
166  */
draw_setting(struct setting_widget * widget)167 static void draw_setting ( struct setting_widget *widget ) {
168 	struct setting_row row;
169 	unsigned int len;
170 	unsigned int curs_col;
171 	char *value;
172 
173 	/* Fill row with spaces */
174 	memset ( &row, ' ', sizeof ( row ) );
175 	row.nul = '\0';
176 
177 	/* Construct dot-padded name */
178 	memset ( row.name, '.', sizeof ( row.name ) );
179 	len = strlen ( widget->setting->name );
180 	if ( len > sizeof ( row.name ) )
181 		len = sizeof ( row.name );
182 	memcpy ( row.name, widget->setting->name, len );
183 
184 	/* Construct space-padded value */
185 	value = widget->value;
186 	if ( ! *value )
187 		value = "<not specified>";
188 	len = strlen ( value );
189 	if ( len > sizeof ( row.value ) )
190 		len = sizeof ( row.value );
191 	memcpy ( row.value, value, len );
192 	curs_col = ( widget->col + offsetof ( typeof ( row ), value )
193 		     + len );
194 
195 	/* Print row */
196 	mvprintw ( widget->row, widget->col, "%s", row.start );
197 	move ( widget->row, curs_col );
198 	if ( widget->editing )
199 		draw_editbox ( &widget->editbox );
200 }
201 
202 /**
203  * Edit setting widget
204  *
205  * @v widget		Setting widget
206  * @v key		Key pressed by user
207  * @ret key		Key returned to application, or zero
208  */
edit_setting(struct setting_widget * widget,int key)209 static int edit_setting ( struct setting_widget *widget, int key ) {
210 	widget->editing = 1;
211 	return edit_editbox ( &widget->editbox, key );
212 }
213 
214 /**
215  * Initialise setting widget by index
216  *
217  * @v widget		Setting widget
218  * @v settings		Settings block
219  * @v index		Index of setting with settings list
220  */
init_setting_index(struct setting_widget * widget,struct settings * settings,unsigned int index)221 static void init_setting_index ( struct setting_widget *widget,
222 				 struct settings *settings,
223 				 unsigned int index ) {
224 	struct setting *all_settings = table_start ( SETTINGS );
225 
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  */
vmsg(unsigned int row,const char * fmt,va_list args)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  */
msg(unsigned int row,const char * fmt,...)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  */
clearmsg(unsigned int row)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  */
valert(const char * fmt,va_list args)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  */
alert(const char * fmt,...)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  */
draw_title_row(void)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  */
draw_info_row(struct setting * setting)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  */
draw_instruction_row(int editing)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 
main_loop(struct settings * settings)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 
settings_ui(struct settings * settings)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 }
428