iPXE
settings_ui.c
Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 *
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
22 */
23
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stdio.h>
28#include <stdarg.h>
29#include <stdlib.h>
30#include <unistd.h>
31#include <string.h>
32#include <curses.h>
33#include <ipxe/console.h>
34#include <ipxe/settings.h>
35#include <ipxe/editbox.h>
36#include <ipxe/keys.h>
37#include <ipxe/ansicol.h>
38#include <ipxe/jumpscroll.h>
39#include <ipxe/message.h>
40#include <ipxe/settings_ui.h>
41#include <config/branding.h>
42
43/** @file
44 *
45 * Option configuration console
46 *
47 */
48
49/* Screen layout */
50#define TITLE_ROW 1U
51#define SETTINGS_LIST_ROW 3U
52#define SETTINGS_LIST_COL 1U
53#define SETTINGS_LIST_ROWS ( LINES - 6U - SETTINGS_LIST_ROW )
54#define INFO_ROW ( LINES - 5U )
55#define ALERT_ROW ( LINES - 2U )
56#define INSTRUCTION_ROW ( LINES - 2U )
57#define INSTRUCTION_PAD " "
58
59/** Layout of text within a setting row */
60#define SETTING_ROW_TEXT( cols ) struct { \
61 char start[0]; \
62 char pad1[1]; \
63 union { \
64 struct { \
65 char name[ cols - 1 - 1 - 1 - 1 - 1 ]; \
66 char pad2[1]; \
67 } __attribute__ (( packed )) settings; \
68 struct { \
69 char name[15]; \
70 char pad2[1]; \
71 char value[ cols - 1 - 15 - 1 - 1 - 1 - 1 ]; \
72 } __attribute__ (( packed )) setting; \
73 } u; \
74 char pad3[1]; \
75 char nul; \
76} __attribute__ (( packed ))
77
78/** A settings user interface row */
80 /** Target configuration settings block
81 *
82 * Valid only for rows that lead to new settings blocks.
83 */
85 /** Configuration setting origin
86 *
87 * Valid only for rows that represent individual settings.
88 */
90 /** Configuration setting
91 *
92 * Valid only for rows that represent individual settings.
93 */
95 /** Screen row */
96 unsigned int row;
97 /** Edit box widget used for editing setting */
99 /** Editing in progress flag */
101 /** Dynamically allocated buffer for setting's value */
102 char *buf;
103};
104
105/** A settings user interface */
107 /** Settings block */
109 /** Jump scroller */
111 /** Current row */
113};
114
115/**
116 * Select a setting
117 *
118 * @v ui Settings user interface
119 * @v index Index of setting row
120 * @ret count Number of setting rows
121 */
122static unsigned int select_setting_row ( struct settings_ui *ui,
123 unsigned int index ) {
124 SETTING_ROW_TEXT ( COLS ) *text;
125 struct settings *settings;
126 struct setting *setting;
127 struct setting *previous = NULL;
128 unsigned int count = 0;
129
130 /* Free any previous setting value */
131 free ( ui->row.buf );
132 ui->row.buf = NULL;
133
134 /* Initialise structure */
135 memset ( &ui->row, 0, sizeof ( ui->row ) );
136 ui->row.row = ( SETTINGS_LIST_ROW + index - ui->scroll.first );
137
138 /* Include parent settings block, if applicable */
139 if ( ui->settings->parent && ( count++ == index ) )
140 ui->row.settings = ui->settings->parent;
141
142 /* Include any child settings blocks, if applicable */
143 list_for_each_entry ( settings, &ui->settings->children, siblings ) {
144 if ( count++ == index )
145 ui->row.settings = settings;
146 }
147
148 /* Include any applicable settings */
150
151 /* Skip inapplicable settings */
152 if ( ! setting_applies ( ui->settings, setting ) )
153 continue;
154
155 /* Skip duplicate settings */
156 if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
157 continue;
158 previous = setting;
159
160 /* Read current setting value and origin */
161 if ( count++ == index ) {
163 &ui->row.origin,
164 &ui->row.setting, &ui->row.buf );
165 }
166 }
167
168 /* Initialise edit box */
169 memset ( &ui->row.editbox, 0, sizeof ( ui->row.editbox ) );
170 init_editbox ( &ui->row.editbox, ui->row.row,
172 offsetof ( typeof ( *text ), u.setting.value ) ),
173 sizeof ( text->u.setting.value ), 0, &ui->row.buf );
174
175 return count;
176}
177
178/**
179 * Copy string without NUL termination
180 *
181 * @v dest Destination
182 * @v src Source
183 * @v len Maximum length of destination
184 * @ret len Length of (unterminated) string
185 */
186static size_t string_copy ( char *dest, const char *src, size_t len ) {
187 size_t src_len;
188
189 src_len = strlen ( src );
190 if ( len > src_len )
191 len = src_len;
192 memcpy ( dest, src, len );
193 return len;
194}
195
196/**
197 * Draw setting row
198 *
199 * @v ui Settings UI
200 */
201static void draw_setting_row ( struct settings_ui *ui ) {
202 SETTING_ROW_TEXT ( COLS ) text;
203 unsigned int curs_offset;
204 const char *value;
205
206 /* Fill row with spaces */
207 memset ( &text, ' ', sizeof ( text ) );
208 text.nul = '\0';
209
210 /* Construct row content */
211 if ( ui->row.settings ) {
212
213 /* Construct space-padded name */
214 value = ( ( ui->row.settings == ui->settings->parent ) ?
215 ".." : ui->row.settings->name );
216 curs_offset = string_copy ( text.u.settings.name, value,
217 sizeof ( text.u.settings.name ) );
218 text.u.settings.name[curs_offset] = '/';
219 curs_offset += offsetof ( typeof ( text ), u.settings );
220
221 } else {
222
223 /* Construct dot-padded name */
224 memset ( text.u.setting.name, '.',
225 sizeof ( text.u.setting.name ) );
226 string_copy ( text.u.setting.name, ui->row.setting.name,
227 sizeof ( text.u.setting.name ) );
228
229 /* Construct space-padded value */
230 value = ui->row.buf;
231 if ( ! ( value && value[0] ) )
232 value = "<not specified>";
233 curs_offset = string_copy ( text.u.setting.value, value,
234 sizeof ( text.u.setting.value ) );
235 curs_offset += offsetof ( typeof ( text ), u.setting.value );
236 }
237
238 /* Print row */
239 if ( ( ui->row.origin == ui->settings ) || ( ui->row.settings != NULL ))
240 attron ( A_BOLD );
241 mvprintw ( ui->row.row, SETTINGS_LIST_COL, "%s", text.start );
242 attroff ( A_BOLD );
243 move ( ui->row.row, ( SETTINGS_LIST_COL + curs_offset ) );
244}
245
246/**
247 * Edit setting ui
248 *
249 * @v ui Settings UI
250 * @v key Key pressed by user
251 * @ret key Key returned to application, or zero
252 */
253static int edit_setting ( struct settings_ui *ui, int key ) {
254 assert ( ui->row.setting.name != NULL );
255 ui->row.editing = 1;
256 return edit_widget ( &ui->row.editbox.widget, key );
257}
258
259/**
260 * Save setting ui value back to configuration settings
261 *
262 * @v ui Settings UI
263 */
264static int save_setting ( struct settings_ui *ui ) {
265 assert ( ui->row.setting.name != NULL );
266 return storef_setting ( ui->settings, &ui->row.setting, ui->row.buf );
267}
268
269/**
270 * Draw title row
271 *
272 * @v ui Settings UI
273 */
274static void draw_title_row ( struct settings_ui *ui ) {
275 const char *name;
276
278 name = settings_name ( ui->settings );
279 attron ( A_BOLD );
280 msg ( TITLE_ROW, PRODUCT_SHORT_NAME " configuration settings%s%s",
281 ( name[0] ? " - " : "" ), name );
282 attroff ( A_BOLD );
283}
284
285/**
286 * Draw information row
287 *
288 * @v ui Settings UI
289 */
290static void draw_info_row ( struct settings_ui *ui ) {
291 char buf[32];
292
293 /* Draw nothing unless this row represents a setting */
294 clearmsg ( INFO_ROW );
295 clearmsg ( INFO_ROW + 1 );
296 if ( ! ui->row.setting.name )
297 return;
298
299 /* Determine a suitable setting name */
300 setting_name ( ( ui->row.origin ?
301 ui->row.origin : ui->settings ),
302 &ui->row.setting, buf, sizeof ( buf ) );
303
304 /* Draw row */
305 attron ( A_BOLD );
306 msg ( INFO_ROW, "%s - %s", buf, ui->row.setting.description );
307 attroff ( A_BOLD );
311}
312
313/**
314 * Draw instruction row
315 *
316 * @v ui Settings UI
317 */
318static void draw_instruction_row ( struct settings_ui *ui ) {
319
321 if ( ui->row.editing ) {
323 "Enter - accept changes" INSTRUCTION_PAD
324 "Ctrl-C - discard changes" );
325 } else {
327 "%sCtrl-X - exit configuration utility",
328 ( ( ui->row.origin == ui->settings ) ?
329 "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) );
330 }
331}
332
333/**
334 * Draw the current block of setting rows
335 *
336 * @v ui Settings UI
337 */
338static void draw_setting_rows ( struct settings_ui *ui ) {
339 unsigned int i;
340
341 /* Draw ellipses before and/or after the list as necessary */
344 jump_scroll_is_first ( &ui->scroll ) ? " " : "..." );
346 ( SETTINGS_LIST_COL + 1 ),
347 jump_scroll_is_last ( &ui->scroll ) ? " " : "..." );
349
350 /* Draw visible settings. */
351 for ( i = 0 ; i < SETTINGS_LIST_ROWS ; i++ ) {
352 if ( ( ui->scroll.first + i ) < ui->scroll.count ) {
353 select_setting_row ( ui, ( ui->scroll.first + i ) );
354 draw_setting_row ( ui );
355 } else {
357 }
358 }
359}
360
361/**
362 * Select settings block
363 *
364 * @v ui Settings UI
365 * @v settings Settings block
366 */
367static void select_settings ( struct settings_ui *ui,
368 struct settings *settings ) {
369
371 ui->scroll.count = select_setting_row ( ui, 0 );
373 ui->scroll.current = 0;
374 ui->scroll.first = 0;
375 draw_title_row ( ui );
376 draw_setting_rows ( ui );
377 select_setting_row ( ui, 0 );
378}
379
380static int main_loop ( struct settings *settings ) {
381 struct settings_ui ui;
382 unsigned int previous;
383 unsigned int move;
384 int redraw = 1;
385 int key;
386 int rc;
387
388 /* Print initial screen content */
390 memset ( &ui, 0, sizeof ( ui ) );
391 select_settings ( &ui, settings );
392
393 while ( 1 ) {
394
395 /* Redraw rows if necessary */
396 if ( redraw ) {
397 draw_info_row ( &ui );
398 draw_instruction_row ( &ui );
399 color_set ( ( ui.row.editing ?
401 draw_setting_row ( &ui );
403 curs_set ( ui.row.editing );
404 redraw = 0;
405 }
406
407 /* Edit setting, if we are currently editing */
408 if ( ui.row.editing ) {
409
410 /* Sanity check */
411 assert ( ui.row.setting.name != NULL );
412
413 /* Redraw edit box */
415
416 /* Process keypress */
417 key = edit_setting ( &ui, getkey ( 0 ) );
418 switch ( key ) {
419 case CR:
420 case LF:
421 if ( ( rc = save_setting ( &ui ) ) != 0 ) {
422 alert ( ALERT_ROW, " %s ",
423 strerror ( rc ) );
424 }
425 /* Fall through */
426 case CTRL_C:
428 redraw = 1;
429 break;
430 default:
431 /* Do nothing */
432 break;
433 }
434
435 continue;
436 }
437
438 /* Otherwise, navigate through settings */
439 key = getkey ( 0 );
440 move = jump_scroll_key ( &ui.scroll, key );
441 if ( move ) {
442 previous = ui.scroll.current;
444 if ( ui.scroll.current != previous ) {
445 draw_setting_row ( &ui );
446 redraw = 1;
447 if ( jump_scroll ( &ui.scroll ) )
448 draw_setting_rows ( &ui );
450 }
451 continue;
452 }
453
454 /* Handle non-navigation keys */
455 switch ( key ) {
456 case CTRL_D:
457 if ( ! ui.row.setting.name )
458 break;
459 if ( ( rc = delete_setting ( ui.settings,
460 &ui.row.setting ) ) != 0 ){
461 alert ( ALERT_ROW, " %s ", strerror ( rc ) );
462 }
464 redraw = 1;
465 break;
466 case CTRL_X:
467 select_setting_row ( &ui, -1U );
468 return 0;
469 case CR:
470 case LF:
471 if ( ui.row.settings ) {
472 select_settings ( &ui, ui.row.settings );
473 redraw = 1;
474 }
475 /* Fall through */
476 default:
477 if ( ui.row.setting.name ) {
478 edit_setting ( &ui, key );
479 redraw = 1;
480 }
481 break;
482 }
483 }
484}
485
487 int rc;
488
489 initscr();
490 start_color();
492 curs_set ( 0 );
493 erase();
494
495 rc = main_loop ( settings );
496
497 endwin();
498
499 return rc;
500}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
union @162305117151260234136356364136041353210355154177 key
Sense key.
Definition scsi.h:3
typeof(acpi_finder=acpi_find)
ACPI table finder.
Definition acpi.c:48
ANSI colours.
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
pseudo_bit_t value[0x00020]
Definition arbel.h:2
long index
Definition bigint.h:65
if(len >=6 *4) __asm__ __volatile__("movsl" if(len >=5 *4) __asm__ __volatile__("movsl" if(len >=4 *4) __asm__ __volatile__("movsl" if(len >=3 *4) __asm__ __volatile__("movsl" if(len >=2 *4) __asm__ __volatile__("movsl" if(len >=1 *4) __asm__ __volatile__("movsl" if((len % 4) >=2) __asm__ __volatile__("movsw" if((len % 2) >=1) __asm__ __volatile__("movsb" retur dest)
Definition string.h:151
static const void * src
Definition string.h:48
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
const char * name
Definition ath9k_hw.c:1986
Branding configuration.
#define PRODUCT_SHORT_NAME
Definition branding.h:29
#define PRODUCT_SETTING_URI
Definition branding.h:171
int erase(void)
Completely clear the screen.
Definition clear.c:98
MuCurses header file.
#define mvprintw(y, x, fmt,...)
Definition curses.h:649
static int mvaddstr(int y, int x, const char *str)
Definition curses.h:618
#define start_color()
Definition curses.h:397
static int move(int y, int x)
Definition curses.h:594
#define A_BOLD
Definition curses.h:139
#define color_set(cpno, opts)
Definition curses.h:241
static int attroff(int attrs)
Definition curses.h:509
static int attron(int attrs)
Definition curses.h:513
union @104331263140136355135267063077374276003064103115 u
ring len
Length.
Definition dwmac.h:226
Editable text box widget.
static void init_editbox(struct edit_box *box, unsigned int row, unsigned int col, unsigned int width, unsigned int flags, char **buf)
Initialise text box widget.
Definition editbox.h:40
#define INSTRUCTION_PAD
Padding between instructions.
Definition form_ui.c:56
#define INSTRUCTION_ROW
Instructions row.
Definition form_ui.c:53
#define TITLE_ROW
Form title row.
Definition form_ui.c:44
int getkey(unsigned long timeout)
Get single keypress.
Definition getkey.c:72
#define CPAIR_SELECT
Highlighted text.
Definition ansicol.h:44
#define CPAIR_EDIT
Editable text.
Definition ansicol.h:50
#define CPAIR_NORMAL
Normal text.
Definition ansicol.h:41
#define CPAIR_SEPARATOR
Unselectable text (e.g.
Definition ansicol.h:47
#define CPAIR_URL
URL text.
Definition ansicol.h:56
static unsigned int count
Number of entries.
Definition dwmac.h:220
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
User interaction.
Configuration settings.
static int delete_setting(struct settings *settings, const struct setting *setting)
Delete setting.
Definition settings.h:534
#define SETTINGS
Configuration setting table.
Definition settings.h:54
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
void * memset(void *dest, int character, size_t len) __nonnull
unsigned int jump_scroll_move(struct jump_scroller *scroll, unsigned int move)
Move scroller.
Definition jumpscroll.c:93
unsigned int jump_scroll_key(struct jump_scroller *scroll, int key)
Jump scrolling.
Definition jumpscroll.c:43
int jump_scroll(struct jump_scroller *scroll)
Jump scroll to new page (if applicable)
Definition jumpscroll.c:141
Jump scrolling.
static int jump_scroll_is_first(struct jump_scroller *scroll)
Check if jump scroller is currently on first page.
Definition jumpscroll.h:62
static int jump_scroll_is_last(struct jump_scroller *scroll)
Check if jump scroller is currently on last page.
Definition jumpscroll.h:73
Key definitions.
#define LF
Definition keys.h:48
#define CTRL_D
Definition keys.h:22
#define CTRL_X
Definition keys.h:42
#define CTRL_C
Definition keys.h:21
#define CR
Definition keys.h:49
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition list.h:432
void alert(unsigned int row, const char *fmt,...)
Show alert message.
Definition message.c:104
void clearmsg(unsigned int row)
Clear message on specified row.
Definition message.c:75
void msg(unsigned int row, const char *fmt,...)
Print message centred on specified row.
Definition message.c:62
Message printing.
int curs_set(int visibility)
Set cursor visibility.
Definition mucurses.c:154
static void(* free)(struct refcnt *refcnt))
Definition refcnt.h:55
int setting_name(struct settings *settings, const struct setting *setting, char *buf, size_t len)
Return full setting name.
Definition settings.c:1607
struct settings * settings_target(struct settings *settings)
Redirect to target settings block.
Definition settings.c:550
int setting_applies(struct settings *settings, const struct setting *setting)
Check applicability of setting.
Definition settings.c:571
int setting_cmp(const struct setting *a, const struct setting *b)
Compare two settings.
Definition settings.c:1121
int fetchf_setting_copy(struct settings *settings, const struct setting *setting, struct settings **origin, struct setting *fetched, char **value)
Fetch copy of formatted value of setting.
Definition settings.c:1277
int storef_setting(struct settings *settings, const struct setting *setting, const char *value)
Store formatted value of setting.
Definition settings.c:1320
const char * settings_name(struct settings *settings)
Return settings block name.
Definition settings.c:346
static void draw_setting_rows(struct settings_ui *ui)
Draw the current block of setting rows.
int settings_ui(struct settings *settings)
#define ALERT_ROW
Definition settings_ui.c:55
static int edit_setting(struct settings_ui *ui, int key)
Edit setting ui.
static unsigned int select_setting_row(struct settings_ui *ui, unsigned int index)
Select a setting.
#define INFO_ROW
Definition settings_ui.c:54
#define SETTINGS_LIST_ROW
Definition settings_ui.c:51
static void draw_info_row(struct settings_ui *ui)
Draw information row.
static void draw_title_row(struct settings_ui *ui)
Draw title row.
static void select_settings(struct settings_ui *ui, struct settings *settings)
Select settings block.
static void draw_instruction_row(struct settings_ui *ui)
Draw instruction row.
#define SETTING_ROW_TEXT(cols)
Layout of text within a setting row.
Definition settings_ui.c:60
static int main_loop(struct settings *settings)
#define SETTINGS_LIST_ROWS
Definition settings_ui.c:53
static void draw_setting_row(struct settings_ui *ui)
Draw setting row.
static size_t string_copy(char *dest, const char *src, size_t len)
Copy string without NUL termination.
#define SETTINGS_LIST_COL
Definition settings_ui.c:52
static int save_setting(struct settings_ui *ui)
Save setting ui value back to configuration settings.
Option configuration console.
#define offsetof(type, field)
Get offset of a field within a structure.
Definition stddef.h:25
char * strerror(int errno)
Retrieve string representation of error number.
Definition strerror.c:79
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
An editable text box widget.
Definition editbox.h:18
struct widget widget
Text widget.
Definition editbox.h:20
A jump scroller.
Definition jumpscroll.h:16
unsigned int count
Total number of items.
Definition jumpscroll.h:20
unsigned int first
First visible item.
Definition jumpscroll.h:24
unsigned int rows
Maximum number of visible rows.
Definition jumpscroll.h:18
unsigned int current
Currently selected item.
Definition jumpscroll.h:22
A setting.
Definition settings.h:24
const char * name
Name.
Definition settings.h:29
const char * description
Description.
Definition settings.h:31
A settings user interface row.
Definition settings_ui.c:79
char * buf
Dynamically allocated buffer for setting's value.
int editing
Editing in progress flag.
unsigned int row
Screen row.
Definition settings_ui.c:96
struct setting setting
Configuration setting.
Definition settings_ui.c:94
struct settings * settings
Target configuration settings block.
Definition settings_ui.c:84
struct settings * origin
Configuration setting origin.
Definition settings_ui.c:89
struct edit_box editbox
Edit box widget used for editing setting.
Definition settings_ui.c:98
A settings user interface.
struct jump_scroller scroll
Jump scroller.
struct settings * settings
Settings block.
struct settings_ui_row row
Current row.
A settings block.
Definition settings.h:133
struct list_head children
Child settings blocks.
Definition settings.h:143
const char * name
Name.
Definition settings.h:137
struct settings * parent
Parent settings block.
Definition settings.h:139
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition tables.h:386
#define COLS
Definition vga.h:27
static void draw_widget(struct widget *widget)
Draw text widget.
Definition widget.h:87
static int edit_widget(struct widget *widget, int key)
Edit text widget.
Definition widget.h:104
WINDOW * initscr(void)
Initialise console environment.
Definition wininit.c:18
int endwin(void)
Finalise console environment.
Definition wininit.c:32