iPXE
menu_ui.c
Go to the documentation of this file.
1/*
2 * Copyright (C) 2012 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/** @file
28 *
29 * Menu interface
30 *
31 */
32
33#include <string.h>
34#include <errno.h>
35#include <curses.h>
36#include <ipxe/keys.h>
37#include <ipxe/timer.h>
38#include <ipxe/console.h>
39#include <ipxe/ansicol.h>
40#include <ipxe/jumpscroll.h>
41#include <ipxe/dynui.h>
42
43/* Screen layout */
44#define TITLE_ROW 1U
45#define MENU_ROW 3U
46#define MENU_COL 1U
47#define MENU_ROWS ( LINES - 2U - MENU_ROW )
48#define MENU_COLS ( COLS - 2U )
49#define MENU_PAD 2U
50
51/** A menu user interface */
52struct menu_ui {
53 /** Dynamic user interface */
55 /** Jump scroller */
57 /** Remaining timeout (0=indefinite) */
58 unsigned long timeout;
59 /** Post-activity timeout (0=indefinite) */
60 unsigned long retimeout;
61};
62
63/**
64 * Draw a numbered menu item
65 *
66 * @v ui Menu user interface
67 * @v index Index
68 */
69static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) {
70 struct dynamic_item *item;
71 unsigned int row_offset;
72 char buf[ MENU_COLS + 1 /* NUL */ ];
73 char timeout_buf[6]; /* "(xxx)" + NUL */
74 size_t timeout_len;
75 size_t max_len;
76 size_t len;
77
78 /* Move to start of row */
79 row_offset = ( index - ui->scroll.first );
80 move ( ( MENU_ROW + row_offset ), MENU_COL );
81
82 /* Get menu item */
83 item = dynui_item ( ui->dynui, index );
84 if ( item ) {
85
86 /* Draw separators in a different colour */
87 if ( ! item->name )
89
90 /* Highlight if this is the selected item */
91 if ( index == ui->scroll.current ) {
93 attron ( A_BOLD );
94 }
95
96 /* Construct row */
97 memset ( buf, ' ', ( sizeof ( buf ) - 1 ) );
98 buf[ sizeof ( buf ) -1 ] = '\0';
99 len = strlen ( item->text );
100 max_len = ( sizeof ( buf ) - 1 /* NUL */ - ( 2 * MENU_PAD ) );
101 if ( len > max_len )
102 len = max_len;
103 memcpy ( ( buf + MENU_PAD ), item->text, len );
104
105 /* Add timeout if applicable */
106 timeout_len =
107 snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
108 ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
109 TICKS_PER_SEC ) );
110 if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) {
111 memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
112 timeout_buf, timeout_len );
113 }
114
115 /* Print row */
116 printw ( "%s", buf );
117
118 /* Reset attributes */
120 attroff ( A_BOLD );
121
122 } else {
123 /* Clear row if there is no corresponding menu item */
124 clrtoeol();
125 }
126
127 /* Move cursor back to start of row */
128 move ( ( MENU_ROW + row_offset ), MENU_COL );
129}
130
131/**
132 * Draw the current block of menu items
133 *
134 * @v ui Menu user interface
135 */
136static void draw_menu_items ( struct menu_ui *ui ) {
137 unsigned int i;
138
139 /* Draw ellipses before and/or after the list as necessary */
141 mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
142 ( jump_scroll_is_first ( &ui->scroll ) ? " " : "..." ) );
144 ( jump_scroll_is_last ( &ui->scroll ) ? " " : "..." ) );
146
147 /* Draw visible items */
148 for ( i = 0 ; i < MENU_ROWS ; i++ )
149 draw_menu_item ( ui, ( ui->scroll.first + i ) );
150}
151
152/**
153 * Menu main loop
154 *
155 * @v ui Menu user interface
156 * @ret selected Selected item
157 * @ret rc Return status code
158 */
159static int menu_loop ( struct menu_ui *ui, struct dynamic_item **selected ) {
160 struct dynamic_item *item;
161 unsigned long timeout;
162 unsigned int previous;
163 unsigned int move;
164 int key;
165 int chosen = 0;
166 int rc = 0;
167
168 do {
169 /* Record current selection */
170 previous = ui->scroll.current;
171
172 /* Calculate timeout as remainder of current second */
173 timeout = ( ui->timeout % TICKS_PER_SEC );
174 if ( ( timeout == 0 ) && ( ui->timeout != 0 ) )
176 ui->timeout -= timeout;
177
178 /* Get key */
180 key = getkey ( timeout );
181 if ( key < 0 ) {
182 /* Choose default if we finally time out */
183 if ( ui->timeout == 0 )
184 chosen = 1;
185 } else {
186 /* Reset timeout after activity */
187 ui->timeout = ui->retimeout;
188
189 /* Handle scroll keys */
190 move = jump_scroll_key ( &ui->scroll, key );
191
192 /* Handle other keys */
193 switch ( key ) {
194 case ESC:
195 case CTRL_C:
196 rc = -ECANCELED;
197 break;
198 case CR:
199 case LF:
200 chosen = 1;
201 break;
202 default:
203 item = dynui_shortcut ( ui->dynui, key );
204 if ( item ) {
205 ui->scroll.current = item->index;
206 if ( item->name ) {
207 chosen = 1;
208 } else {
210 }
211 }
212 break;
213 }
214 }
215
216 /* Move selection, if applicable */
217 while ( move ) {
218 move = jump_scroll_move ( &ui->scroll, move );
219 item = dynui_item ( ui->dynui, ui->scroll.current );
220 if ( item->name )
221 break;
222 }
223
224 /* Redraw selection if necessary */
225 if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) {
226 draw_menu_item ( ui, previous );
227 if ( jump_scroll ( &ui->scroll ) )
228 draw_menu_items ( ui );
229 draw_menu_item ( ui, ui->scroll.current );
230 }
231
232 /* Record selection */
233 item = dynui_item ( ui->dynui, ui->scroll.current );
234 assert ( item != NULL );
235 assert ( item->name != NULL );
236 *selected = item;
237
238 } while ( ( rc == 0 ) && ! chosen );
239
240 return rc;
241}
242
243/**
244 * Show menu
245 *
246 * @v dynui Dynamic user interface
247 * @v timeout Initial timeout period, in ticks (0=indefinite)
248 * @v retimeout Post-activity timeout period, in ticks (0=indefinite)
249 * @ret selected Selected item
250 * @ret rc Return status code
251 */
252int show_menu ( struct dynamic_ui *dynui, unsigned long timeout,
253 unsigned long retimeout, const char *select,
254 struct dynamic_item **selected ) {
255 struct dynamic_item *item;
256 struct menu_ui ui;
257 char buf[ MENU_COLS + 1 /* NUL */ ];
258 int named_count = 0;
259 int rc;
260
261 /* Initialise UI */
262 memset ( &ui, 0, sizeof ( ui ) );
263 ui.dynui = dynui;
264 ui.scroll.rows = MENU_ROWS;
265 ui.timeout = timeout;
266 ui.retimeout = retimeout;
267
268 list_for_each_entry ( item, &dynui->items, list ) {
269 if ( item->name ) {
270 if ( ! named_count )
271 ui.scroll.current = ui.scroll.count;
272 named_count++;
273 if ( select ) {
274 if ( strcmp ( select, item->name ) == 0 )
275 ui.scroll.current = ui.scroll.count;
276 } else {
277 if ( item->flags & DYNUI_DEFAULT )
278 ui.scroll.current = ui.scroll.count;
279 }
280 }
281 ui.scroll.count++;
282 }
283 if ( ! named_count ) {
284 /* Menus with no named items cannot be selected from,
285 * and will seriously confuse the navigation logic.
286 * Refuse to display any such menus.
287 */
288 return -ENOENT;
289 }
290
291 /* Initialise screen */
292 initscr();
293 start_color();
295 curs_set ( 0 );
296 erase();
297
298 /* Draw initial content */
299 attron ( A_BOLD );
300 snprintf ( buf, sizeof ( buf ), "%s", ui.dynui->title );
301 mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
302 attroff ( A_BOLD );
303 jump_scroll ( &ui.scroll );
304 draw_menu_items ( &ui );
305 draw_menu_item ( &ui, ui.scroll.current );
306
307 /* Enter main loop */
308 rc = menu_loop ( &ui, selected );
309 assert ( *selected );
310
311 /* Clear screen */
312 endwin();
313
314 return rc;
315}
#define NULL
NULL pointer (VOID *)
Definition Base.h:322
union @162305117151260234136356364136041353210355154177 key
Sense key.
Definition scsi.h:3
ANSI colours.
#define ESC
Escape character.
Definition ansiesc.h:93
struct arbelprm_rc_send_wqe rc
Definition arbel.h:3
long index
Definition bigint.h:65
#define assert(condition)
Assert a condition at run-time.
Definition assert.h:50
int erase(void)
Completely clear the screen.
Definition clear.c:98
MuCurses header file.
#define printw(fmt,...)
Definition curses.h:717
#define mvprintw(y, x, fmt,...)
Definition curses.h:649
void timeout(int)
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 clrtoeol(void)
Definition curses.h:554
static int attroff(int attrs)
Definition curses.h:509
static int attron(int attrs)
Definition curses.h:513
ring len
Length.
Definition dwmac.h:226
struct dynamic_item * dynui_item(struct dynamic_ui *dynui, unsigned int index)
Find dynamic user interface item by index.
Definition dynui.c:193
struct dynamic_item * dynui_shortcut(struct dynamic_ui *dynui, int key)
Find dynamic user interface item by shortcut key.
Definition dynui.c:212
Dynamic user interfaces.
#define DYNUI_DEFAULT
Dynamic user interface item is default selection.
Definition dynui.h:46
Error codes.
#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_NORMAL
Normal text.
Definition ansicol.h:41
#define CPAIR_SEPARATOR
Unselectable text (e.g.
Definition ansicol.h:47
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define ENOENT
No such file or directory.
Definition errno.h:515
#define ECANCELED
Operation canceled.
Definition errno.h:344
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
User interaction.
iPXE timers
#define TICKS_PER_SEC
Number of ticks per second.
Definition timer.h:16
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
#define SCROLL_NONE
Do not scroll.
Definition jumpscroll.h:48
#define SCROLL_DOWN
Scroll down by one line.
Definition jumpscroll.h:54
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_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
static void draw_menu_item(struct menu_ui *ui, unsigned int index)
Draw a numbered menu item.
Definition menu_ui.c:69
#define MENU_COL
Definition menu_ui.c:46
static int menu_loop(struct menu_ui *ui, struct dynamic_item **selected)
Menu main loop.
Definition menu_ui.c:159
int show_menu(struct dynamic_ui *dynui, unsigned long timeout, unsigned long retimeout, const char *select, struct dynamic_item **selected)
Show menu.
Definition menu_ui.c:252
#define MENU_COLS
Definition menu_ui.c:48
#define MENU_ROW
Definition menu_ui.c:45
#define MENU_ROWS
Definition menu_ui.c:47
#define MENU_PAD
Definition menu_ui.c:49
static void draw_menu_items(struct menu_ui *ui)
Draw the current block of menu items.
Definition menu_ui.c:136
int curs_set(int visibility)
Set cursor visibility.
Definition mucurses.c:154
int select(fd_set *readfds, int wait)
Check file descriptors for readiness.
Definition posix_io.c:229
int strcmp(const char *first, const char *second)
Compare strings.
Definition string.c:174
size_t strlen(const char *src)
Get length of string.
Definition string.c:244
A dynamic user interface item.
Definition dynui.h:30
unsigned int index
Index.
Definition dynui.h:38
const char * text
Text.
Definition dynui.h:36
unsigned int flags
Flags.
Definition dynui.h:40
const char * name
Name.
Definition dynui.h:34
A dynamic user interface.
Definition dynui.h:16
const char * title
Title.
Definition dynui.h:22
struct list_head items
Dynamic user interface items.
Definition dynui.h:24
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 menu user interface.
Definition menu_ui.c:52
unsigned long retimeout
Post-activity timeout (0=indefinite)
Definition menu_ui.c:60
struct jump_scroller scroll
Jump scroller.
Definition menu_ui.c:56
struct dynamic_ui * dynui
Dynamic user interface.
Definition menu_ui.c:54
unsigned long timeout
Remaining timeout (0=indefinite)
Definition menu_ui.c:58
#define COLS
Definition vga.h:27
int snprintf(char *buf, size_t size, const char *fmt,...)
Write a formatted string to a buffer.
Definition vsprintf.c:383
WINDOW * initscr(void)
Initialise console environment.
Definition wininit.c:18
int endwin(void)
Finalise console environment.
Definition wininit.c:32