menu.4th revision 254105
1\ Copyright (c) 2003 Scott Long <scottl@freebsd.org> 2\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> 3\ Copyright (c) 2006-2013 Devin Teske <dteske@FreeBSD.org> 4\ All rights reserved. 5\ 6\ Redistribution and use in source and binary forms, with or without 7\ modification, are permitted provided that the following conditions 8\ are met: 9\ 1. Redistributions of source code must retain the above copyright 10\ notice, this list of conditions and the following disclaimer. 11\ 2. Redistributions in binary form must reproduce the above copyright 12\ notice, this list of conditions and the following disclaimer in the 13\ documentation and/or other materials provided with the distribution. 14\ 15\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25\ SUCH DAMAGE. 26\ 27\ $FreeBSD: head/sys/boot/forth/menu.4th 254105 2013-08-08 22:09:46Z dteske $ 28 29marker task-menu.4th 30 31\ Frame drawing 32include /boot/frames.4th 33 34f_double \ Set frames to double (see frames.4th). Replace with 35 \ f_single if you want single frames. 3646 constant dot \ ASCII definition of a period (in decimal) 37 38 4 constant menu_timeout_default_x \ default column position of timeout 3923 constant menu_timeout_default_y \ default row position of timeout msg 4010 constant menu_timeout_default \ default timeout (in seconds) 41 42\ Customize the following values with care 43 44 1 constant menu_start \ Numerical prefix of first menu item 45dot constant bullet \ Menu bullet (appears after numerical prefix) 46 5 constant menu_x \ Row position of the menu (from the top) 47 10 constant menu_y \ Column position of the menu (from left side) 48 49\ Menu Appearance 50variable menuidx \ Menu item stack for number prefixes 51variable menurow \ Menu item stack for positioning 52variable menubllt \ Menu item bullet 53 54\ Menu Positioning 55variable menuX \ Menu X offset (columns) 56variable menuY \ Menu Y offset (rows) 57 58\ Menu-item key association/detection 59variable menukey1 60variable menukey2 61variable menukey3 62variable menukey4 63variable menukey5 64variable menukey6 65variable menukey7 66variable menukey8 67variable menureboot 68variable menurebootadded 69variable menuacpi 70variable menuoptions 71 72\ Menu timer [count-down] variables 73variable menu_timeout_enabled \ timeout state (internal use only) 74variable menu_time \ variable for tracking the passage of time 75variable menu_timeout \ determined configurable delay duration 76variable menu_timeout_x \ column position of timeout message 77variable menu_timeout_y \ row position of timeout message 78 79\ Menu initialization status variables 80variable init_state1 81variable init_state2 82variable init_state3 83variable init_state4 84variable init_state5 85variable init_state6 86variable init_state7 87variable init_state8 88 89\ Boolean option status variables 90variable toggle_state1 91variable toggle_state2 92variable toggle_state3 93variable toggle_state4 94variable toggle_state5 95variable toggle_state6 96variable toggle_state7 97variable toggle_state8 98 99\ Array option status variables 100variable cycle_state1 101variable cycle_state2 102variable cycle_state3 103variable cycle_state4 104variable cycle_state5 105variable cycle_state6 106variable cycle_state7 107variable cycle_state8 108 109\ Containers for storing the initial caption text 110create init_text1 255 allot 111create init_text2 255 allot 112create init_text3 255 allot 113create init_text4 255 allot 114create init_text5 255 allot 115create init_text6 255 allot 116create init_text7 255 allot 117create init_text8 255 allot 118 119: +c! ( N C-ADDR/U K -- C-ADDR/U ) 120 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr ) 121 rot + c! ( n c-addr/u k n c-addr -- n c-addr/u ) 122 rot drop ( n c-addr/u -- c-addr/u ) 123; 124 125: menukeyN ( N -- ADDR ) s" menukeyN" 7 +c! evaluate ; 126: init_stateN ( N -- ADDR ) s" init_stateN" 10 +c! evaluate ; 127: toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 +c! evaluate ; 128: cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 +c! evaluate ; 129: init_textN ( N -- C-ADDR ) s" init_textN" 9 +c! evaluate ; 130 131: str_loader_menu_title ( -- C-ADDR/U ) s" loader_menu_title" ; 132: str_loader_menu_timeout_x ( -- C-ADDR/U ) s" loader_menu_timeout_x" ; 133: str_loader_menu_timeout_y ( -- C-ADDR/U ) s" loader_menu_timeout_y" ; 134: str_menu_init ( -- C-ADDR/U ) s" menu_init" ; 135: str_menu_timeout_command ( -- C-ADDR/U ) s" menu_timeout_command" ; 136: str_menu_reboot ( -- C-ADDR/U ) s" menu_reboot" ; 137: str_menu_acpi ( -- C-ADDR/U ) s" menu_acpi" ; 138: str_menu_options ( -- C-ADDR/U ) s" menu_options" ; 139: str_menu_optionstext ( -- C-ADDR/U ) s" menu_optionstext" ; 140 141: str_menu_init[x] ( -- C-ADDR/U ) s" menu_init[x]" ; 142: str_menu_command[x] ( -- C-ADDR/U ) s" menu_command[x]" ; 143: str_menu_caption[x] ( -- C-ADDR/U ) s" menu_caption[x]" ; 144: str_ansi_caption[x] ( -- C-ADDR/U ) s" ansi_caption[x]" ; 145: str_menu_keycode[x] ( -- C-ADDR/U ) s" menu_keycode[x]" ; 146: str_toggled_text[x] ( -- C-ADDR/U ) s" toggled_text[x]" ; 147: str_toggled_ansi[x] ( -- C-ADDR/U ) s" toggled_ansi[x]" ; 148: str_menu_caption[x][y] ( -- C-ADDR/U ) s" menu_caption[x][y]" ; 149: str_ansi_caption[x][y] ( -- C-ADDR/U ) s" ansi_caption[x][y]" ; 150 151: menu_init[x] ( N -- C-ADDR/U ) str_menu_init[x] 10 +c! ; 152: menu_command[x] ( N -- C-ADDR/U ) str_menu_command[x] 13 +c! ; 153: menu_caption[x] ( N -- C-ADDR/U ) str_menu_caption[x] 13 +c! ; 154: ansi_caption[x] ( N -- C-ADDR/U ) str_ansi_caption[x] 13 +c! ; 155: menu_keycode[x] ( N -- C-ADDR/U ) str_menu_keycode[x] 13 +c! ; 156: toggled_text[x] ( N -- C-ADDR/U ) str_toggled_text[x] 13 +c! ; 157: toggled_ansi[x] ( N -- C-ADDR/U ) str_toggled_ansi[x] 13 +c! ; 158: menu_caption[x][y] ( N M -- C-ADDR/U ) str_menu_caption[x][y] 16 +c! 13 +c! ; 159: ansi_caption[x][y] ( N M -- C-ADDR/U ) str_ansi_caption[x][y] 16 +c! 13 +c! ; 160 161: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. 162 s" arch-i386" environment? dup if 163 drop 164 then 165; 166 167\ This function prints a menu item at menuX (row) and menuY (column), returns 168\ the incremental decimal ASCII value associated with the menu item, and 169\ increments the cursor position to the next row for the creation of the next 170\ menu item. This function is called by the menu-create function. You need not 171\ call it directly. 172\ 173: printmenuitem ( menu_item_str -- ascii_keycode ) 174 175 menurow dup @ 1+ swap ! ( increment menurow ) 176 menuidx dup @ 1+ swap ! ( increment menuidx ) 177 178 \ Calculate the menuitem row position 179 menurow @ menuY @ + 180 181 \ Position the cursor at the menuitem position 182 dup menuX @ swap at-xy 183 184 \ Print the value of menuidx 185 loader_color? if 186 ." [1m" ( [22m ) 187 then 188 menuidx @ . 189 loader_color? if 190 ." [37m" ( [39m ) 191 then 192 193 \ Move the cursor forward 1 column 194 dup menuX @ 1+ swap at-xy 195 196 menubllt @ emit \ Print the menu bullet using the emit function 197 198 \ Move the cursor to the 3rd column from the current position 199 \ to allow for a space between the numerical prefix and the 200 \ text caption 201 menuX @ 3 + swap at-xy 202 203 \ Print the menu caption (we expect a string to be on the stack 204 \ prior to invoking this function) 205 type 206 207 \ Here we will add the ASCII decimal of the numerical prefix 208 \ to the stack (decimal ASCII for `1' is 49) as a "return value" 209 menuidx @ 48 + 210; 211 212: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state 213 214 \ ASCII numeral equal to user-selected menu item must be on the stack. 215 \ We do not modify the stack, so the ASCII numeral is left on top. 216 217 dup init_textN c@ 0= if 218 \ NOTE: no need to check toggle_stateN since the first time we 219 \ are called, we will populate init_textN. Further, we don't 220 \ need to test whether menu_caption[x] (ansi_caption[x] when 221 \ loader_color?=1) is available since we would not have been 222 \ called if the caption was NULL. 223 224 \ base name of environment variable 225 dup ( n -- n n ) \ key pressed 226 loader_color? if 227 ansi_caption[x] 228 else 229 menu_caption[x] 230 then 231 getenv dup -1 <> if 232 233 2 pick ( n c-addr/u -- n c-addr/u n ) 234 init_textN ( n c-addr/u n -- n c-addr/u c-addr ) 235 236 \ now we have the buffer c-addr on top 237 \ ( followed by c-addr/u of current caption ) 238 239 \ Copy the current caption into our buffer 240 2dup c! -rot \ store strlen at first byte 241 begin 242 rot 1+ \ bring alt addr to top and increment 243 -rot -rot \ bring buffer addr to top 244 2dup c@ swap c! \ copy current character 245 1+ \ increment buffer addr 246 rot 1- \ bring buffer len to top and decrement 247 dup 0= \ exit loop if buffer len is zero 248 until 249 2drop \ buffer len/addr 250 drop \ alt addr 251 252 else 253 drop 254 then 255 then 256 257 \ Now we are certain to have init_textN populated with the initial 258 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). 259 \ We can now use init_textN as the untoggled caption and 260 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the 261 \ toggled caption and store the appropriate value into menu_caption[x] 262 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll 263 \ negate the toggled state so that we reverse the flow on subsequent 264 \ calls. 265 266 dup toggle_stateN @ 0= if 267 \ state is OFF, toggle to ON 268 269 dup ( n -- n n ) \ key pressed 270 loader_color? if 271 toggled_ansi[x] 272 else 273 toggled_text[x] 274 then 275 getenv dup -1 <> if 276 \ Assign toggled text to menu caption 277 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 278 loader_color? if 279 ansi_caption[x] 280 else 281 menu_caption[x] 282 then 283 setenv 284 else 285 \ No toggled text, keep the same caption 286 drop ( n -1 -- n ) \ getenv cruft 287 then 288 289 true \ new value of toggle state var (to be stored later) 290 else 291 \ state is ON, toggle to OFF 292 293 dup init_textN count ( n -- n c-addr/u ) 294 295 \ Assign init_textN text to menu caption 296 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 297 loader_color? if 298 ansi_caption[x] 299 else 300 menu_caption[x] 301 then 302 setenv 303 304 false \ new value of toggle state var (to be stored below) 305 then 306 307 \ now we'll store the new toggle state (on top of stack) 308 over toggle_stateN ! 309; 310 311: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem 312 313 \ ASCII numeral equal to user-selected menu item must be on the stack. 314 \ We do not modify the stack, so the ASCII numeral is left on top. 315 316 dup cycle_stateN dup @ 1+ \ get value and increment 317 318 \ Before assigning the (incremented) value back to the pointer, 319 \ let's test for the existence of this particular array element. 320 \ If the element exists, we'll store index value and move on. 321 \ Otherwise, we'll loop around to zero and store that. 322 323 dup 48 + ( n addr k -- n addr k k' ) 324 \ duplicate array index and convert to ASCII numeral 325 326 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y) 327 loader_color? if 328 ansi_caption[x][y] 329 else 330 menu_caption[x][y] 331 then 332 ( n addr k n k' -- n addr k c-addr/u ) 333 334 \ Now test for the existence of our incremented array index in the 335 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color 336 \ enabled) as set in loader.rc(5), et. al. 337 338 getenv dup -1 = if 339 \ No caption set for this array index. Loop back to zero. 340 341 drop ( n addr k -1 -- n addr k ) \ getenv cruft 342 drop 0 ( n addr k -- n addr 0 ) \ new value to store later 343 344 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y) 345 loader_color? if 346 ansi_caption[x][y] 347 else 348 menu_caption[x][y] 349 then 350 ( n addr 0 n 48 -- n addr 0 c-addr/u ) 351 getenv dup -1 = if 352 \ This is highly unlikely to occur, but to make 353 \ sure that things move along smoothly, allocate 354 \ a temporary NULL string 355 356 drop ( n addr 0 -1 -- n addr 0 ) \ getenv cruft 357 s" " ( n addr 0 -- n addr 0 c-addr/u ) 358 then 359 then 360 361 \ At this point, we should have the following on the stack (in order, 362 \ from bottom to top): 363 \ 364 \ n - Ascii numeral representing the menu choice (inherited) 365 \ addr - address of our internal cycle_stateN variable 366 \ k - zero-based number we intend to store to the above 367 \ c-addr/u - string value we intend to store to menu_caption[x] 368 \ (or ansi_caption[x] with loader_color enabled) 369 \ 370 \ Let's perform what we need to with the above. 371 372 \ Assign array value text to menu caption 373 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n ) 374 loader_color? if 375 ansi_caption[x] 376 else 377 menu_caption[x] 378 then 379 setenv 380 381 swap ! ( n addr k -- n ) \ update array state variable 382; 383 384: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 385 s" hint.acpi.0.rsdp" getenv 386 dup -1 = if 387 drop false exit 388 then 389 2drop 390 true 391; 392 393: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 394 s" hint.acpi.0.disabled" getenv 395 dup -1 <> if 396 s" 0" compare 0<> if 397 false exit 398 then 399 else 400 drop 401 then 402 true 403; 404 405\ This function prints the appropriate menuitem basename to the stack if an 406\ ACPI option is to be presented to the user, otherwise returns -1. Used 407\ internally by menu-create, you need not (nor should you) call this directly. 408\ 409: acpimenuitem ( -- C-Addr/U | -1 ) 410 411 arch-i386? if 412 acpipresent? if 413 acpienabled? if 414 loader_color? if 415 str_toggled_ansi[x] 416 else 417 str_toggled_text[x] 418 then 419 else 420 loader_color? if 421 str_ansi_caption[x] 422 else 423 str_menu_caption[x] 424 then 425 then 426 else 427 menuidx dup @ 1+ swap ! ( increment menuidx ) 428 -1 429 then 430 else 431 -1 432 then 433; 434 435\ This function creates the list of menu items. This function is called by the 436\ menu-display function. You need not be call it directly. 437\ 438: menu-create ( -- ) 439 440 \ Print the frame caption at (x,y) 441 str_loader_menu_title getenv dup -1 = if 442 drop s" Welcome to FreeBSD" 443 then 444 24 over 2 / - 9 at-xy type 445 446 \ If $menu_init is set, evaluate it (allowing for whole menus to be 447 \ constructed dynamically -- as this function could conceivably set 448 \ the remaining environment variables to construct the menu entirely). 449 \ 450 str_menu_init getenv dup -1 <> if 451 evaluate 452 else 453 drop 454 then 455 456 \ Print our menu options with respective key/variable associations. 457 \ `printmenuitem' ends by adding the decimal ASCII value for the 458 \ numerical prefix to the stack. We store the value left on the stack 459 \ to the key binding variable for later testing against a character 460 \ captured by the `getkey' function. 461 462 \ Note that any menu item beyond 9 will have a numerical prefix on the 463 \ screen consisting of the first digit (ie. 1 for the tenth menu item) 464 \ and the key required to activate that menu item will be the decimal 465 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 466 \ which is misleading and not desirable. 467 \ 468 \ Thus, we do not allow more than 8 configurable items on the menu 469 \ (with "Reboot" as the optional ninth and highest numbered item). 470 471 \ 472 \ Initialize the ACPI option status. 473 \ 474 0 menuacpi ! 475 str_menu_acpi getenv -1 <> if 476 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 477 menuacpi ! 478 arch-i386? if acpipresent? if 479 \ 480 \ Set menu toggle state to active state 481 \ (required by generic toggle_menuitem) 482 \ 483 acpienabled? menuacpi @ toggle_stateN ! 484 then then 485 else 486 drop 487 then 488 then 489 490 \ 491 \ Initialize the menu_options visual separator. 492 \ 493 0 menuoptions ! 494 str_menu_options getenv -1 <> if 495 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 496 menuoptions ! 497 else 498 drop 499 then 500 then 501 502 \ Initialize "Reboot" menu state variable (prevents double-entry) 503 false menurebootadded ! 504 505 menu_start 506 1- menuidx ! \ Initialize the starting index for the menu 507 0 menurow ! \ Initialize the starting position for the menu 508 509 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 510 begin 511 \ If the "Options:" separator, print it. 512 dup menuoptions @ = if 513 \ Optionally add a reboot option to the menu 514 str_menu_reboot getenv -1 <> if 515 drop 516 s" Reboot" printmenuitem menureboot ! 517 true menurebootadded ! 518 then 519 520 menuX @ 521 menurow @ 2 + menurow ! 522 menurow @ menuY @ + 523 at-xy 524 str_menu_optionstext getenv dup -1 <> if 525 type 526 else 527 drop ." Options:" 528 then 529 then 530 531 \ If this is the ACPI menu option, act accordingly. 532 dup menuacpi @ = if 533 dup acpimenuitem ( n -- n n c-addr/u | n n -1 ) 534 dup -1 <> if 535 13 +c! ( n n c-addr/u -- n c-addr/u ) 536 \ replace 'x' with n 537 else 538 swap drop ( n n -1 -- n -1 ) 539 over menu_command[x] unsetenv 540 then 541 else 542 \ make sure we have not already initialized this item 543 dup init_stateN dup @ 0= if 544 1 swap ! 545 546 \ If this menuitem has an initializer, run it 547 dup menu_init[x] 548 getenv dup -1 <> if 549 evaluate 550 else 551 drop 552 then 553 else 554 drop 555 then 556 557 dup 558 loader_color? if 559 ansi_caption[x] 560 else 561 menu_caption[x] 562 then 563 then 564 565 dup -1 <> if 566 \ test for environment variable 567 getenv dup -1 <> if 568 printmenuitem ( c-addr/u -- n ) 569 dup menukeyN ! 570 else 571 drop 572 then 573 else 574 drop 575 then 576 577 1+ dup 56 > \ add 1 to iterator, continue if less than 57 578 until 579 drop \ iterator 580 581 \ Optionally add a reboot option to the menu 582 menurebootadded @ true <> if 583 str_menu_reboot getenv -1 <> if 584 drop \ no need for the value 585 s" Reboot" \ menu caption (required by printmenuitem) 586 587 printmenuitem 588 menureboot ! 589 else 590 0 menureboot ! 591 then 592 then 593; 594 595\ Takes a single integer on the stack and updates the timeout display. The 596\ integer must be between 0 and 9 (we will only update a single digit in the 597\ source message). 598\ 599: menu-timeout-update ( N -- ) 600 601 \ Enforce minimum/maximum 602 dup 9 > if drop 9 then 603 dup 0 < if drop 0 then 604 605 s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u ) 606 607 2 pick 0> if 608 rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII 609 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above 610 611 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 612 type ( c-addr/u -- ) \ print message 613 else 614 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 615 spaces ( n c-addr/u -- n c-addr ) \ erase message 616 2drop ( n c-addr -- ) 617 then 618 619 0 25 at-xy ( position cursor back at bottom-left ) 620; 621 622\ This function blocks program flow (loops forever) until a key is pressed. 623\ The key that was pressed is added to the top of the stack in the form of its 624\ decimal ASCII representation. This function is called by the menu-display 625\ function. You need not call it directly. 626\ 627: getkey ( -- ascii_keycode ) 628 629 begin \ loop forever 630 631 menu_timeout_enabled @ 1 = if 632 ( -- ) 633 seconds ( get current time: -- N ) 634 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 635 636 \ At least 1 second has elapsed since last loop 637 \ so we will decrement our "timeout" (really a 638 \ counter, insuring that we do not proceed too 639 \ fast) and update our timeout display. 640 641 menu_time ! ( update time record: N -- ) 642 menu_timeout @ ( "time" remaining: -- N ) 643 dup 0> if ( greater than 0?: N N 0 -- N ) 644 1- ( decrement counter: N -- N ) 645 dup menu_timeout ! 646 ( re-assign: N N Addr -- N ) 647 then 648 ( -- N ) 649 650 dup 0= swap 0< or if ( N <= 0?: N N -- ) 651 \ halt the timer 652 0 menu_timeout ! ( 0 Addr -- ) 653 0 menu_timeout_enabled ! ( 0 Addr -- ) 654 then 655 656 \ update the timer display ( N -- ) 657 menu_timeout @ menu-timeout-update 658 659 menu_timeout @ 0= if 660 \ We've reached the end of the timeout 661 \ (user did not cancel by pressing ANY 662 \ key) 663 664 str_menu_timeout_command getenv dup 665 -1 = if 666 drop \ clean-up 667 else 668 evaluate 669 then 670 then 671 672 else ( -- N ) 673 \ No [detectable] time has elapsed (in seconds) 674 drop ( N -- ) 675 then 676 ( -- ) 677 then 678 679 key? if \ Was a key pressed? (see loader(8)) 680 681 \ An actual key was pressed (if the timeout is running, 682 \ kill it regardless of which key was pressed) 683 menu_timeout @ 0<> if 684 0 menu_timeout ! 685 0 menu_timeout_enabled ! 686 687 \ clear screen of timeout message 688 0 menu-timeout-update 689 then 690 691 \ get the key that was pressed and exit (if we 692 \ get a non-zero ASCII code) 693 key dup 0<> if 694 exit 695 else 696 drop 697 then 698 then 699 50 ms \ sleep for 50 milliseconds (see loader(8)) 700 701 again 702; 703 704: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1. 705 706 \ Clear the screen area associated with the interactive menu 707 menuX @ menuY @ 708 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 709 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 710 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 711 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 712 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 713 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 714 2drop 715 716 \ Reset the starting index and position for the menu 717 menu_start 1- menuidx ! 718 0 menurow ! 719; 720 721\ Erase and redraw the menu. Useful if you change a caption and want to 722\ update the menu to reflect the new value. 723\ 724: menu-redraw ( -- ) 725 menu-erase 726 menu-create 727; 728 729\ This function initializes the menu. Call this from your `loader.rc' file 730\ before calling any other menu-related functions. 731\ 732: menu-init ( -- ) 733 menu_start 734 1- menuidx ! \ Initialize the starting index for the menu 735 0 menurow ! \ Initialize the starting position for the menu 736 42 13 2 9 box \ Draw frame (w,h,x,y) 737 0 25 at-xy \ Move cursor to the bottom for output 738; 739 740\ Main function. Call this from your `loader.rc' file. 741\ 742: menu-display ( -- ) 743 744 0 menu_timeout_enabled ! \ start with automatic timeout disabled 745 746 \ check indication that automatic execution after delay is requested 747 str_menu_timeout_command getenv -1 <> if ( Addr C -1 -- | Addr ) 748 drop ( just testing existence right now: Addr -- ) 749 750 \ initialize state variables 751 seconds menu_time ! ( store the time we started ) 752 1 menu_timeout_enabled ! ( enable automatic timeout ) 753 754 \ read custom time-duration (if set) 755 s" autoboot_delay" getenv dup -1 = if 756 drop \ no custom duration (remove dup'd bunk -1) 757 menu_timeout_default \ use default setting 758 else 759 2dup ?number 0= if ( if not a number ) 760 \ disable timeout if "NO", else use default 761 s" NO" compare-insensitive 0= if 762 0 menu_timeout_enabled ! 763 0 ( assigned to menu_timeout below ) 764 else 765 menu_timeout_default 766 then 767 else 768 -rot 2drop 769 770 \ boot immediately if less than zero 771 dup 0< if 772 drop 773 menu-create 774 0 25 at-xy 775 0 boot 776 then 777 then 778 then 779 menu_timeout ! ( store value on stack from above ) 780 781 menu_timeout_enabled @ 1 = if 782 \ read custom column position (if set) 783 str_loader_menu_timeout_x getenv dup -1 = if 784 drop \ no custom column position 785 menu_timeout_default_x \ use default setting 786 else 787 \ make sure custom position is a number 788 ?number 0= if 789 menu_timeout_default_x \ or use default 790 then 791 then 792 menu_timeout_x ! ( store value on stack from above ) 793 794 \ read custom row position (if set) 795 str_loader_menu_timeout_y getenv dup -1 = if 796 drop \ no custom row position 797 menu_timeout_default_y \ use default setting 798 else 799 \ make sure custom position is a number 800 ?number 0= if 801 menu_timeout_default_y \ or use default 802 then 803 then 804 menu_timeout_y ! ( store value on stack from above ) 805 then 806 then 807 808 menu-create 809 810 begin \ Loop forever 811 812 0 25 at-xy \ Move cursor to the bottom for output 813 getkey \ Block here, waiting for a key to be pressed 814 815 dup -1 = if 816 drop exit \ Caught abort (abnormal return) 817 then 818 819 \ Boot if the user pressed Enter/Ctrl-M (13) or 820 \ Ctrl-Enter/Ctrl-J (10) 821 dup over 13 = swap 10 = or if 822 drop ( no longer needed ) 823 s" boot" evaluate 824 exit ( pedantic; never reached ) 825 then 826 827 dup menureboot @ = if 0 reboot then 828 829 \ Evaluate the decimal ASCII value against known menu item 830 \ key associations and act accordingly 831 832 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 833 begin 834 dup menukeyN @ 835 rot tuck = if 836 837 \ Adjust for missing ACPI menuitem on non-i386 838 arch-i386? true <> menuacpi @ 0<> and if 839 menuacpi @ over 2dup < -rot = or 840 over 58 < and if 841 ( key >= menuacpi && key < 58: N -- N ) 842 1+ 843 then 844 then 845 846 \ Test for the environment variable 847 dup menu_command[x] 848 getenv dup -1 <> if 849 \ Execute the stored procedure 850 evaluate 851 852 \ We expect there to be a non-zero 853 \ value left on the stack after 854 \ executing the stored procedure. 855 \ If so, continue to run, else exit. 856 857 0= if 858 drop \ key pressed 859 drop \ loop iterator 860 exit 861 else 862 swap \ need iterator on top 863 then 864 then 865 866 \ Re-adjust for missing ACPI menuitem 867 arch-i386? true <> menuacpi @ 0<> and if 868 swap 869 menuacpi @ 1+ over 2dup < -rot = or 870 over 59 < and if 871 1- 872 then 873 swap 874 then 875 else 876 swap \ need iterator on top 877 then 878 879 \ 880 \ Check for menu keycode shortcut(s) 881 \ 882 dup menu_keycode[x] 883 getenv dup -1 = if 884 drop 885 else 886 ?number 0<> if 887 rot tuck = if 888 swap 889 dup menu_command[x] 890 getenv dup -1 <> if 891 evaluate 892 0= if 893 2drop 894 exit 895 then 896 else 897 drop 898 then 899 else 900 swap 901 then 902 then 903 then 904 905 1+ dup 56 > \ increment iterator 906 \ continue if less than 57 907 until 908 drop \ loop iterator 909 drop \ key pressed 910 911 again \ Non-operational key was pressed; repeat 912; 913 914\ This function unsets all the possible environment variables associated with 915\ creating the interactive menu. 916\ 917: menu-unset ( -- ) 918 919 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 920 begin 921 dup menu_init[x] unsetenv \ menu initializer 922 dup menu_command[x] unsetenv \ menu command 923 dup menu_caption[x] unsetenv \ menu caption 924 dup ansi_caption[x] unsetenv \ ANSI caption 925 dup menu_keycode[x] unsetenv \ menu keycode 926 dup toggled_text[x] unsetenv \ toggle_menuitem caption 927 dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption 928 929 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9') 930 begin 931 \ cycle_menuitem caption and ANSI caption 932 2dup menu_caption[x][y] unsetenv 933 2dup ansi_caption[x][y] unsetenv 934 1+ dup 57 > 935 until 936 drop \ inner iterator 937 938 0 over menukeyN ! \ used by menu-create, menu-display 939 0 over init_stateN ! \ used by menu-create 940 0 over toggle_stateN ! \ used by toggle_menuitem 941 0 over init_textN c! \ used by toggle_menuitem 942 0 over cycle_stateN ! \ used by cycle_menuitem 943 944 1+ dup 56 > \ increment, continue if less than 57 945 until 946 drop \ iterator 947 948 str_menu_timeout_command unsetenv \ menu timeout command 949 str_menu_reboot unsetenv \ Reboot menu option flag 950 str_menu_acpi unsetenv \ ACPI menu option flag 951 str_menu_options unsetenv \ Options separator flag 952 str_menu_optionstext unsetenv \ separator display text 953 str_menu_init unsetenv \ menu initializer 954 955 0 menureboot ! 956 0 menuacpi ! 957 0 menuoptions ! 958; 959 960\ This function both unsets menu variables and visually erases the menu area 961\ in-preparation for another menu. 962\ 963: menu-clear ( -- ) 964 menu-unset 965 menu-erase 966; 967 968\ Assign configuration values 969bullet menubllt ! 97010 menuY ! 9715 menuX ! 972 973\ Initialize our menu initialization state variables 9740 init_state1 ! 9750 init_state2 ! 9760 init_state3 ! 9770 init_state4 ! 9780 init_state5 ! 9790 init_state6 ! 9800 init_state7 ! 9810 init_state8 ! 982 983\ Initialize our boolean state variables 9840 toggle_state1 ! 9850 toggle_state2 ! 9860 toggle_state3 ! 9870 toggle_state4 ! 9880 toggle_state5 ! 9890 toggle_state6 ! 9900 toggle_state7 ! 9910 toggle_state8 ! 992 993\ Initialize our array state variables 9940 cycle_state1 ! 9950 cycle_state2 ! 9960 cycle_state3 ! 9970 cycle_state4 ! 9980 cycle_state5 ! 9990 cycle_state6 ! 10000 cycle_state7 ! 10010 cycle_state8 ! 1002 1003\ Initialize string containers 10040 init_text1 c! 10050 init_text2 c! 10060 init_text3 c! 10070 init_text4 c! 10080 init_text5 c! 10090 init_text6 c! 10100 init_text7 c! 10110 init_text8 c! 1012