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