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