|  | @@ -36,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 | 
		
	
		
			
			| 36 | 36 |  #include <ipxe/timer.h>
 | 
		
	
		
			
			| 37 | 37 |  #include <ipxe/console.h>
 | 
		
	
		
			
			| 38 | 38 |  #include <ipxe/ansicol.h>
 | 
		
	
		
			
			|  | 39 | +#include <ipxe/jumpscroll.h>
 | 
		
	
		
			
			| 39 | 40 |  #include <ipxe/menu.h>
 | 
		
	
		
			
			| 40 | 41 |  
 | 
		
	
		
			
			| 41 | 42 |  /* Screen layout */
 | 
		
	
	
		
			
			|  | @@ -50,12 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 | 
		
	
		
			
			| 50 | 51 |  struct menu_ui {
 | 
		
	
		
			
			| 51 | 52 |  	/** Menu */
 | 
		
	
		
			
			| 52 | 53 |  	struct menu *menu;
 | 
		
	
		
			
			| 53 |  | -	/** Number of menu items */
 | 
		
	
		
			
			| 54 |  | -	unsigned int count;
 | 
		
	
		
			
			| 55 |  | -	/** Currently selected item */
 | 
		
	
		
			
			| 56 |  | -	int selected;
 | 
		
	
		
			
			| 57 |  | -	/** First visible item */
 | 
		
	
		
			
			| 58 |  | -	int first_visible;
 | 
		
	
		
			
			|  | 54 | +	/** Jump scroller */
 | 
		
	
		
			
			|  | 55 | +	struct jump_scroller scroll;
 | 
		
	
		
			
			| 59 | 56 |  	/** Timeout (0=indefinite) */
 | 
		
	
		
			
			| 60 | 57 |  	unsigned long timeout;
 | 
		
	
		
			
			| 61 | 58 |  };
 | 
		
	
	
		
			
			|  | @@ -84,7 +81,7 @@ static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) {
 | 
		
	
		
			
			| 84 | 81 |   * @v ui		Menu user interface
 | 
		
	
		
			
			| 85 | 82 |   * @v index		Index
 | 
		
	
		
			
			| 86 | 83 |   */
 | 
		
	
		
			
			| 87 |  | -static void draw_menu_item ( struct menu_ui *ui, int index ) {
 | 
		
	
		
			
			|  | 84 | +static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) {
 | 
		
	
		
			
			| 88 | 85 |  	struct menu_item *item;
 | 
		
	
		
			
			| 89 | 86 |  	unsigned int row_offset;
 | 
		
	
		
			
			| 90 | 87 |  	char buf[ MENU_COLS + 1 /* NUL */ ];
 | 
		
	
	
		
			
			|  | @@ -94,7 +91,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
 | 
		
	
		
			
			| 94 | 91 |  	size_t len;
 | 
		
	
		
			
			| 95 | 92 |  
 | 
		
	
		
			
			| 96 | 93 |  	/* Move to start of row */
 | 
		
	
		
			
			| 97 |  | -	row_offset = ( index - ui->first_visible );
 | 
		
	
		
			
			|  | 94 | +	row_offset = ( index - ui->scroll.first );
 | 
		
	
		
			
			| 98 | 95 |  	move ( ( MENU_ROW + row_offset ), MENU_COL );
 | 
		
	
		
			
			| 99 | 96 |  
 | 
		
	
		
			
			| 100 | 97 |  	/* Get menu item */
 | 
		
	
	
		
			
			|  | @@ -106,7 +103,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
 | 
		
	
		
			
			| 106 | 103 |  			color_set ( CPAIR_SEPARATOR, NULL );
 | 
		
	
		
			
			| 107 | 104 |  
 | 
		
	
		
			
			| 108 | 105 |  		/* Highlight if this is the selected item */
 | 
		
	
		
			
			| 109 |  | -		if ( index == ui->selected ) {
 | 
		
	
		
			
			|  | 106 | +		if ( index == ui->scroll.current ) {
 | 
		
	
		
			
			| 110 | 107 |  			color_set ( CPAIR_SELECT, NULL );
 | 
		
	
		
			
			| 111 | 108 |  			attron ( A_BOLD );
 | 
		
	
		
			
			| 112 | 109 |  		}
 | 
		
	
	
		
			
			|  | @@ -125,7 +122,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
 | 
		
	
		
			
			| 125 | 122 |  			snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)",
 | 
		
	
		
			
			| 126 | 123 |  				   ( ( ui->timeout + TICKS_PER_SEC - 1 ) /
 | 
		
	
		
			
			| 127 | 124 |  				     TICKS_PER_SEC ) );
 | 
		
	
		
			
			| 128 |  | -		if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) {
 | 
		
	
		
			
			|  | 125 | +		if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) {
 | 
		
	
		
			
			| 129 | 126 |  			memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ),
 | 
		
	
		
			
			| 130 | 127 |  				 timeout_buf, timeout_len );
 | 
		
	
		
			
			| 131 | 128 |  		}
 | 
		
	
	
		
			
			|  | @@ -154,24 +151,17 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) {
 | 
		
	
		
			
			| 154 | 151 |  static void draw_menu_items ( struct menu_ui *ui ) {
 | 
		
	
		
			
			| 155 | 152 |  	unsigned int i;
 | 
		
	
		
			
			| 156 | 153 |  
 | 
		
	
		
			
			| 157 |  | -	/* Jump scroll to correct point in list */
 | 
		
	
		
			
			| 158 |  | -	while ( ui->first_visible < ui->selected )
 | 
		
	
		
			
			| 159 |  | -		ui->first_visible += MENU_ROWS;
 | 
		
	
		
			
			| 160 |  | -	while ( ui->first_visible > ui->selected )
 | 
		
	
		
			
			| 161 |  | -		ui->first_visible -= MENU_ROWS;
 | 
		
	
		
			
			| 162 |  | -
 | 
		
	
		
			
			| 163 | 154 |  	/* Draw ellipses before and/or after the list as necessary */
 | 
		
	
		
			
			| 164 | 155 |  	color_set ( CPAIR_SEPARATOR, NULL );
 | 
		
	
		
			
			| 165 | 156 |  	mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ),
 | 
		
	
		
			
			| 166 |  | -		   ( ( ui->first_visible > 0 ) ? "..." : "   " ) );
 | 
		
	
		
			
			|  | 157 | +		   ( jump_scroll_is_first ( &ui->scroll ) ? "   " : "..." ) );
 | 
		
	
		
			
			| 167 | 158 |  	mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ),
 | 
		
	
		
			
			| 168 |  | -		   ( ( ( ui->first_visible + MENU_ROWS ) < ui->count ) ?
 | 
		
	
		
			
			| 169 |  | -		     "..." : "   " ) );
 | 
		
	
		
			
			|  | 159 | +		   ( jump_scroll_is_last ( &ui->scroll ) ? "   " : "..." ) );
 | 
		
	
		
			
			| 170 | 160 |  	color_set ( CPAIR_NORMAL, NULL );
 | 
		
	
		
			
			| 171 | 161 |  
 | 
		
	
		
			
			| 172 | 162 |  	/* Draw visible items */
 | 
		
	
		
			
			| 173 | 163 |  	for ( i = 0 ; i < MENU_ROWS ; i++ )
 | 
		
	
		
			
			| 174 |  | -		draw_menu_item ( ui, ( ui->first_visible + i ) );
 | 
		
	
		
			
			|  | 164 | +		draw_menu_item ( ui, ( ui->scroll.first + i ) );
 | 
		
	
		
			
			| 175 | 165 |  }
 | 
		
	
		
			
			| 176 | 166 |  
 | 
		
	
		
			
			| 177 | 167 |  /**
 | 
		
	
	
		
			
			|  | @@ -184,8 +174,7 @@ static void draw_menu_items ( struct menu_ui *ui ) {
 | 
		
	
		
			
			| 184 | 174 |  static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
 | 
		
	
		
			
			| 185 | 175 |  	struct menu_item *item;
 | 
		
	
		
			
			| 186 | 176 |  	unsigned long timeout;
 | 
		
	
		
			
			| 187 |  | -	unsigned int delta;
 | 
		
	
		
			
			| 188 |  | -	int current;
 | 
		
	
		
			
			|  | 177 | +	unsigned int previous;
 | 
		
	
		
			
			| 189 | 178 |  	int key;
 | 
		
	
		
			
			| 190 | 179 |  	int i;
 | 
		
	
		
			
			| 191 | 180 |  	int move;
 | 
		
	
	
		
			
			|  | @@ -194,7 +183,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
 | 
		
	
		
			
			| 194 | 183 |  
 | 
		
	
		
			
			| 195 | 184 |  	do {
 | 
		
	
		
			
			| 196 | 185 |  		/* Record current selection */
 | 
		
	
		
			
			| 197 |  | -		current = ui->selected;
 | 
		
	
		
			
			|  | 186 | +		previous = ui->scroll.current;
 | 
		
	
		
			
			| 198 | 187 |  
 | 
		
	
		
			
			| 199 | 188 |  		/* Calculate timeout as remainder of current second */
 | 
		
	
		
			
			| 200 | 189 |  		timeout = ( ui->timeout % TICKS_PER_SEC );
 | 
		
	
	
		
			
			|  | @@ -213,27 +202,11 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
 | 
		
	
		
			
			| 213 | 202 |  			/* Cancel any timeout */
 | 
		
	
		
			
			| 214 | 203 |  			ui->timeout = 0;
 | 
		
	
		
			
			| 215 | 204 |  
 | 
		
	
		
			
			| 216 |  | -			/* Handle key */
 | 
		
	
		
			
			|  | 205 | +			/* Handle scroll keys */
 | 
		
	
		
			
			|  | 206 | +			move = jump_scroll_key ( &ui->scroll, key );
 | 
		
	
		
			
			|  | 207 | +
 | 
		
	
		
			
			|  | 208 | +			/* Handle other keys */
 | 
		
	
		
			
			| 217 | 209 |  			switch ( key ) {
 | 
		
	
		
			
			| 218 |  | -			case KEY_UP:
 | 
		
	
		
			
			| 219 |  | -				move = -1;
 | 
		
	
		
			
			| 220 |  | -				break;
 | 
		
	
		
			
			| 221 |  | -			case KEY_DOWN:
 | 
		
	
		
			
			| 222 |  | -				move = +1;
 | 
		
	
		
			
			| 223 |  | -				break;
 | 
		
	
		
			
			| 224 |  | -			case KEY_PPAGE:
 | 
		
	
		
			
			| 225 |  | -				move = ( ui->first_visible - ui->selected - 1 );
 | 
		
	
		
			
			| 226 |  | -				break;
 | 
		
	
		
			
			| 227 |  | -			case KEY_NPAGE:
 | 
		
	
		
			
			| 228 |  | -				move = ( ui->first_visible - ui->selected
 | 
		
	
		
			
			| 229 |  | -					 + MENU_ROWS );
 | 
		
	
		
			
			| 230 |  | -				break;
 | 
		
	
		
			
			| 231 |  | -			case KEY_HOME:
 | 
		
	
		
			
			| 232 |  | -				move = -ui->count;
 | 
		
	
		
			
			| 233 |  | -				break;
 | 
		
	
		
			
			| 234 |  | -			case KEY_END:
 | 
		
	
		
			
			| 235 |  | -				move = +ui->count;
 | 
		
	
		
			
			| 236 |  | -				break;
 | 
		
	
		
			
			| 237 | 210 |  			case ESC:
 | 
		
	
		
			
			| 238 | 211 |  			case CTRL_C:
 | 
		
	
		
			
			| 239 | 212 |  				rc = -ECANCELED;
 | 
		
	
	
		
			
			|  | @@ -251,7 +224,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
 | 
		
	
		
			
			| 251 | 224 |  						i++;
 | 
		
	
		
			
			| 252 | 225 |  						continue;
 | 
		
	
		
			
			| 253 | 226 |  					}
 | 
		
	
		
			
			| 254 |  | -					ui->selected = i;
 | 
		
	
		
			
			|  | 227 | +					ui->scroll.current = i;
 | 
		
	
		
			
			| 255 | 228 |  					if ( item->label ) {
 | 
		
	
		
			
			| 256 | 229 |  						chosen = 1;
 | 
		
	
		
			
			| 257 | 230 |  					} else {
 | 
		
	
	
		
			
			|  | @@ -264,31 +237,22 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) {
 | 
		
	
		
			
			| 264 | 237 |  
 | 
		
	
		
			
			| 265 | 238 |  		/* Move selection, if applicable */
 | 
		
	
		
			
			| 266 | 239 |  		while ( move ) {
 | 
		
	
		
			
			| 267 |  | -			ui->selected += move;
 | 
		
	
		
			
			| 268 |  | -			if ( ui->selected < 0 ) {
 | 
		
	
		
			
			| 269 |  | -				ui->selected = 0;
 | 
		
	
		
			
			| 270 |  | -				move = +1;
 | 
		
	
		
			
			| 271 |  | -			} else if ( ui->selected >= ( int ) ui->count ) {
 | 
		
	
		
			
			| 272 |  | -				ui->selected = ( ui->count - 1 );
 | 
		
	
		
			
			| 273 |  | -				move = -1;
 | 
		
	
		
			
			| 274 |  | -			}
 | 
		
	
		
			
			| 275 |  | -			item = menu_item ( ui->menu, ui->selected );
 | 
		
	
		
			
			|  | 240 | +			move = jump_scroll_move ( &ui->scroll, move );
 | 
		
	
		
			
			|  | 241 | +			item = menu_item ( ui->menu, ui->scroll.current );
 | 
		
	
		
			
			| 276 | 242 |  			if ( item->label )
 | 
		
	
		
			
			| 277 | 243 |  				break;
 | 
		
	
		
			
			| 278 |  | -			move = ( ( move > 0 ) ? +1 : -1 );
 | 
		
	
		
			
			| 279 | 244 |  		}
 | 
		
	
		
			
			| 280 | 245 |  
 | 
		
	
		
			
			| 281 | 246 |  		/* Redraw selection if necessary */
 | 
		
	
		
			
			| 282 |  | -		if ( ( ui->selected != current ) || ( timeout != 0 ) ) {
 | 
		
	
		
			
			| 283 |  | -			draw_menu_item ( ui, current );
 | 
		
	
		
			
			| 284 |  | -			delta = ( ui->selected - ui->first_visible );
 | 
		
	
		
			
			| 285 |  | -			if ( delta >= MENU_ROWS )
 | 
		
	
		
			
			|  | 247 | +		if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) {
 | 
		
	
		
			
			|  | 248 | +			draw_menu_item ( ui, previous );
 | 
		
	
		
			
			|  | 249 | +			if ( jump_scroll ( &ui->scroll ) )
 | 
		
	
		
			
			| 286 | 250 |  				draw_menu_items ( ui );
 | 
		
	
		
			
			| 287 |  | -			draw_menu_item ( ui, ui->selected );
 | 
		
	
		
			
			|  | 251 | +			draw_menu_item ( ui, ui->scroll.current );
 | 
		
	
		
			
			| 288 | 252 |  		}
 | 
		
	
		
			
			| 289 | 253 |  
 | 
		
	
		
			
			| 290 | 254 |  		/* Record selection */
 | 
		
	
		
			
			| 291 |  | -		item = menu_item ( ui->menu, ui->selected );
 | 
		
	
		
			
			|  | 255 | +		item = menu_item ( ui->menu, ui->scroll.current );
 | 
		
	
		
			
			| 292 | 256 |  		assert ( item != NULL );
 | 
		
	
		
			
			| 293 | 257 |  		assert ( item->label != NULL );
 | 
		
	
		
			
			| 294 | 258 |  		*selected = item;
 | 
		
	
	
		
			
			|  | @@ -317,21 +281,22 @@ int show_menu ( struct menu *menu, unsigned long timeout,
 | 
		
	
		
			
			| 317 | 281 |  	/* Initialise UI */
 | 
		
	
		
			
			| 318 | 282 |  	memset ( &ui, 0, sizeof ( ui ) );
 | 
		
	
		
			
			| 319 | 283 |  	ui.menu = menu;
 | 
		
	
		
			
			|  | 284 | +	ui.scroll.rows = MENU_ROWS;
 | 
		
	
		
			
			| 320 | 285 |  	ui.timeout = timeout;
 | 
		
	
		
			
			| 321 | 286 |  	list_for_each_entry ( item, &menu->items, list ) {
 | 
		
	
		
			
			| 322 | 287 |  		if ( item->label ) {
 | 
		
	
		
			
			| 323 | 288 |  			if ( ! labelled_count )
 | 
		
	
		
			
			| 324 |  | -				ui.selected = ui.count;
 | 
		
	
		
			
			|  | 289 | +				ui.scroll.current = ui.scroll.count;
 | 
		
	
		
			
			| 325 | 290 |  			labelled_count++;
 | 
		
	
		
			
			| 326 | 291 |  			if ( select ) {
 | 
		
	
		
			
			| 327 | 292 |  				if ( strcmp ( select, item->label ) == 0 )
 | 
		
	
		
			
			| 328 |  | -					ui.selected = ui.count;
 | 
		
	
		
			
			|  | 293 | +					ui.scroll.current = ui.scroll.count;
 | 
		
	
		
			
			| 329 | 294 |  			} else {
 | 
		
	
		
			
			| 330 | 295 |  				if ( item->is_default )
 | 
		
	
		
			
			| 331 |  | -					ui.selected = ui.count;
 | 
		
	
		
			
			|  | 296 | +					ui.scroll.current = ui.scroll.count;
 | 
		
	
		
			
			| 332 | 297 |  			}
 | 
		
	
		
			
			| 333 | 298 |  		}
 | 
		
	
		
			
			| 334 |  | -		ui.count++;
 | 
		
	
		
			
			|  | 299 | +		ui.scroll.count++;
 | 
		
	
		
			
			| 335 | 300 |  	}
 | 
		
	
		
			
			| 336 | 301 |  	if ( ! labelled_count ) {
 | 
		
	
		
			
			| 337 | 302 |  		/* Menus with no labelled items cannot be selected
 | 
		
	
	
		
			
			|  | @@ -353,8 +318,9 @@ int show_menu ( struct menu *menu, unsigned long timeout,
 | 
		
	
		
			
			| 353 | 318 |  	snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title );
 | 
		
	
		
			
			| 354 | 319 |  	mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf );
 | 
		
	
		
			
			| 355 | 320 |  	attroff ( A_BOLD );
 | 
		
	
		
			
			|  | 321 | +	jump_scroll ( &ui.scroll );
 | 
		
	
		
			
			| 356 | 322 |  	draw_menu_items ( &ui );
 | 
		
	
		
			
			| 357 |  | -	draw_menu_item ( &ui, ui.selected );
 | 
		
	
		
			
			|  | 323 | +	draw_menu_item ( &ui, ui.scroll.current );
 | 
		
	
		
			
			| 358 | 324 |  
 | 
		
	
		
			
			| 359 | 325 |  	/* Enter main loop */
 | 
		
	
		
			
			| 360 | 326 |  	rc = menu_loop ( &ui, selected );
 |