menu.4th revision 242923
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 242923 2012-11-12 18:38:54Z 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 drop ( getenv cruft ) 346 s" " 347 then 348 then 349 350 \ At this point, we should have the following on the stack (in order, 351 \ from bottom to top): 352 \ 353 \ N - Ascii numeral representing the menu choice (inherited) 354 \ Addr - address of our internal cycle_stateN variable 355 \ N - zero-based number we intend to store to the above 356 \ C-Addr - string value we intend to store to menu_caption[x] 357 \ (or ansi_caption[x] with loader_color enabled) 358 \ 359 \ Let's perform what we need to with the above. 360 361 \ base name of menuitem caption var 362 loader_color? if 363 s" ansi_caption[x]" 364 else 365 s" menu_caption[x]" 366 then 367 6 pick rot tuck 13 + c! swap \ replace 'x' with menu choice 368 setenv \ set the new caption 369 370 swap ! \ update array state variable 371; 372 373: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 374 s" hint.acpi.0.rsdp" getenv 375 dup -1 = if 376 drop false exit 377 then 378 2drop 379 true 380; 381 382: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 383 s" hint.acpi.0.disabled" getenv 384 dup -1 <> if 385 s" 0" compare 0<> if 386 false exit 387 then 388 else 389 drop 390 then 391 true 392; 393 394\ This function prints the appropriate menuitem basename to the stack if an 395\ ACPI option is to be presented to the user, otherwise returns -1. Used 396\ internally by menu-create, you need not (nor should you) call this directly. 397\ 398: acpimenuitem ( -- C-Addr/U | -1 ) 399 400 arch-i386? if 401 acpipresent? if 402 acpienabled? if 403 loader_color? if 404 s" toggled_ansi[x]" 405 else 406 s" toggled_text[x]" 407 then 408 else 409 loader_color? if 410 s" ansi_caption[x]" 411 else 412 s" menu_caption[x]" 413 then 414 then 415 else 416 menuidx dup @ 1+ swap ! ( increment menuidx ) 417 -1 418 then 419 else 420 -1 421 then 422; 423 424\ This function creates the list of menu items. This function is called by the 425\ menu-display function. You need not be call it directly. 426\ 427: menu-create ( -- ) 428 429 \ Print the frame caption at (x,y) 430 s" loader_menu_title" getenv dup -1 = if 431 drop s" Welcome to FreeBSD" 432 then 433 24 over 2 / - 9 at-xy type 434 435 \ If $menu_init is set, evaluate it (allowing for whole menus to be 436 \ constructed dynamically -- as this function could conceivably set 437 \ the remaining environment variables to construct the menu entirely). 438 \ 439 s" menu_init" getenv dup -1 <> if 440 evaluate 441 else 442 drop 443 then 444 445 \ Print our menu options with respective key/variable associations. 446 \ `printmenuitem' ends by adding the decimal ASCII value for the 447 \ numerical prefix to the stack. We store the value left on the stack 448 \ to the key binding variable for later testing against a character 449 \ captured by the `getkey' function. 450 451 \ Note that any menu item beyond 9 will have a numerical prefix on the 452 \ screen consisting of the first digit (ie. 1 for the tenth menu item) 453 \ and the key required to activate that menu item will be the decimal 454 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 455 \ which is misleading and not desirable. 456 \ 457 \ Thus, we do not allow more than 8 configurable items on the menu 458 \ (with "Reboot" as the optional ninth and highest numbered item). 459 460 \ 461 \ Initialize the ACPI option status. 462 \ 463 0 menuacpi ! 464 s" menu_acpi" getenv -1 <> if 465 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 466 menuacpi ! 467 arch-i386? if acpipresent? if 468 \ 469 \ Set menu toggle state to active state 470 \ (required by generic toggle_menuitem) 471 \ 472 menuacpi @ 473 s" acpienabled? toggle_stateN !" 474 -rot tuck 25 + c! swap 475 evaluate 476 then then 477 else 478 drop 479 then 480 then 481 482 \ 483 \ Initialize the menu_options visual separator. 484 \ 485 0 menuoptions ! 486 s" menu_options" getenv -1 <> if 487 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 488 menuoptions ! 489 else 490 drop 491 then 492 then 493 494 \ Initialize "Reboot" menu state variable (prevents double-entry) 495 false menurebootadded ! 496 497 menu_start 498 1- menuidx ! \ Initialize the starting index for the menu 499 0 menurow ! \ Initialize the starting position for the menu 500 501 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 502 begin 503 \ If the "Options:" separator, print it. 504 dup menuoptions @ = if 505 \ Optionally add a reboot option to the menu 506 s" menu_reboot" getenv -1 <> if 507 drop 508 s" Reboot" printmenuitem menureboot ! 509 true menurebootadded ! 510 then 511 512 menuX @ 513 menurow @ 2 + menurow ! 514 menurow @ menuY @ + 515 at-xy 516 s" menu_optionstext" getenv dup -1 <> if 517 type 518 else 519 drop ." Options:" 520 then 521 then 522 523 \ If this is the ACPI menu option, act accordingly. 524 dup menuacpi @ = if 525 acpimenuitem ( -- C-Addr/U | -1 ) 526 else 527 \ make sure we have not already initialized this item 528 s" init_stateN" 529 -rot 2dup 10 + c! rot \ repace 'N' 530 evaluate dup @ 0= if 531 1 swap ! 532 533 \ If this menuitem has an initializer, run it 534 s" menu_init[x]" 535 -rot 2dup 10 + c! rot \ replace 'x' 536 getenv dup -1 <> if 537 evaluate 538 else 539 drop 540 then 541 else 542 drop 543 then 544 545 loader_color? if 546 s" ansi_caption[x]" 547 else 548 s" menu_caption[x]" 549 then 550 then 551 552 ( C-Addr/U | -1 ) 553 dup -1 <> if 554 \ replace 'x' with current iteration 555 -rot 2dup 13 + c! rot 556 557 \ test for environment variable 558 getenv dup -1 <> if 559 printmenuitem ( C-Addr/U -- N ) 560 561 s" menukeyN !" \ generate cmd to store result 562 -rot 2dup 7 + c! rot 563 564 evaluate 565 else 566 drop 567 then 568 else 569 drop 570 571 s" menu_command[x]" 572 -rot 2dup 13 + c! rot ( replace 'x' ) 573 unsetenv 574 then 575 576 1+ dup 56 > \ add 1 to iterator, continue if less than 57 577 until 578 drop \ iterator 579 580 \ Optionally add a reboot option to the menu 581 menurebootadded @ true <> if 582 s" menu_reboot" getenv -1 <> if 583 drop \ no need for the value 584 s" Reboot" \ menu caption (required by printmenuitem) 585 586 printmenuitem 587 menureboot ! 588 else 589 0 menureboot ! 590 then 591 then 592; 593 594\ Takes a single integer on the stack and updates the timeout display. The 595\ integer must be between 0 and 9 (we will only update a single digit in the 596\ source message). 597\ 598: menu-timeout-update ( N -- ) 599 600 dup 9 > if ( N N 9 -- N ) 601 drop ( N -- ) 602 9 ( maximum: -- N ) 603 then 604 605 dup 0 < if ( N N 0 -- N ) 606 drop ( N -- ) 607 0 ( minimum: -- N ) 608 then 609 610 48 + ( convert single-digit numeral to ASCII: N 48 -- N ) 611 612 s" Autoboot in N seconds. [Space] to pause" ( N -- N Addr C ) 613 614 2 pick 48 - 0> if ( N Addr C N 48 -- N Addr C ) 615 616 \ Modify 'N' (Addr+12) above to reflect time-left 617 618 -rot ( N Addr C -- C N Addr ) 619 tuck ( C N Addr -- C Addr N Addr ) 620 12 + ( C Addr N Addr -- C Addr N Addr2 ) 621 c! ( C Addr N Addr2 -- C Addr ) 622 swap ( C Addr -- Addr C ) 623 624 menu_timeout_x @ 625 menu_timeout_y @ 626 at-xy ( position cursor: Addr C N N -- Addr C ) 627 628 type ( print message: Addr C -- ) 629 630 else ( N Addr C N -- N Addr C ) 631 632 menu_timeout_x @ 633 menu_timeout_y @ 634 at-xy ( position cursor: N Addr C N N -- N Addr C ) 635 636 spaces ( erase message: N Addr C -- N Addr ) 637 2drop ( N Addr -- ) 638 639 then 640 641 0 25 at-xy ( position cursor back at bottom-left ) 642; 643 644\ This function blocks program flow (loops forever) until a key is pressed. 645\ The key that was pressed is added to the top of the stack in the form of its 646\ decimal ASCII representation. This function is called by the menu-display 647\ function. You need not call it directly. 648\ 649: getkey ( -- ascii_keycode ) 650 651 begin \ loop forever 652 653 menu_timeout_enabled @ 1 = if 654 ( -- ) 655 seconds ( get current time: -- N ) 656 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 657 658 \ At least 1 second has elapsed since last loop 659 \ so we will decrement our "timeout" (really a 660 \ counter, insuring that we do not proceed too 661 \ fast) and update our timeout display. 662 663 menu_time ! ( update time record: N -- ) 664 menu_timeout @ ( "time" remaining: -- N ) 665 dup 0> if ( greater than 0?: N N 0 -- N ) 666 1- ( decrement counter: N -- N ) 667 dup menu_timeout ! 668 ( re-assign: N N Addr -- N ) 669 then 670 ( -- N ) 671 672 dup 0= swap 0< or if ( N <= 0?: N N -- ) 673 \ halt the timer 674 0 menu_timeout ! ( 0 Addr -- ) 675 0 menu_timeout_enabled ! ( 0 Addr -- ) 676 then 677 678 \ update the timer display ( N -- ) 679 menu_timeout @ menu-timeout-update 680 681 menu_timeout @ 0= if 682 \ We've reached the end of the timeout 683 \ (user did not cancel by pressing ANY 684 \ key) 685 686 s" menu_timeout_command" getenv dup 687 -1 = if 688 drop \ clean-up 689 else 690 evaluate 691 then 692 then 693 694 else ( -- N ) 695 \ No [detectable] time has elapsed (in seconds) 696 drop ( N -- ) 697 then 698 ( -- ) 699 then 700 701 key? if \ Was a key pressed? (see loader(8)) 702 703 \ An actual key was pressed (if the timeout is running, 704 \ kill it regardless of which key was pressed) 705 menu_timeout @ 0<> if 706 0 menu_timeout ! 707 0 menu_timeout_enabled ! 708 709 \ clear screen of timeout message 710 0 menu-timeout-update 711 then 712 713 \ get the key that was pressed and exit (if we 714 \ get a non-zero ASCII code) 715 key dup 0<> if 716 exit 717 else 718 drop 719 then 720 then 721 50 ms \ sleep for 50 milliseconds (see loader(8)) 722 723 again 724; 725 726: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1. 727 728 \ Clear the screen area associated with the interactive menu 729 menuX @ menuY @ 730 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 731 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 732 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 733 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 734 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 735 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 736 2drop 737 738 \ Reset the starting index and position for the menu 739 menu_start 1- menuidx ! 740 0 menurow ! 741; 742 743\ Erase and redraw the menu. Useful if you change a caption and want to 744\ update the menu to reflect the new value. 745\ 746: menu-redraw ( -- ) 747 menu-erase 748 menu-create 749; 750 751\ This function initializes the menu. Call this from your `loader.rc' file 752\ before calling any other menu-related functions. 753\ 754: menu-init ( -- ) 755 menu_start 756 1- menuidx ! \ Initialize the starting index for the menu 757 0 menurow ! \ Initialize the starting position for the menu 758 42 13 2 9 box \ Draw frame (w,h,x,y) 759 0 25 at-xy \ Move cursor to the bottom for output 760; 761 762\ Main function. Call this from your `loader.rc' file. 763\ 764: menu-display ( -- ) 765 766 0 menu_timeout_enabled ! \ start with automatic timeout disabled 767 768 \ check indication that automatic execution after delay is requested 769 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 770 drop ( just testing existence right now: Addr -- ) 771 772 \ initialize state variables 773 seconds menu_time ! ( store the time we started ) 774 1 menu_timeout_enabled ! ( enable automatic timeout ) 775 776 \ read custom time-duration (if set) 777 s" autoboot_delay" getenv dup -1 = if 778 drop \ no custom duration (remove dup'd bunk -1) 779 menu_timeout_default \ use default setting 780 else 781 2dup ?number 0= if ( if not a number ) 782 \ disable timeout if "NO", else use default 783 s" NO" compare-insensitive 0= if 784 0 menu_timeout_enabled ! 785 0 ( assigned to menu_timeout below ) 786 else 787 menu_timeout_default 788 then 789 else 790 -rot 2drop 791 792 \ boot immediately if less than zero 793 dup 0< if 794 drop 795 menu-create 796 0 25 at-xy 797 0 boot 798 then 799 then 800 then 801 menu_timeout ! ( store value on stack from above ) 802 803 menu_timeout_enabled @ 1 = if 804 \ read custom column position (if set) 805 s" loader_menu_timeout_x" getenv dup -1 = if 806 drop \ no custom column position 807 menu_timeout_default_x \ use default setting 808 else 809 \ make sure custom position is a number 810 ?number 0= if 811 menu_timeout_default_x \ or use default 812 then 813 then 814 menu_timeout_x ! ( store value on stack from above ) 815 816 \ read custom row position (if set) 817 s" loader_menu_timeout_y" getenv dup -1 = if 818 drop \ no custom row position 819 menu_timeout_default_y \ use default setting 820 else 821 \ make sure custom position is a number 822 ?number 0= if 823 menu_timeout_default_y \ or use default 824 then 825 then 826 menu_timeout_y ! ( store value on stack from above ) 827 then 828 then 829 830 menu-create 831 832 begin \ Loop forever 833 834 0 25 at-xy \ Move cursor to the bottom for output 835 getkey \ Block here, waiting for a key to be pressed 836 837 dup -1 = if 838 drop exit \ Caught abort (abnormal return) 839 then 840 841 \ Boot if the user pressed Enter/Ctrl-M (13) or 842 \ Ctrl-Enter/Ctrl-J (10) 843 dup over 13 = swap 10 = or if 844 drop ( no longer needed ) 845 s" boot" evaluate 846 exit ( pedantic; never reached ) 847 then 848 849 dup menureboot @ = if 0 reboot then 850 851 \ Evaluate the decimal ASCII value against known menu item 852 \ key associations and act accordingly 853 854 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 855 begin 856 s" menukeyN @" 857 858 \ replace 'N' with current iteration 859 -rot 2dup 7 + c! rot 860 861 evaluate rot tuck = if 862 863 \ Adjust for missing ACPI menuitem on non-i386 864 arch-i386? true <> menuacpi @ 0<> and if 865 menuacpi @ over 2dup < -rot = or 866 over 58 < and if 867 ( key >= menuacpi && key < 58: N -- N ) 868 1+ 869 then 870 then 871 872 \ base env name for the value (x is a number) 873 s" menu_command[x]" 874 875 \ Copy ASCII number to string at offset 13 876 -rot 2dup 13 + c! rot 877 878 \ Test for the environment variable 879 getenv dup -1 <> if 880 \ Execute the stored procedure 881 evaluate 882 883 \ We expect there to be a non-zero 884 \ value left on the stack after 885 \ executing the stored procedure. 886 \ If so, continue to run, else exit. 887 888 0= if 889 drop \ key pressed 890 drop \ loop iterator 891 exit 892 else 893 swap \ need iterator on top 894 then 895 then 896 897 \ Re-adjust for missing ACPI menuitem 898 arch-i386? true <> menuacpi @ 0<> and if 899 swap 900 menuacpi @ 1+ over 2dup < -rot = or 901 over 59 < and if 902 1- 903 then 904 swap 905 then 906 else 907 swap \ need iterator on top 908 then 909 910 \ 911 \ Check for menu keycode shortcut(s) 912 \ 913 s" menu_keycode[x]" 914 -rot 2dup 13 + c! rot 915 getenv dup -1 = if 916 drop 917 else 918 ?number 0<> if 919 rot tuck = if 920 swap 921 s" menu_command[x]" 922 -rot 2dup 13 + c! rot 923 getenv dup -1 <> if 924 evaluate 925 0= if 926 2drop 927 exit 928 then 929 else 930 drop 931 then 932 else 933 swap 934 then 935 then 936 then 937 938 1+ dup 56 > \ increment iterator 939 \ continue if less than 57 940 until 941 drop \ loop iterator 942 drop \ key pressed 943 944 again \ Non-operational key was pressed; repeat 945; 946 947\ This function unsets all the possible environment variables associated with 948\ creating the interactive menu. 949\ 950: menu-unset ( -- ) 951 952 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 953 begin 954 \ Unset variables in-order of appearance in menu.4th(8) 955 956 s" menu_caption[x]" \ basename for caption variable 957 -rot 2dup 13 + c! rot \ replace 'x' with current iteration 958 unsetenv \ not erroneous to unset unknown var 959 960 s" menu_command[x]" \ command basename 961 -rot 2dup 13 + c! rot \ replace 'x' 962 unsetenv 963 964 s" menu_init[x]" \ initializer basename 965 -rot 2dup 10 + c! rot \ replace 'x' 966 unsetenv 967 968 s" menu_keycode[x]" \ keycode basename 969 -rot 2dup 13 + c! rot \ replace 'x' 970 unsetenv 971 972 s" ansi_caption[x]" \ ANSI caption basename 973 -rot 2dup 13 + c! rot \ replace 'x' 974 unsetenv 975 976 s" toggled_text[x]" \ toggle_menuitem caption basename 977 -rot 2dup 13 + c! rot \ replace 'x' 978 unsetenv 979 980 s" toggled_ansi[x]" \ toggle_menuitem ANSI caption basename 981 -rot 2dup 13 + c! rot \ replace 'x' 982 unsetenv 983 984 s" menu_caption[x][y]" \ cycle_menuitem caption 985 -rot 2dup 13 + c! rot \ replace 'x' 986 48 -rot 987 begin 988 16 2over rot + c! \ replace 'y' 989 2dup unsetenv 990 991 rot 1+ dup 57 > 2swap rot 992 until 993 2drop drop 994 995 s" ansi_caption[x][y]" \ cycle_menuitem ANSI caption 996 -rot 2dup 13 + c! rot \ replace 'x' 997 48 -rot 998 begin 999 16 2over rot + c! \ replace 'y' 1000 2dup unsetenv 1001 1002 rot 1+ dup 57 > 2swap rot 1003 until 1004 2drop drop 1005 1006 s" 0 menukeyN !" \ basename for key association var 1007 -rot 2dup 9 + c! rot \ replace 'N' with current iteration 1008 evaluate \ assign zero (0) to key assoc. var 1009 1010 s" 0 init_stateN !" \ used by menu-create 1011 -rot 2dup 12 + c! rot \ replace 'N' 1012 evaluate 1013 1014 s" 0 toggle_stateN !" \ used by toggle_menuitem 1015 -rot 2dup 14 + c! rot \ replace 'N' 1016 evaluate 1017 1018 s" 0 cycle_stateN !" \ used by cycle_menuitem 1019 -rot 2dup 13 + c! rot \ replace 'N' 1020 evaluate 1021 1022 s" 0 init_textN c!" \ used by toggle_menuitem 1023 -rot 2dup 11 + c! rot \ replace 'N' 1024 evaluate 1025 1026 1+ dup 56 > \ increment, continue if less than 57 1027 until 1028 drop \ iterator 1029 1030 \ unset the timeout command 1031 s" menu_timeout_command" unsetenv 1032 1033 \ clear the "Reboot" menu option flag 1034 s" menu_reboot" unsetenv 1035 0 menureboot ! 1036 1037 \ clear the ACPI menu option flag 1038 s" menu_acpi" unsetenv 1039 0 menuacpi ! 1040 1041 \ clear the "Options" menu separator flag 1042 s" menu_options" unsetenv 1043 s" menu_optionstext" unsetenv 1044 0 menuoptions ! 1045 1046 \ clear the menu initializer 1047 s" menu_init" unsetenv 1048; 1049 1050\ This function both unsets menu variables and visually erases the menu area 1051\ in-preparation for another menu. 1052\ 1053: menu-clear ( -- ) 1054 menu-unset 1055 menu-erase 1056; 1057 1058\ Assign configuration values 1059bullet menubllt ! 106010 menuY ! 10615 menuX ! 1062 1063\ Initialize our menu initialization state variables 10640 init_state1 ! 10650 init_state2 ! 10660 init_state3 ! 10670 init_state4 ! 10680 init_state5 ! 10690 init_state6 ! 10700 init_state7 ! 10710 init_state8 ! 1072 1073\ Initialize our boolean state variables 10740 toggle_state1 ! 10750 toggle_state2 ! 10760 toggle_state3 ! 10770 toggle_state4 ! 10780 toggle_state5 ! 10790 toggle_state6 ! 10800 toggle_state7 ! 10810 toggle_state8 ! 1082 1083\ Initialize our array state variables 10840 cycle_state1 ! 10850 cycle_state2 ! 10860 cycle_state3 ! 10870 cycle_state4 ! 10880 cycle_state5 ! 10890 cycle_state6 ! 10900 cycle_state7 ! 10910 cycle_state8 ! 1092 1093\ Initialize string containers 10940 init_text1 c! 10950 init_text2 c! 10960 init_text3 c! 10970 init_text4 c! 10980 init_text5 c! 10990 init_text6 c! 11000 init_text7 c! 11010 init_text8 c! 1102