menu.4th revision 241363
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 241363 2012-10-09 01:31:03Z 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/U | -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 s" menu_optionstext" getenv dup -1 <> if 492 type 493 else 494 drop ." Options:" 495 then 496 then 497 498 \ If this is the ACPI menu option, act accordingly. 499 dup menuacpi @ = if 500 acpimenuitem ( -- C-Addr/U | -1 ) 501 else 502 loader_color? if 503 s" ansi_caption[x]" 504 else 505 s" menu_caption[x]" 506 then 507 then 508 509 ( C-Addr/U | -1 ) 510 dup -1 <> if 511 \ replace 'x' with current iteration 512 -rot 2dup 13 + c! rot 513 514 \ test for environment variable 515 getenv dup -1 <> if 516 printmenuitem ( C-Addr/U -- N ) 517 518 s" menukeyN !" \ generate cmd to store result 519 -rot 2dup 7 + c! rot 520 521 evaluate 522 else 523 drop 524 then 525 else 526 drop 527 528 s" menu_command[x]" 529 -rot 2dup 13 + c! rot ( replace 'x' ) 530 unsetenv 531 then 532 533 1+ dup 56 > \ add 1 to iterator, continue if less than 57 534 until 535 drop \ iterator 536 537 \ Optionally add a reboot option to the menu 538 menurebootadded @ true <> if 539 s" menu_reboot" getenv -1 <> if 540 drop \ no need for the value 541 s" Reboot" \ menu caption (required by printmenuitem) 542 543 printmenuitem 544 menureboot ! 545 else 546 0 menureboot ! 547 then 548 then 549; 550 551\ Takes a single integer on the stack and updates the timeout display. The 552\ integer must be between 0 and 9 (we will only update a single digit in the 553\ source message). 554\ 555: menu-timeout-update ( N -- ) 556 557 dup 9 > if ( N N 9 -- N ) 558 drop ( N -- ) 559 9 ( maximum: -- N ) 560 then 561 562 dup 0 < if ( N N 0 -- N ) 563 drop ( N -- ) 564 0 ( minimum: -- N ) 565 then 566 567 48 + ( convert single-digit numeral to ASCII: N 48 -- N ) 568 569 s" Autoboot in N seconds. [Space] to pause" ( N -- N Addr C ) 570 571 2 pick 48 - 0> if ( N Addr C N 48 -- N Addr C ) 572 573 \ Modify 'N' (Addr+12) above to reflect time-left 574 575 -rot ( N Addr C -- C N Addr ) 576 tuck ( C N Addr -- C Addr N Addr ) 577 12 + ( C Addr N Addr -- C Addr N Addr2 ) 578 c! ( C Addr N Addr2 -- C Addr ) 579 swap ( C Addr -- Addr C ) 580 581 menu_timeout_x @ 582 menu_timeout_y @ 583 at-xy ( position cursor: Addr C N N -- Addr C ) 584 585 type ( print message: Addr C -- ) 586 587 else ( N Addr C N -- N Addr C ) 588 589 menu_timeout_x @ 590 menu_timeout_y @ 591 at-xy ( position cursor: N Addr C N N -- N Addr C ) 592 593 spaces ( erase message: N Addr C -- N Addr ) 594 2drop ( N Addr -- ) 595 596 then 597 598 0 25 at-xy ( position cursor back at bottom-left ) 599; 600 601\ This function blocks program flow (loops forever) until a key is pressed. 602\ The key that was pressed is added to the top of the stack in the form of its 603\ decimal ASCII representation. This function is called by the menu-display 604\ function. You need not call it directly. 605\ 606: getkey ( -- ascii_keycode ) 607 608 begin \ loop forever 609 610 menu_timeout_enabled @ 1 = if 611 ( -- ) 612 seconds ( get current time: -- N ) 613 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 614 615 \ At least 1 second has elapsed since last loop 616 \ so we will decrement our "timeout" (really a 617 \ counter, insuring that we do not proceed too 618 \ fast) and update our timeout display. 619 620 menu_time ! ( update time record: N -- ) 621 menu_timeout @ ( "time" remaining: -- N ) 622 dup 0> if ( greater than 0?: N N 0 -- N ) 623 1- ( decrement counter: N -- N ) 624 dup menu_timeout ! 625 ( re-assign: N N Addr -- N ) 626 then 627 ( -- N ) 628 629 dup 0= swap 0< or if ( N <= 0?: N N -- ) 630 \ halt the timer 631 0 menu_timeout ! ( 0 Addr -- ) 632 0 menu_timeout_enabled ! ( 0 Addr -- ) 633 then 634 635 \ update the timer display ( N -- ) 636 menu_timeout @ menu-timeout-update 637 638 menu_timeout @ 0= if 639 \ We've reached the end of the timeout 640 \ (user did not cancel by pressing ANY 641 \ key) 642 643 s" menu_timeout_command" getenv dup 644 -1 = if 645 drop \ clean-up 646 else 647 evaluate 648 then 649 then 650 651 else ( -- N ) 652 \ No [detectable] time has elapsed (in seconds) 653 drop ( N -- ) 654 then 655 ( -- ) 656 then 657 658 key? if \ Was a key pressed? (see loader(8)) 659 660 \ An actual key was pressed (if the timeout is running, 661 \ kill it regardless of which key was pressed) 662 menu_timeout @ 0<> if 663 0 menu_timeout ! 664 0 menu_timeout_enabled ! 665 666 \ clear screen of timeout message 667 0 menu-timeout-update 668 then 669 670 \ get the key that was pressed and exit (if we 671 \ get a non-zero ASCII code) 672 key dup 0<> if 673 exit 674 else 675 drop 676 then 677 then 678 50 ms \ sleep for 50 milliseconds (see loader(8)) 679 680 again 681; 682 683: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1. 684 685 \ Clear the screen area associated with the interactive menu 686 menuX @ menuY @ 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 1+ 689 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 690 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 691 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 692 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 693 2drop 694 695 \ Reset the starting index and position for the menu 696 menu_start 1- menuidx ! 697 0 menurow ! 698; 699 700\ Erase and redraw the menu. Useful if you change a caption and want to 701\ update the menu to reflect the new value. 702\ 703: menu-redraw ( -- ) 704 menu-erase 705 menu-create 706; 707 708\ This function initializes the menu. Call this from your `loader.rc' file 709\ before calling any other menu-related functions. 710\ 711: menu-init ( -- ) 712 menu_start 713 1- menuidx ! \ Initialize the starting index for the menu 714 0 menurow ! \ Initialize the starting position for the menu 715 42 13 2 9 box \ Draw frame (w,h,x,y) 716 0 25 at-xy \ Move cursor to the bottom for output 717; 718 719\ Main function. Call this from your `loader.rc' file. 720\ 721: menu-display ( -- ) 722 723 0 menu_timeout_enabled ! \ start with automatic timeout disabled 724 725 \ check indication that automatic execution after delay is requested 726 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 727 drop ( just testing existence right now: Addr -- ) 728 729 \ initialize state variables 730 seconds menu_time ! ( store the time we started ) 731 1 menu_timeout_enabled ! ( enable automatic timeout ) 732 733 \ read custom time-duration (if set) 734 s" autoboot_delay" getenv dup -1 = if 735 drop \ no custom duration (remove dup'd bunk -1) 736 menu_timeout_default \ use default setting 737 else 738 2dup ?number 0= if ( if not a number ) 739 \ disable timeout if "NO", else use default 740 s" NO" compare-insensitive 0= if 741 0 menu_timeout_enabled ! 742 0 ( assigned to menu_timeout below ) 743 else 744 menu_timeout_default 745 then 746 else 747 -rot 2drop 748 749 \ boot immediately if less than zero 750 dup 0< if 751 drop 752 menu-create 753 0 25 at-xy 754 0 boot 755 then 756 then 757 then 758 menu_timeout ! ( store value on stack from above ) 759 760 menu_timeout_enabled @ 1 = if 761 \ read custom column position (if set) 762 s" loader_menu_timeout_x" getenv dup -1 = if 763 drop \ no custom column position 764 menu_timeout_default_x \ use default setting 765 else 766 \ make sure custom position is a number 767 ?number 0= if 768 menu_timeout_default_x \ or use default 769 then 770 then 771 menu_timeout_x ! ( store value on stack from above ) 772 773 \ read custom row position (if set) 774 s" loader_menu_timeout_y" getenv dup -1 = if 775 drop \ no custom row position 776 menu_timeout_default_y \ use default setting 777 else 778 \ make sure custom position is a number 779 ?number 0= if 780 menu_timeout_default_y \ or use default 781 then 782 then 783 menu_timeout_y ! ( store value on stack from above ) 784 then 785 then 786 787 menu-create 788 789 begin \ Loop forever 790 791 0 25 at-xy \ Move cursor to the bottom for output 792 getkey \ Block here, waiting for a key to be pressed 793 794 dup -1 = if 795 drop exit \ Caught abort (abnormal return) 796 then 797 798 \ Boot if the user pressed Enter/Ctrl-M (13) or 799 \ Ctrl-Enter/Ctrl-J (10) 800 dup over 13 = swap 10 = or if 801 drop ( no longer needed ) 802 s" boot" evaluate 803 exit ( pedantic; never reached ) 804 then 805 806 \ Evaluate the decimal ASCII value against known menu item 807 \ key associations and act accordingly 808 809 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 810 begin 811 s" menukeyN @" 812 813 \ replace 'N' with current iteration 814 -rot 2dup 7 + c! rot 815 816 evaluate rot tuck = if 817 818 \ Adjust for missing ACPI menuitem on non-i386 819 arch-i386? true <> menuacpi @ 0<> and if 820 menuacpi @ over 2dup < -rot = or 821 over 58 < and if 822 ( key >= menuacpi && key < 58: N -- N ) 823 1+ 824 then 825 then 826 827 \ base env name for the value (x is a number) 828 s" menu_command[x]" 829 830 \ Copy ASCII number to string at offset 13 831 -rot 2dup 13 + c! rot 832 833 \ Test for the environment variable 834 getenv dup -1 <> if 835 \ Execute the stored procedure 836 evaluate 837 838 \ We expect there to be a non-zero 839 \ value left on the stack after 840 \ executing the stored procedure. 841 \ If so, continue to run, else exit. 842 843 0= if 844 drop \ key pressed 845 drop \ loop iterator 846 exit 847 else 848 swap \ need iterator on top 849 then 850 then 851 852 \ Re-adjust for missing ACPI menuitem 853 arch-i386? true <> menuacpi @ 0<> and if 854 swap 855 menuacpi @ 1+ over 2dup < -rot = or 856 over 59 < and if 857 1- 858 then 859 swap 860 then 861 else 862 swap \ need iterator on top 863 then 864 865 \ 866 \ Check for menu keycode shortcut(s) 867 \ 868 s" menu_keycode[x]" 869 -rot 2dup 13 + c! rot 870 getenv dup -1 = if 871 drop 872 else 873 ?number 0<> if 874 rot tuck = if 875 swap 876 s" menu_command[x]" 877 -rot 2dup 13 + c! rot 878 getenv dup -1 <> if 879 evaluate 880 0= if 881 2drop 882 exit 883 then 884 else 885 drop 886 then 887 else 888 swap 889 then 890 then 891 then 892 893 1+ dup 56 > \ increment iterator 894 \ continue if less than 57 895 until 896 drop \ loop iterator 897 898 menureboot @ = if 0 reboot then 899 900 again \ Non-operational key was pressed; repeat 901; 902 903\ This function unsets all the possible environment variables associated with 904\ creating the interactive menu. 905\ 906: menu-unset ( -- ) 907 908 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 909 begin 910 \ Unset variables in-order of appearance in menu.4th(8) 911 912 s" menu_caption[x]" \ basename for caption variable 913 -rot 2dup 13 + c! rot \ replace 'x' with current iteration 914 unsetenv \ not erroneous to unset unknown var 915 916 s" menu_command[x]" \ command basename 917 -rot 2dup 13 + c! rot \ replace 'x' 918 unsetenv 919 920 s" menu_keycode[x]" \ keycode basename 921 -rot 2dup 13 + c! rot \ replace 'x' 922 unsetenv 923 924 s" ansi_caption[x]" \ ANSI caption basename 925 -rot 2dup 13 + c! rot \ replace 'x' 926 unsetenv 927 928 s" toggled_text[x]" \ toggle_menuitem caption basename 929 -rot 2dup 13 + c! rot \ replace 'x' 930 unsetenv 931 932 s" toggled_ansi[x]" \ toggle_menuitem ANSI caption basename 933 -rot 2dup 13 + c! rot \ replace 'x' 934 unsetenv 935 936 s" menu_caption[x][y]" \ cycle_menuitem caption 937 -rot 2dup 13 + c! rot \ replace 'x' 938 49 -rot 939 begin 940 16 2over rot + c! \ replace 'y' 941 2dup unsetenv 942 943 rot 1+ dup 56 > 2swap rot 944 until 945 2drop drop 946 947 s" ansi_caption[x][y]" \ cycle_menuitem ANSI caption 948 -rot 2dup 13 + c! rot \ replace 'x' 949 49 -rot 950 begin 951 16 2over rot + c! \ replace 'y' 952 2dup unsetenv 953 954 rot 1+ dup 56 > 2swap rot 955 until 956 2drop drop 957 958 s" 0 menukeyN !" \ basename for key association var 959 -rot 2dup 9 + c! rot \ replace 'N' with current iteration 960 evaluate \ assign zero (0) to key assoc. var 961 962 1+ dup 56 > \ increment, continue if less than 57 963 until 964 drop \ iterator 965 966 \ unset the timeout command 967 s" menu_timeout_command" unsetenv 968 969 \ clear the "Reboot" menu option flag 970 s" menu_reboot" unsetenv 971 0 menureboot ! 972 973 \ clear the ACPI menu option flag 974 s" menu_acpi" unsetenv 975 0 menuacpi ! 976 977 \ clear the "Options" menu separator flag 978 s" menu_options" unsetenv 979 s" menu_optionstext" unsetenv 980 0 menuoptions ! 981 982; 983 984\ This function both unsets menu variables and visually erases the menu area 985\ in-preparation for another menu. 986\ 987: menu-clear ( -- ) 988 menu-unset 989 menu-erase 990; 991 992\ Assign configuration values 993bullet menubllt ! 99410 menuY ! 9955 menuX ! 996 997\ Initialize our boolean state variables 9980 toggle_state1 ! 9990 toggle_state2 ! 10000 toggle_state3 ! 10010 toggle_state4 ! 10020 toggle_state5 ! 10030 toggle_state6 ! 10040 toggle_state7 ! 10050 toggle_state8 ! 1006 1007\ Initialize our array state variables 10080 cycle_state1 ! 10090 cycle_state2 ! 10100 cycle_state3 ! 10110 cycle_state4 ! 10120 cycle_state5 ! 10130 cycle_state6 ! 10140 cycle_state7 ! 10150 cycle_state8 ! 1016 1017\ Initialize string containers 10180 init_text1 c! 10190 init_text2 c! 10200 init_text3 c! 10210 init_text4 c! 10220 init_text5 c! 10230 init_text6 c! 10240 init_text7 c! 10250 init_text8 c! 1026