|
|
1.1 root 1: /*
2: * Copyright (C) 2006 Michael Brown <[email protected]>.
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 <ipxe/console.h>
27: #include <ipxe/settings.h>
28: #include <ipxe/editbox.h>
29: #include <ipxe/keys.h>
30: #include <ipxe/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: #define CPAIR_URL 5
44:
45: /* Screen layout */
46: #define TITLE_ROW 1
47: #define SETTINGS_LIST_ROW 3
48: #define SETTINGS_LIST_COL 1
49: #define SETTINGS_LIST_ROWS 15
50: #define INFO_ROW 19
51: #define ALERT_ROW 22
52: #define INSTRUCTION_ROW 22
53: #define INSTRUCTION_PAD " "
54:
55: /** Layout of text within a setting widget */
56: struct setting_row_text {
57: char start[0];
58: char pad1[1];
59: char name[15];
60: char pad2[1];
61: char value[60];
62: char pad3[1];
63: char nul;
64: } __attribute__ (( packed ));
65:
66: /** A setting row widget */
67: struct setting_row_widget {
68: /** Target configuration settings block
69: *
70: * Valid only for rows that lead to new settings blocks.
71: */
72: struct settings *settings;
73: /** Configuration setting
74: *
75: * Valid only for rows that represent individual settings.
76: */
77: struct setting *setting;
78: /** Screen row */
79: unsigned int row;
80: /** Screen column */
81: unsigned int col;
82: /** Edit box widget used for editing setting */
83: struct edit_box editbox;
84: /** Editing in progress flag */
85: int editing;
86: /** Setting originates from this block flag */
87: int originates_here;
88: /** Buffer for setting's value */
89: char value[256]; /* enough size for a DHCP string */
90: };
91:
92: /** A settings widget */
93: struct setting_widget {
94: /** Settings block */
95: struct settings *settings;
96: /** Number of rows */
97: unsigned int num_rows;
98: /** Current row index */
99: unsigned int current;
100: /** Index of the first visible row, for scrolling. */
101: unsigned int first_visible;
102: /** Active row */
103: struct setting_row_widget row;
104: };
105:
106: /**
107: * Select a setting row
108: *
109: * @v widget Setting widget
110: * @v index Index of setting row
111: * @ret count Number of settings rows
112: */
113: static unsigned int select_setting_row ( struct setting_widget *widget,
114: unsigned int index ) {
115: struct settings *settings;
116: struct settings *origin;
117: struct setting *setting;
118: unsigned int count = 0;
119:
120: /* Initialise structure */
121: memset ( &widget->row, 0, sizeof ( widget->row ) );
122: widget->current = index;
123: widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible );
124: widget->row.col = SETTINGS_LIST_COL;
125:
126: /* Include parent settings block, if applicable */
127: if ( widget->settings->parent && ( count++ == index ) ) {
128: widget->row.settings = widget->settings->parent;
129: snprintf ( widget->row.value, sizeof ( widget->row.value ),
130: "../" );
131: }
132:
133: /* Include any child settings blocks, if applicable */
134: list_for_each_entry ( settings, &widget->settings->children, siblings ){
135: if ( count++ == index ) {
136: widget->row.settings = settings;
137: snprintf ( widget->row.value,
138: sizeof ( widget->row.value ), "%s/",
139: settings->name );
140: }
141: }
142:
143: /* Include any applicable settings */
144: for_each_table_entry ( setting, SETTINGS ) {
145: if ( ! setting_applies ( widget->settings, setting ) )
146: continue;
147: if ( count++ == index ) {
148: widget->row.setting = setting;
149:
150: /* Read current setting value */
151: fetchf_setting ( widget->settings, widget->row.setting,
152: widget->row.value,
153: sizeof ( widget->row.value ) );
154:
155: /* Check setting's origin */
156: origin = fetch_setting_origin ( widget->settings,
157: widget->row.setting );
158: widget->row.originates_here =
159: ( origin == widget->settings );
160: }
161: }
162:
163: /* Initialise edit box */
164: init_editbox ( &widget->row.editbox, widget->row.value,
165: sizeof ( widget->row.value ), NULL, widget->row.row,
166: ( widget->row.col +
167: offsetof ( struct setting_row_text, value ) ),
168: sizeof ( ( ( struct setting_row_text * ) NULL )->value ),
169: 0 );
170:
171: return count;
172: }
173:
174: static size_t string_copy ( char *dest, const char *src, size_t len ) {
175: size_t src_len;
176:
177: src_len = strlen ( src );
178: if ( len > src_len )
179: len = src_len;
180: memcpy ( dest, src, len );
181: return len;
182: }
183:
184: /**
185: * Draw setting row
186: *
187: * @v widget Setting widget
188: */
189: static void draw_setting_row ( struct setting_widget *widget ) {
190: struct setting_row_text text;
191: unsigned int curs_offset;
192: char *value;
193:
194: /* Fill row with spaces */
195: memset ( &text, ' ', sizeof ( text ) );
196: text.nul = '\0';
197:
198: /* Construct row content */
199: if ( widget->row.settings ) {
200:
201: /* Construct space-padded name */
202: curs_offset = ( offsetof ( typeof ( text ), name ) +
203: string_copy ( text.name, widget->row.value,
204: sizeof ( text.name ) ) );
205:
206: } else {
207:
208: /* Construct dot-padded name */
209: memset ( text.name, '.', sizeof ( text.name ) );
210: string_copy ( text.name, widget->row.setting->name,
211: sizeof ( text.name ) );
212:
213: /* Construct space-padded value */
214: value = widget->row.value;
215: if ( ! *value )
216: value = "<not specified>";
217: curs_offset = ( offsetof ( typeof ( text ), value ) +
218: string_copy ( text.value, value,
219: sizeof ( text.value ) ) );
220: }
221:
222: /* Print row */
223: if ( widget->row.originates_here || widget->row.settings )
224: attron ( A_BOLD );
225: mvprintw ( widget->row.row, widget->row.col, "%s", text.start );
226: attroff ( A_BOLD );
227: move ( widget->row.row, widget->row.col + curs_offset );
228: }
229:
230: /**
231: * Edit setting widget
232: *
233: * @v widget Setting widget
234: * @v key Key pressed by user
235: * @ret key Key returned to application, or zero
236: */
237: static int edit_setting ( struct setting_widget *widget, int key ) {
238: assert ( widget->row.setting != NULL );
239: widget->row.editing = 1;
240: return edit_editbox ( &widget->row.editbox, key );
241: }
242:
243: /**
244: * Save setting widget value back to configuration settings
245: *
246: * @v widget Setting widget
247: */
248: static int save_setting ( struct setting_widget *widget ) {
249: assert ( widget->row.setting != NULL );
250: return storef_setting ( widget->settings, widget->row.setting,
251: widget->row.value );
252: }
253:
254: /**
255: * Print message centred on specified row
256: *
257: * @v row Row
258: * @v fmt printf() format string
259: * @v args printf() argument list
260: */
261: static void vmsg ( unsigned int row, const char *fmt, va_list args ) {
262: char buf[COLS];
263: size_t len;
264:
265: len = vsnprintf ( buf, sizeof ( buf ), fmt, args );
266: mvprintw ( row, ( ( COLS - len ) / 2 ), "%s", buf );
267: }
268:
269: /**
270: * Print message centred on specified row
271: *
272: * @v row Row
273: * @v fmt printf() format string
274: * @v .. printf() arguments
275: */
276: static void msg ( unsigned int row, const char *fmt, ... ) {
277: va_list args;
278:
279: va_start ( args, fmt );
280: vmsg ( row, fmt, args );
281: va_end ( args );
282: }
283:
284: /**
285: * Clear message on specified row
286: *
287: * @v row Row
288: */
289: static void clearmsg ( unsigned int row ) {
290: move ( row, 0 );
291: clrtoeol();
292: }
293:
294: /**
295: * Print alert message
296: *
297: * @v fmt printf() format string
298: * @v args printf() argument list
299: */
300: static void valert ( const char *fmt, va_list args ) {
301: clearmsg ( ALERT_ROW );
302: color_set ( CPAIR_ALERT, NULL );
303: vmsg ( ALERT_ROW, fmt, args );
304: sleep ( 2 );
305: color_set ( CPAIR_NORMAL, NULL );
306: clearmsg ( ALERT_ROW );
307: }
308:
309: /**
310: * Print alert message
311: *
312: * @v fmt printf() format string
313: * @v ... printf() arguments
314: */
315: static void alert ( const char *fmt, ... ) {
316: va_list args;
317:
318: va_start ( args, fmt );
319: valert ( fmt, args );
320: va_end ( args );
321: }
322:
323: /**
324: * Draw title row
325: *
326: * @v widget Setting widget
327: */
328: static void draw_title_row ( struct setting_widget *widget ) {
329: const char *name;
330:
331: clearmsg ( TITLE_ROW );
332: name = settings_name ( widget->settings );
333: attron ( A_BOLD );
334: msg ( TITLE_ROW, "iPXE configuration settings%s%s",
335: ( name[0] ? " - " : "" ), name );
336: attroff ( A_BOLD );
337: }
338:
339: /**
340: * Draw information row
341: *
342: * @v widget Setting widget
343: */
344: static void draw_info_row ( struct setting_widget *widget ) {
345: struct settings *origin;
346: char buf[32];
347:
348: /* Draw nothing unless this row represents a setting */
349: clearmsg ( INFO_ROW );
350: clearmsg ( INFO_ROW + 1 );
351: if ( ! widget->row.setting )
352: return;
353:
354: /* Determine a suitable setting name */
355: origin = fetch_setting_origin ( widget->settings, widget->row.setting );
356: if ( ! origin )
357: origin = widget->settings;
358: setting_name ( origin, widget->row.setting, buf, sizeof ( buf ) );
359:
360: /* Draw row */
361: attron ( A_BOLD );
362: msg ( INFO_ROW, "%s - %s", buf, widget->row.setting->description );
363: attroff ( A_BOLD );
364: color_set ( CPAIR_URL, NULL );
365: msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s",
366: widget->row.setting->name );
367: color_set ( CPAIR_NORMAL, NULL );
368: }
369:
370: /**
371: * Draw instruction row
372: *
373: * @v widget Setting widget
374: */
375: static void draw_instruction_row ( struct setting_widget *widget ) {
376:
377: clearmsg ( INSTRUCTION_ROW );
378: if ( widget->row.editing ) {
379: msg ( INSTRUCTION_ROW,
380: "Enter - accept changes" INSTRUCTION_PAD
381: "Ctrl-C - discard changes" );
382: } else {
383: msg ( INSTRUCTION_ROW,
384: "%sCtrl-X - exit configuration utility",
385: ( widget->row.originates_here ?
386: "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
387: }
388: }
389:
390: /**
391: * Reveal setting row
392: *
393: * @v widget Setting widget
394: * @v index Index of setting row
395: */
396: static void reveal_setting_row ( struct setting_widget *widget,
397: unsigned int index ) {
398: unsigned int i;
399:
400: /* Simply return if setting N is already on-screen. */
401: if ( index - widget->first_visible < SETTINGS_LIST_ROWS )
402: return;
403:
404: /* Jump scroll to make the specified setting row visible. */
405: while ( widget->first_visible < index )
406: widget->first_visible += SETTINGS_LIST_ROWS;
407: while ( widget->first_visible > index )
408: widget->first_visible -= SETTINGS_LIST_ROWS;
409:
410: /* Draw ellipses before and/or after the settings list to
411: * represent any invisible settings.
412: */
413: mvaddstr ( SETTINGS_LIST_ROW - 1,
414: SETTINGS_LIST_COL + 1,
415: widget->first_visible > 0 ? "..." : " " );
416: mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS,
417: SETTINGS_LIST_COL + 1,
418: ( ( widget->first_visible + SETTINGS_LIST_ROWS )
419: < widget->num_rows ? "..." : " " ) );
420:
421: /* Draw visible settings. */
422: for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) {
423: if ( ( widget->first_visible + i ) < widget->num_rows ) {
424: select_setting_row ( widget,
425: widget->first_visible + i );
426: draw_setting_row ( widget );
427: } else {
428: clearmsg ( SETTINGS_LIST_ROW + i );
429: }
430: }
431: }
432:
433: /**
434: * Reveal setting row
435: *
436: * @v widget Setting widget
437: * @v settings Settings block
438: */
439: static void init_widget ( struct setting_widget *widget,
440: struct settings *settings ) {
441:
442: widget->settings = settings;
443: widget->num_rows = select_setting_row ( widget, 0 );
444: widget->first_visible = SETTINGS_LIST_ROWS;
445: draw_title_row ( widget );
446: reveal_setting_row ( widget, 0 );
447: select_setting_row ( widget, 0 );
448: }
449:
450: static int main_loop ( struct settings *settings ) {
451: struct setting_widget widget;
452: int redraw = 1;
453: int move;
454: unsigned int next;
455: int key;
456: int rc;
457:
458: /* Print initial screen content */
459: color_set ( CPAIR_NORMAL, NULL );
460: memset ( &widget, 0, sizeof ( widget ) );
461: init_widget ( &widget, settings );
462:
463: while ( 1 ) {
464:
465: /* Redraw rows if necessary */
466: if ( redraw ) {
467: draw_info_row ( &widget );
468: draw_instruction_row ( &widget );
469: color_set ( ( widget.row.editing ?
470: CPAIR_EDIT : CPAIR_SELECT ), NULL );
471: draw_setting_row ( &widget );
472: color_set ( CPAIR_NORMAL, NULL );
473: redraw = 0;
474: }
475:
476: if ( widget.row.editing ) {
477:
478: /* Sanity check */
479: assert ( widget.row.setting != NULL );
480:
481: /* Redraw edit box */
482: color_set ( CPAIR_EDIT, NULL );
483: draw_editbox ( &widget.row.editbox );
484: color_set ( CPAIR_NORMAL, NULL );
485:
486: /* Process keypress */
487: key = edit_setting ( &widget, getkey ( 0 ) );
488: switch ( key ) {
489: case CR:
490: case LF:
491: if ( ( rc = save_setting ( &widget ) ) != 0 )
492: alert ( " %s ", strerror ( rc ) );
493: /* Fall through */
494: case CTRL_C:
495: select_setting_row ( &widget, widget.current );
496: redraw = 1;
497: break;
498: default:
499: /* Do nothing */
500: break;
501: }
502:
503: } else {
504:
505: /* Process keypress */
506: key = getkey ( 0 );
507: move = 0;
508: switch ( key ) {
509: case KEY_DOWN:
510: if ( widget.current < ( widget.num_rows - 1 ) )
511: move = +1;
512: break;
513: case KEY_UP:
514: if ( widget.current > 0 )
515: move = -1;
516: break;
517: case CTRL_D:
518: if ( ! widget.row.setting )
519: break;
520: if ( ( rc = delete_setting ( widget.settings,
521: widget.row.setting ) ) != 0 ) {
522: alert ( " %s ", strerror ( rc ) );
523: }
524: select_setting_row ( &widget, widget.current );
525: redraw = 1;
526: break;
527: case CTRL_X:
528: return 0;
529: case CR:
530: case LF:
531: if ( widget.row.settings ) {
532: init_widget ( &widget,
533: widget.row.settings );
534: redraw = 1;
535: }
536: /* Fall through */
537: default:
538: if ( widget.row.setting ) {
539: edit_setting ( &widget, key );
540: redraw = 1;
541: }
542: break;
543: }
544: if ( move ) {
545: next = ( widget.current + move );
546: draw_setting_row ( &widget );
547: redraw = 1;
548: reveal_setting_row ( &widget, next );
549: select_setting_row ( &widget, next );
550: }
551: }
552: }
553: }
554:
555: int settings_ui ( struct settings *settings ) {
556: int rc;
557:
558: initscr();
559: start_color();
560: init_pair ( CPAIR_NORMAL, COLOR_WHITE, COLOR_BLUE );
561: init_pair ( CPAIR_SELECT, COLOR_WHITE, COLOR_RED );
562: init_pair ( CPAIR_EDIT, COLOR_BLACK, COLOR_CYAN );
563: init_pair ( CPAIR_ALERT, COLOR_WHITE, COLOR_RED );
564: init_pair ( CPAIR_URL, COLOR_CYAN, COLOR_BLUE );
565: color_set ( CPAIR_NORMAL, NULL );
566: erase();
567:
568: rc = main_loop ( settings );
569:
570: endwin();
571:
572: return rc;
573: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.