menu.4th revision 241523
1\ Copyright (c) 2003 Scott Long <scottl@freebsd.org> 2\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> 3\ Copyright (c) 2006-2012 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 241523 2012-10-14 06:52:49Z 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: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. 120 s" arch-i386" environment? dup if 121 drop 122 then 123; 124 125\ This function prints a menu item at menuX (row) and menuY (column), returns 126\ the incremental decimal ASCII value associated with the menu item, and 127\ increments the cursor position to the next row for the creation of the next 128\ menu item. This function is called by the menu-create function. You need not 129\ call it directly. 130\ 131: printmenuitem ( menu_item_str -- ascii_keycode ) 132 133 menurow dup @ 1+ swap ! ( increment menurow ) 134 menuidx dup @ 1+ swap ! ( increment menuidx ) 135 136 \ Calculate the menuitem row position 137 menurow @ menuY @ + 138 139 \ Position the cursor at the menuitem position 140 dup menuX @ swap at-xy 141 142 \ Print the value of menuidx 143 loader_color? if 144 ." [1m" ( [22m ) 145 then 146 menuidx @ . 147 loader_color? if 148 ." [37m" ( [39m ) 149 then 150 151 \ Move the cursor forward 1 column 152 dup menuX @ 1+ swap at-xy 153 154 menubllt @ emit \ Print the menu bullet using the emit function 155 156 \ Move the cursor to the 3rd column from the current position 157 \ to allow for a space between the numerical prefix and the 158 \ text caption 159 menuX @ 3 + swap at-xy 160 161 \ Print the menu caption (we expect a string to be on the stack 162 \ prior to invoking this function) 163 type 164 165 \ Here we will add the ASCII decimal of the numerical prefix 166 \ to the stack (decimal ASCII for `1' is 49) as a "return value" 167 menuidx @ 48 + 168; 169 170: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state 171 172 \ ASCII numeral equal to user-selected menu item must be on the stack. 173 \ We do not modify the stack, so the ASCII numeral is left on top. 174 175 s" init_textN" \ base name of buffer 176 -rot 2dup 9 + c! rot \ replace 'N' with ASCII num 177 178 evaluate c@ 0= if 179 \ NOTE: no need to check toggle_stateN since the first time we 180 \ are called, we will populate init_textN. Further, we don't 181 \ need to test whether menu_caption[x] (ansi_caption[x] when 182 \ loader_color=1) is available since we would not have been 183 \ called if the caption was NULL. 184 185 \ base name of environment variable 186 loader_color? if 187 s" ansi_caption[x]" 188 else 189 s" menu_caption[x]" 190 then 191 -rot 2dup 13 + c! rot \ replace 'x' with ASCII numeral 192 193 getenv dup -1 <> if 194 195 s" init_textN" \ base name of buffer 196 4 pick \ copy ASCII num to top 197 rot tuck 9 + c! swap \ replace 'N' with ASCII num 198 evaluate 199 200 \ now we have the buffer c-addr on top 201 \ ( followed by c-addr/u of current caption ) 202 203 \ Copy the current caption into our buffer 204 2dup c! -rot \ store strlen at first byte 205 begin 206 rot 1+ \ bring alt addr to top and increment 207 -rot -rot \ bring buffer addr to top 208 2dup c@ swap c! \ copy current character 209 1+ \ increment buffer addr 210 rot 1- \ bring buffer len to top and decrement 211 dup 0= \ exit loop if buffer len is zero 212 until 213 2drop \ buffer len/addr 214 drop \ alt addr 215 216 else 217 drop 218 then 219 then 220 221 \ Now we are certain to have init_textN populated with the initial 222 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). 223 \ We can now use init_textN as the untoggled caption and 224 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the 225 \ toggled caption and store the appropriate value into menu_caption[x] 226 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll 227 \ negate the toggled state so that we reverse the flow on subsequent 228 \ calls. 229 230 s" toggle_stateN @" \ base name of toggle state var 231 -rot 2dup 12 + c! rot \ replace 'N' with ASCII numeral 232 233 evaluate 0= if 234 \ state is OFF, toggle to ON 235 236 \ base name of toggled text var 237 loader_color? if 238 s" toggled_ansi[x]" 239 else 240 s" toggled_text[x]" 241 then 242 -rot 2dup 13 + c! rot \ replace 'x' with ASCII num 243 244 getenv dup -1 <> if 245 \ Assign toggled text to menu caption 246 247 \ base name of caption var 248 loader_color? if 249 s" ansi_caption[x]" 250 else 251 s" menu_caption[x]" 252 then 253 4 pick \ copy ASCII num to top 254 rot tuck 13 + c! swap \ replace 'x' with ASCII num 255 256 setenv \ set new caption 257 else 258 \ No toggled text, keep the same caption 259 260 drop 261 then 262 263 true \ new value of toggle state var (to be stored later) 264 else 265 \ state is ON, toggle to OFF 266 267 s" init_textN" \ base name of initial text buffer 268 -rot 2dup 9 + c! rot \ replace 'N' with ASCII numeral 269 evaluate \ convert string to c-addr 270 count \ convert c-addr to c-addr/u 271 272 \ base name of caption var 273 loader_color? if 274 s" ansi_caption[x]" 275 else 276 s" menu_caption[x]" 277 then 278 4 pick \ copy ASCII num to top 279 rot tuck 13 + c! swap \ replace 'x' with ASCII numeral 280 281 setenv \ set new caption 282 false \ new value of toggle state var (to be stored below) 283 then 284 285 \ now we'll store the new toggle state (on top of stack) 286 s" toggle_stateN" \ base name of toggle state var 287 3 pick \ copy ASCII numeral to top 288 rot tuck 12 + c! swap \ replace 'N' with ASCII numeral 289 evaluate \ convert string to addr 290 ! \ store new value 291; 292 293: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem 294 295 \ ASCII numeral equal to user-selected menu item must be on the stack. 296 \ We do not modify the stack, so the ASCII numeral is left on top. 297 298 s" cycle_stateN" \ base name of array state var 299 -rot 2dup 11 + c! rot \ replace 'N' with ASCII numeral 300 301 evaluate \ we now have a pointer to the proper variable 302 dup @ \ resolve the pointer (but leave it on the stack) 303 1+ \ increment the value 304 305 \ Before assigning the (incremented) value back to the pointer, 306 \ let's test for the existence of this particular array element. 307 \ If the element exists, we'll store index value and move on. 308 \ Otherwise, we'll loop around to zero and store that. 309 310 dup 48 + \ duplicate Array index and convert to ASCII numeral 311 312 \ base name of array caption text 313 loader_color? if 314 s" ansi_caption[x][y]" 315 else 316 s" menu_caption[x][y]" 317 then 318 -rot tuck 16 + c! swap \ replace 'y' with Array index 319 4 pick rot tuck 13 + c! swap \ replace 'x' with menu choice 320 321 \ Now test for the existence of our incremented array index in the 322 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color 323 \ enabled) as set in loader.rc(5), et. al. 324 325 getenv dup -1 = if 326 \ No caption set for this array index. Loop back to zero. 327 328 drop ( getenv cruft ) 329 drop ( incremented array index ) 330 0 ( new array index that will be stored later ) 331 332 \ base name of caption var 333 loader_color? if 334 s" ansi_caption[x][0]" 335 else 336 s" menu_caption[x][0]" 337 then 338 4 pick rot tuck 13 + c! swap \ replace 'x' with menu choice 339 340 getenv dup -1 = if 341 \ This is highly unlikely to occur, but to make 342 \ sure that things move along smoothly, allocate 343 \ a temporary NULL string 344 345 s" " 346 then 347 then 348 349 \ At this point, we should have the following on the stack (in order, 350 \ from bottom to top): 351 \ 352 \ N - Ascii numeral representing the menu choice (inherited) 353 \ Addr - address of our internal cycle_stateN variable 354 \ N - zero-based number we intend to store to the above 355 \ C-Addr - string value we intend to store to menu_caption[x] 356 \ (or ansi_caption[x] with loader_color enabled) 357 \ 358 \ Let's perform what we need to with the above. 359 360 \ base name of menuitem caption var 361 loader_color? if 362 s" ansi_caption[x]" 363 else 364 s" menu_caption[x]" 365 then 366 6 pick rot tuck 13 + c! swap \ replace 'x' with menu choice 367 setenv \ set the new caption 368 369 swap ! \ update array state variable 370; 371 372: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 373 s" hint.acpi.0.rsdp" getenv 374 dup -1 = if 375 drop false exit 376 then 377 2drop 378 true 379; 380 381: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 382 s" hint.acpi.0.disabled" getenv 383 dup -1 <> if 384 s" 0" compare 0<> if 385 false exit 386 then 387 else 388 drop 389 then 390 true 391; 392 393\ This function prints the appropriate menuitem basename to the stack if an 394\ ACPI option is to be presented to the user, otherwise returns -1. Used 395\ internally by menu-create, you need not (nor should you) call this directly. 396\ 397: acpimenuitem ( -- C-Addr/U | -1 ) 398 399 arch-i386? if 400 acpipresent? if 401 acpienabled? if 402 loader_color? if 403 s" toggled_ansi[x]" 404 else 405 s" toggled_text[x]" 406 then 407 else 408 loader_color? if 409 s" ansi_caption[x]" 410 else 411 s" menu_caption[x]" 412 then 413 then 414 else 415 menuidx dup @ 1+ swap ! ( increment menuidx ) 416 -1 417 then 418 else 419 -1 420 then 421; 422 423\ This function creates the list of menu items. This function is called by the 424\ menu-display function. You need not be call it directly. 425\ 426: menu-create ( -- ) 427 428 \ Print the frame caption at (x,y) 429 s" loader_menu_title" getenv dup -1 = if 430 drop s" Welcome to FreeBSD" 431 then 432 24 over 2 / - 9 at-xy type 433 434 \ If $menu_init is set, evaluate it (allowing for whole menus to be 435 \ constructed dynamically -- as this function could conceivably set 436 \ the remaining environment variables to construct the menu entirely). 437 \ 438 s" menu_init" getenv dup -1 <> if 439 evaluate 440 else 441 drop 442 then 443 444 \ Print our menu options with respective key/variable associations. 445 \ `printmenuitem' ends by adding the decimal ASCII value for the 446 \ numerical prefix to the stack. We store the value left on the stack 447 \ to the key binding variable for later testing against a character 448 \ captured by the `getkey' function. 449 450 \ Note that any menu item beyond 9 will have a numerical prefix on the 451 \ screen consisting of the first digit (ie. 1 for the tenth menu item) 452 \ and the key required to activate that menu item will be the decimal 453 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 454 \ which is misleading and not desirable. 455 \ 456 \ Thus, we do not allow more than 8 configurable items on the menu 457 \ (with "Reboot" as the optional ninth and highest numbered item). 458 459 \ 460 \ Initialize the ACPI option status. 461 \ 462 0 menuacpi ! 463 s" menu_acpi" getenv -1 <> if 464 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 465 menuacpi ! 466 arch-i386? if acpipresent? if 467 \ 468 \ Set menu toggle state to active state 469 \ (required by generic toggle_menuitem) 470 \ 471 menuacpi @ 472 s" acpienabled? toggle_stateN !" 473 -rot tuck 25 + c! swap 474 evaluate 475 then then 476 else 477 drop 478 then 479 then 480 481 \ 482 \ Initialize the menu_options visual separator. 483 \ 484 0 menuoptions ! 485 s" menu_options" getenv -1 <> if 486 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 487 menuoptions ! 488 else 489 drop 490 then 491 then 492 493 \ Initialize "Reboot" menu state variable (prevents double-entry) 494 false menurebootadded ! 495 496 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 497 begin 498 \ If the "Options:" separator, print it. 499 dup menuoptions @ = if 500 \ Optionally add a reboot option to the menu 501 s" menu_reboot" getenv -1 <> if 502 drop 503 s" Reboot" printmenuitem menureboot ! 504 true menurebootadded ! 505 then 506 507 menuX @ 508 menurow @ 2 + menurow ! 509 menurow @ menuY @ + 510 at-xy 511 s" menu_optionstext" getenv dup -1 <> if 512 type 513 else 514 drop ." Options:" 515 then 516 then 517 518 \ If this is the ACPI menu option, act accordingly. 519 dup menuacpi @ = if 520 acpimenuitem ( -- C-Addr/U | -1 ) 521 else 522 \ make sure we have not already initialized this item 523 s" init_stateN" 524 -rot 2dup 10 + c! rot \ repace 'N' 525 evaluate dup @ 0= if 526 1 swap ! 527 528 \ If this menuitem has an initializer, run it 529 s" menu_init[x]" 530 -rot 2dup 10 + c! rot \ replace 'x' 531 getenv dup -1 <> if 532 evaluate 533 else 534 drop 535 then 536 else 537 drop 538 then 539 540 loader_color? if 541 s" ansi_caption[x]" 542 else 543 s" menu_caption[x]" 544 then 545 then 546 547 ( C-Addr/U | -1 ) 548 dup -1 <> if 549 \ replace 'x' with current iteration 550 -rot 2dup 13 + c! rot 551 552 \ test for environment variable 553 getenv dup -1 <> if 554 printmenuitem ( C-Addr/U -- N ) 555 556 s" menukeyN !" \ generate cmd to store result 557 -rot 2dup 7 + c! rot 558 559 evaluate 560 else 561 drop 562 then 563 else 564 drop 565 566 s" menu_command[x]" 567 -rot 2dup 13 + c! rot ( replace 'x' ) 568 unsetenv 569 then 570 571 1+ dup 56 > \ add 1 to iterator, continue if less than 57 572 until 573 drop \ iterator 574 575 \ Optionally add a reboot option to the menu 576 menurebootadded @ true <> if 577 s" menu_reboot" getenv -1 <> if 578 drop \ no need for the value 579 s" Reboot" \ menu caption (required by printmenuitem) 580 581 printmenuitem 582 menureboot ! 583 else 584 0 menureboot ! 585 then 586 then 587; 588 589\ Takes a single integer on the stack and updates the timeout display. The 590\ integer must be between 0 and 9 (we will only update a single digit in the 591\ source message). 592\ 593: menu-timeout-update ( N -- ) 594 595 dup 9 > if ( N N 9 -- N ) 596 drop ( N -- ) 597 9 ( maximum: -- N ) 598 then 599 600 dup 0 < if ( N N 0 -- N ) 601 drop ( N -- ) 602 0 ( minimum: -- N ) 603 then 604 605 48 + ( convert single-digit numeral to ASCII: N 48 -- N ) 606 607 s" Autoboot in N seconds. [Space] to pause" ( N -- N Addr C ) 608 609 2 pick 48 - 0> if ( N Addr C N 48 -- N Addr C ) 610 611 \ Modify 'N' (Addr+12) above to reflect time-left 612 613 -rot ( N Addr C -- C N Addr ) 614 tuck ( C N Addr -- C Addr N Addr ) 615 12 + ( C Addr N Addr -- C Addr N Addr2 ) 616 c! ( C Addr N Addr2 -- C Addr ) 617 swap ( C Addr -- Addr C ) 618 619 menu_timeout_x @ 620 menu_timeout_y @ 621 at-xy ( position cursor: Addr C N N -- Addr C ) 622 623 type ( print message: Addr C -- ) 624 625 else ( N Addr C N -- N Addr C ) 626 627 menu_timeout_x @ 628 menu_timeout_y @ 629 at-xy ( position cursor: N Addr C N N -- N Addr C ) 630 631 spaces ( erase message: N Addr C -- N Addr ) 632 2drop ( N Addr -- ) 633 634 then 635 636 0 25 at-xy ( position cursor back at bottom-left ) 637; 638 639\ This function blocks program flow (loops forever) until a key is pressed. 640\ The key that was pressed is added to the top of the stack in the form of its 641\ decimal ASCII representation. This function is called by the menu-display 642\ function. You need not call it directly. 643\ 644: getkey ( -- ascii_keycode ) 645 646 begin \ loop forever 647 648 menu_timeout_enabled @ 1 = if 649 ( -- ) 650 seconds ( get current time: -- N ) 651 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 652 653 \ At least 1 second has elapsed since last loop 654 \ so we will decrement our "timeout" (really a 655 \ counter, insuring that we do not proceed too 656 \ fast) and update our timeout display. 657 658 menu_time ! ( update time record: N -- ) 659 menu_timeout @ ( "time" remaining: -- N ) 660 dup 0> if ( greater than 0?: N N 0 -- N ) 661 1- ( decrement counter: N -- N ) 662 dup menu_timeout ! 663 ( re-assign: N N Addr -- N ) 664 then 665 ( -- N ) 666 667 dup 0= swap 0< or if ( N <= 0?: N N -- ) 668 \ halt the timer 669 0 menu_timeout ! ( 0 Addr -- ) 670 0 menu_timeout_enabled ! ( 0 Addr -- ) 671 then 672 673 \ update the timer display ( N -- ) 674 menu_timeout @ menu-timeout-update 675 676 menu_timeout @ 0= if 677 \ We've reached the end of the timeout 678 \ (user did not cancel by pressing ANY 679 \ key) 680 681 s" menu_timeout_command" getenv dup 682 -1 = if 683 drop \ clean-up 684 else 685 evaluate 686 then 687 then 688 689 else ( -- N ) 690 \ No [detectable] time has elapsed (in seconds) 691 drop ( N -- ) 692 then 693 ( -- ) 694 then 695 696 key? if \ Was a key pressed? (see loader(8)) 697 698 \ An actual key was pressed (if the timeout is running, 699 \ kill it regardless of which key was pressed) 700 menu_timeout @ 0<> if 701 0 menu_timeout ! 702 0 menu_timeout_enabled ! 703 704 \ clear screen of timeout message 705 0 menu-timeout-update 706 then 707 708 \ get the key that was pressed and exit (if we 709 \ get a non-zero ASCII code) 710 key dup 0<> if 711 exit 712 else 713 drop 714 then 715 then 716 50 ms \ sleep for 50 milliseconds (see loader(8)) 717 718 again 719; 720 721: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1. 722 723 \ Clear the screen area associated with the interactive menu 724 menuX @ menuY @ 725 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 726 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 727 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 728 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 729 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 730 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 731 2drop 732 733 \ Reset the starting index and position for the menu 734 menu_start 1- menuidx ! 735 0 menurow ! 736; 737 738\ Erase and redraw the menu. Useful if you change a caption and want to 739\ update the menu to reflect the new value. 740\ 741: menu-redraw ( -- ) 742 menu-erase 743 menu-create 744; 745 746\ This function initializes the menu. Call this from your `loader.rc' file 747\ before calling any other menu-related functions. 748\ 749: menu-init ( -- ) 750 menu_start 751 1- menuidx ! \ Initialize the starting index for the menu 752 0 menurow ! \ Initialize the starting position for the menu 753 42 13 2 9 box \ Draw frame (w,h,x,y) 754 0 25 at-xy \ Move cursor to the bottom for output 755; 756 757\ Main function. Call this from your `loader.rc' file. 758\ 759: menu-display ( -- ) 760 761 0 menu_timeout_enabled ! \ start with automatic timeout disabled 762 763 \ check indication that automatic execution after delay is requested 764 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 765 drop ( just testing existence right now: Addr -- ) 766 767 \ initialize state variables 768 seconds menu_time ! ( store the time we started ) 769 1 menu_timeout_enabled ! ( enable automatic timeout ) 770 771 \ read custom time-duration (if set) 772 s" autoboot_delay" getenv dup -1 = if 773 drop \ no custom duration (remove dup'd bunk -1) 774 menu_timeout_default \ use default setting 775 else 776 2dup ?number 0= if ( if not a number ) 777 \ disable timeout if "NO", else use default 778 s" NO" compare-insensitive 0= if 779 0 menu_timeout_enabled ! 780 0 ( assigned to menu_timeout below ) 781 else 782 menu_timeout_default 783 then 784 else 785 -rot 2drop 786 787 \ boot immediately if less than zero 788 dup 0< if 789 drop 790 menu-create 791 0 25 at-xy 792 0 boot 793 then 794 then 795 then 796 menu_timeout ! ( store value on stack from above ) 797 798 menu_timeout_enabled @ 1 = if 799 \ read custom column position (if set) 800 s" loader_menu_timeout_x" getenv dup -1 = if 801 drop \ no custom column position 802 menu_timeout_default_x \ use default setting 803 else 804 \ make sure custom position is a number 805 ?number 0= if 806 menu_timeout_default_x \ or use default 807 then 808 then 809 menu_timeout_x ! ( store value on stack from above ) 810 811 \ read custom row position (if set) 812 s" loader_menu_timeout_y" getenv dup -1 = if 813 drop \ no custom row position 814 menu_timeout_default_y \ use default setting 815 else 816 \ make sure custom position is a number 817 ?number 0= if 818 menu_timeout_default_y \ or use default 819 then 820 then 821 menu_timeout_y ! ( store value on stack from above ) 822 then 823 then 824 825 menu-create 826 827 begin \ Loop forever 828 829 0 25 at-xy \ Move cursor to the bottom for output 830 getkey \ Block here, waiting for a key to be pressed 831 832 dup -1 = if 833 drop exit \ Caught abort (abnormal return) 834 then 835 836 \ Boot if the user pressed Enter/Ctrl-M (13) or 837 \ Ctrl-Enter/Ctrl-J (10) 838 dup over 13 = swap 10 = or if 839 drop ( no longer needed ) 840 s" boot" evaluate 841 exit ( pedantic; never reached ) 842 then 843 844 \ Evaluate the decimal ASCII value against known menu item 845 \ key associations and act accordingly 846 847 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 848 begin 849 s" menukeyN @" 850 851 \ replace 'N' with current iteration 852 -rot 2dup 7 + c! rot 853 854 evaluate rot tuck = if 855 856 \ Adjust for missing ACPI menuitem on non-i386 857 arch-i386? true <> menuacpi @ 0<> and if 858 menuacpi @ over 2dup < -rot = or 859 over 58 < and if 860 ( key >= menuacpi && key < 58: N -- N ) 861 1+ 862 then 863 then 864 865 \ base env name for the value (x is a number) 866 s" menu_command[x]" 867 868 \ Copy ASCII number to string at offset 13 869 -rot 2dup 13 + c! rot 870 871 \ Test for the environment variable 872 getenv dup -1 <> if 873 \ Execute the stored procedure 874 evaluate 875 876 \ We expect there to be a non-zero 877 \ value left on the stack after 878 \ executing the stored procedure. 879 \ If so, continue to run, else exit. 880 881 0= if 882 drop \ key pressed 883 drop \ loop iterator 884 exit 885 else 886 swap \ need iterator on top 887 then 888 then 889 890 \ Re-adjust for missing ACPI menuitem 891 arch-i386? true <> menuacpi @ 0<> and if 892 swap 893 menuacpi @ 1+ over 2dup < -rot = or 894 over 59 < and if 895 1- 896 then 897 swap 898 then 899 else 900 swap \ need iterator on top 901 then 902 903 \ 904 \ Check for menu keycode shortcut(s) 905 \ 906 s" menu_keycode[x]" 907 -rot 2dup 13 + c! rot 908 getenv dup -1 = if 909 drop 910 else 911 ?number 0<> if 912 rot tuck = if 913 swap 914 s" menu_command[x]" 915 -rot 2dup 13 + c! rot 916 getenv dup -1 <> if 917 evaluate 918 0= if 919 2drop 920 exit 921 then 922 else 923 drop 924 then 925 else 926 swap 927 then 928 then 929 then 930 931 1+ dup 56 > \ increment iterator 932 \ continue if less than 57 933 until 934 drop \ loop iterator 935 936 menureboot @ = if 0 reboot then 937 938 again \ Non-operational key was pressed; repeat 939; 940 941\ This function unsets all the possible environment variables associated with 942\ creating the interactive menu. 943\ 944: menu-unset ( -- ) 945 946 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 947 begin 948 \ Unset variables in-order of appearance in menu.4th(8) 949 950 s" menu_caption[x]" \ basename for caption variable 951 -rot 2dup 13 + c! rot \ replace 'x' with current iteration 952 unsetenv \ not erroneous to unset unknown var 953 954 s" menu_command[x]" \ command basename 955 -rot 2dup 13 + c! rot \ replace 'x' 956 unsetenv 957 958 s" menu_init[x]" \ initializer basename 959 -rot 2dup 10 + c! rot \ replace 'x' 960 unsetenv 961 962 s" menu_keycode[x]" \ keycode basename 963 -rot 2dup 13 + c! rot \ replace 'x' 964 unsetenv 965 966 s" ansi_caption[x]" \ ANSI caption basename 967 -rot 2dup 13 + c! rot \ replace 'x' 968 unsetenv 969 970 s" toggled_text[x]" \ toggle_menuitem caption basename 971 -rot 2dup 13 + c! rot \ replace 'x' 972 unsetenv 973 974 s" toggled_ansi[x]" \ toggle_menuitem ANSI caption basename 975 -rot 2dup 13 + c! rot \ replace 'x' 976 unsetenv 977 978 s" menu_caption[x][y]" \ cycle_menuitem caption 979 -rot 2dup 13 + c! rot \ replace 'x' 980 48 -rot 981 begin 982 16 2over rot + c! \ replace 'y' 983 2dup unsetenv 984 985 rot 1+ dup 57 > 2swap rot 986 until 987 2drop drop 988 989 s" ansi_caption[x][y]" \ cycle_menuitem ANSI caption 990 -rot 2dup 13 + c! rot \ replace 'x' 991 48 -rot 992 begin 993 16 2over rot + c! \ replace 'y' 994 2dup unsetenv 995 996 rot 1+ dup 57 > 2swap rot 997 until 998 2drop drop 999 1000 s" 0 menukeyN !" \ basename for key association var 1001 -rot 2dup 9 + c! rot \ replace 'N' with current iteration 1002 evaluate \ assign zero (0) to key assoc. var 1003 1004 s" 0 init_stateN !" \ used by menu-create 1005 -rot 2dup 12 + c! rot \ replace 'N' 1006 evaluate 1007 1008 1+ dup 56 > \ increment, continue if less than 57 1009 until 1010 drop \ iterator 1011 1012 \ unset the timeout command 1013 s" menu_timeout_command" unsetenv 1014 1015 \ clear the "Reboot" menu option flag 1016 s" menu_reboot" unsetenv 1017 0 menureboot ! 1018 1019 \ clear the ACPI menu option flag 1020 s" menu_acpi" unsetenv 1021 0 menuacpi ! 1022 1023 \ clear the "Options" menu separator flag 1024 s" menu_options" unsetenv 1025 s" menu_optionstext" unsetenv 1026 0 menuoptions ! 1027 1028 \ clear the menu initializer 1029 s" menu_init" unsetenv 1030; 1031 1032\ This function both unsets menu variables and visually erases the menu area 1033\ in-preparation for another menu. 1034\ 1035: menu-clear ( -- ) 1036 menu-unset 1037 menu-erase 1038; 1039 1040\ Assign configuration values 1041bullet menubllt ! 104210 menuY ! 10435 menuX ! 1044 1045\ Initialize our menu initialization state variables 10460 init_state1 ! 10470 init_state2 ! 10480 init_state3 ! 10490 init_state4 ! 10500 init_state5 ! 10510 init_state6 ! 10520 init_state7 ! 10530 init_state8 ! 1054 1055\ Initialize our boolean state variables 10560 toggle_state1 ! 10570 toggle_state2 ! 10580 toggle_state3 ! 10590 toggle_state4 ! 10600 toggle_state5 ! 10610 toggle_state6 ! 10620 toggle_state7 ! 10630 toggle_state8 ! 1064 1065\ Initialize our array state variables 10660 cycle_state1 ! 10670 cycle_state2 ! 10680 cycle_state3 ! 10690 cycle_state4 ! 10700 cycle_state5 ! 10710 cycle_state6 ! 10720 cycle_state7 ! 10730 cycle_state8 ! 1074 1075\ Initialize string containers 10760 init_text1 c! 10770 init_text2 c! 10780 init_text3 c! 10790 init_text4 c! 10800 init_text5 c! 10810 init_text6 c! 10820 init_text7 c! 10830 init_text8 c! 1084