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