1\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org> 2\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> 3\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org> 4\ Copyright 2020 OmniOS Community Edition (OmniOSce) Association. 5\ All rights reserved. 6\ 7\ Redistribution and use in source and binary forms, with or without 8\ modification, are permitted provided that the following conditions 9\ are met: 10\ 1. Redistributions of source code must retain the above copyright 11\ notice, this list of conditions and the following disclaimer. 12\ 2. Redistributions in binary form must reproduce the above copyright 13\ notice, this list of conditions and the following disclaimer in the 14\ documentation and/or other materials provided with the distribution. 15\ 16\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26\ SUCH DAMAGE. 27 28marker task-menu.4th 29 30\ Frame drawing 31include /boot/forth/frames.4th 32 33vocabulary menu-infrastructure 34vocabulary menu-namespace 35vocabulary menu-command-helpers 36 37only forth also menu-infrastructure definitions 38 39f_double \ Set frames to double (see frames.4th). Replace with 40 \ f_single if you want single frames. 4146 constant dot \ ASCII definition of a period (in decimal) 42 43 5 constant menu_default_x \ default column position of timeout 4410 constant menu_default_y \ default row position of timeout msg 45 4 constant menu_timeout_default_x \ default column position of timeout 4623 constant menu_timeout_default_y \ default row position of timeout msg 4710 constant menu_timeout_default \ default timeout (in seconds) 48 49\ Customize the following values with care 50 51 1 constant menu_start \ Numerical prefix of first menu item 52dot constant bullet \ Menu bullet (appears after numerical prefix) 53 5 constant menu_x \ Row position of the menu (from the top) 54 10 constant menu_y \ Column position of the menu (from left side) 55 56\ Menu Appearance 57variable menuidx \ Menu item stack for number prefixes 58variable menurow \ Menu item stack for positioning 59variable menubllt \ Menu item bullet 60 61\ Menu Positioning 62variable menuX \ Menu X offset (columns) 63variable menuY \ Menu Y offset (rows) 64 65\ Menu-item elements 66variable menurebootadded 67 68\ Menu timer [count-down] variables 69variable menu_timeout_enabled \ timeout state (internal use only) 70variable menu_time \ variable for tracking the passage of time 71variable menu_timeout \ determined configurable delay duration 72variable menu_timeout_x \ column position of timeout message 73variable menu_timeout_y \ row position of timeout message 74 75only forth also menu-namespace definitions 76 77\ Menu-item key association/detection 78variable menukey1 79variable menukey2 80variable menukey3 81variable menukey4 82variable menukey5 83variable menukey6 84variable menukey7 85variable menukey8 86variable menureboot 87variable menuacpi 88variable menuosconsole 89variable menukmdb 90variable menuoptions 91 92\ Menu initialization status variables 93variable init_state1 94variable init_state2 95variable init_state3 96variable init_state4 97variable init_state5 98variable init_state6 99variable init_state7 100variable init_state8 101 102\ Boolean option status variables 103variable toggle_state1 104variable toggle_state2 105variable toggle_state3 106variable toggle_state4 107variable toggle_state5 108variable toggle_state6 109variable toggle_state7 110variable toggle_state8 111 112\ Array option status variables 113variable cycle_state1 114variable cycle_state2 115variable cycle_state3 116variable cycle_state4 117variable cycle_state5 118variable cycle_state6 119variable cycle_state7 120variable cycle_state8 121 122\ Containers for storing the initial caption text 123create init_text1 64 allot 124create init_text2 64 allot 125create init_text3 64 allot 126create init_text4 64 allot 127create init_text5 64 allot 128create init_text6 64 allot 129create init_text7 64 allot 130create init_text8 64 allot 131 132only forth definitions 133 134: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. 135 s" arch-i386" environment? dup if 136 drop 137 then 138; 139 140: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 141 s" hint.acpi.0.rsdp" getenv 142 dup -1 = if 143 drop false exit 144 then 145 2drop 146 true 147; 148 149: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 150 s" hint.acpi.0.disabled" getenv 151 dup -1 <> if 152 s" 0" compare 0<> if 153 false exit 154 then 155 else 156 drop 157 then 158 true 159; 160 161: +c! ( N C-ADDR/U K -- C-ADDR/U ) 162 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr ) 163 rot + c! ( n c-addr/u k n c-addr -- n c-addr/u ) 164 rot drop ( n c-addr/u -- c-addr/u ) 165; 166 167only forth also menu-namespace definitions 168 169\ Forth variables 170: namespace ( C-ADDR/U N -- ) also menu-namespace +c! evaluate previous ; 171: menukeyN ( N -- ADDR ) s" menukeyN" 7 namespace ; 172: init_stateN ( N -- ADDR ) s" init_stateN" 10 namespace ; 173: toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 namespace ; 174: cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 namespace ; 175: init_textN ( N -- C-ADDR ) s" init_textN" 9 namespace ; 176 177\ Environment variables 178: menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ; 179: menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ; 180: menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ; 181: ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ; 182: menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ; 183: toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ; 184: toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ; 185: menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ; 186: ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ; 187 188also menu-infrastructure definitions 189 190\ This function prints a menu item at menuX (row) and menuY (column), returns 191\ the incremental decimal ASCII value associated with the menu item, and 192\ increments the cursor position to the next row for the creation of the next 193\ menu item. This function is called by the menu-create function. You need not 194\ call it directly. 195\ 196: printmenuitem ( menu_item_str -- ascii_keycode ) 197 198 loader_color? if [char] ^ escc! then 199 200 menurow dup @ 1+ swap ! ( increment menurow ) 201 menuidx dup @ 1+ swap ! ( increment menuidx ) 202 203 \ Calculate the menuitem row position 204 menurow @ menuY @ + 205 206 \ Position the cursor at the menuitem position 207 dup menuX @ swap at-xy 208 209 \ Print the value of menuidx 210 loader_color? dup ( -- bool bool ) 211 if b then 212 menuidx @ . 213 if me then 214 215 \ Move the cursor forward 1 column 216 dup menuX @ 1+ swap at-xy 217 218 menubllt @ emit \ Print the menu bullet using the emit function 219 220 \ Move the cursor to the 3rd column from the current position 221 \ to allow for a space between the numerical prefix and the 222 \ text caption 223 menuX @ 3 + swap at-xy 224 225 \ Print the menu caption (we expect a string to be on the stack 226 \ prior to invoking this function) 227 type 228 229 \ Here we will add the ASCII decimal of the numerical prefix 230 \ to the stack (decimal ASCII for `1' is 49) as a "return value" 231 menuidx @ 48 + 232; 233 234: delim? ( C -- BOOL ) 235 dup 32 = ( c -- c bool ) \ [sp] space 236 over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab 237 over 10 = or ( c bool -- c bool ) \ [nl] newline 238 over 13 = or ( c bool -- c bool ) \ [cr] carriage return 239 over [char] , = or ( c bool -- c bool ) \ comma 240 swap drop ( c bool -- bool ) \ return boolean 241; 242 243\ illumos kernel acpi-user-options has following values: 244\ default: 0 - system will enable acpi based on bios date 245\ on: 1 - acpi is set on 246\ off: 2 - acpi is set off 247\ madt: 4 - use only MADT 248\ legacy: 8 - use legacy mode 249 250: acpi-captions ( N -- ) 251 \ first entry 252 dup s" [A]CPI................ default" rot 48 menu_caption[x][y] setenv 253 dup s" ^[1mA^[mCPI.............. ^[32;7mdefault^[m" rot 48 ansi_caption[x][y] setenv 254 255 dup s" [A]CPI................ On" rot 49 menu_caption[x][y] setenv 256 dup s" ^[1mA^[mCPI.............. ^[34;1mOn^[m" rot 49 ansi_caption[x][y] setenv 257 258 dup s" [A]CPI................ Off" rot 50 menu_caption[x][y] setenv 259 dup s" ^[1mA^[mCPI.............. ^[34;1mOff^[m" rot 50 ansi_caption[x][y] setenv 260 261 dup s" [A]CPI................ MADT" rot 51 menu_caption[x][y] setenv 262 dup s" ^[1mA^[mCPI.............. ^[34;1mMADT^[m" rot 51 ansi_caption[x][y] setenv 263 264 dup s" [A]CPI................ Legacy" rot 52 menu_caption[x][y] setenv 265 s" ^[1mA^[mCPI.............. ^[34;1mLegacy^[m" rot 52 ansi_caption[x][y] setenv 266; 267 268\ illumos console has following values: 269\ text, ttya, ttyb, ttyc, ttyd 270 271: osconsole-captions ( N -- ) 272 \ first entry 273 dup s" Os[C]onsole........... text" rot 48 menu_caption[x][y] setenv 274 dup s" Os^[1mC^[monsole............ ^[32;7mtext^[m" rot 48 ansi_caption[x][y] setenv 275 276 dup s" Os[C]onsole........... ttya" rot 49 menu_caption[x][y] setenv 277 dup s" Os^[1mC^[monsole............ ^[34;1mttya^[m" rot 49 ansi_caption[x][y] setenv 278 279 dup s" Os[C]onsole........... ttyb" rot 50 menu_caption[x][y] setenv 280 dup s" Os^[1mC^[monsole............ ^[34;1mttyb^[m" rot 50 ansi_caption[x][y] setenv 281 282 dup s" Os[C]onsole........... ttyc" rot 51 menu_caption[x][y] setenv 283 dup s" Os^[1mC^[monsole............ ^[34;1mttyc^[m" rot 51 ansi_caption[x][y] setenv 284 285 dup s" Os[C]onsole........... ttyd" rot 52 menu_caption[x][y] setenv 286 s" Os^[1mC^[monsole............ ^[34;1mttyd^[m" rot 52 ansi_caption[x][y] setenv 287; 288 289\ kmdb options are as follows 290\ default: 0 - disabled 291\ 1 - boot with -k option 292\ 2 - as 1 + configure NMI to drop to kmdb 293\ 3 - boot with -k and -d options 294\ 4 - as 3 + configure NMI to drop to kmdb 295 296: kmdb-captions ( N -- ) 297 \ first entry 298 dup s" [k]mdb Mode........... Off" rot 48 menu_caption[x][y] setenv 299 dup s" ^[1mk^[mmdb Mode............. ^[34;1mOff^[m" rot 48 ansi_caption[x][y] setenv 300 301 dup s" [k]mdb Mode........... Loaded" rot 49 menu_caption[x][y] setenv 302 dup s" ^[1mk^[mmdb Mode............. ^[32;7mLoaded^[m" rot 49 ansi_caption[x][y] setenv 303 304 dup s" [k]mdb Mode........... On NMI" rot 50 menu_caption[x][y] setenv 305 dup s" ^[1mk^[mmdb Mode............. ^[32;7mOn NMI^[m" rot 50 ansi_caption[x][y] setenv 306 307 dup s" [k]mdb Mode........... On Boot" rot 51 menu_caption[x][y] setenv 308 dup s" ^[1mk^[mmdb Mode............. ^[32;7mOn Boot^[m" rot 51 ansi_caption[x][y] setenv 309 310 dup s" [k]mdb Mode........... On Boot/NMI" rot 52 menu_caption[x][y] setenv 311 s" ^[1mk^[mmdb Mode............. ^[32;7mOn Boot/NMI^[m" rot 52 ansi_caption[x][y] setenv 312; 313 314: set-captions ( x y - x y ) 315 \ Set the current non-ANSI caption 316 2dup swap dup ( x y -- x y y x x ) 317 s" set menu_caption[x]=$menu_caption[x][y]" 318 17 +c! 34 +c! 37 +c! evaluate 319 ( x y y x x c-addr/u -- x y ) 320 321 \ Set the current ANSI caption 322 2dup swap dup ( x y -- x y y x x ) 323 s" set ansi_caption[x]=$ansi_caption[x][y]" 324 17 +c! 34 +c! 37 +c! evaluate 325 ( x y y x x c-addr/u -- x y ) 326; 327 328\ This function creates the list of menu items. This function is called by the 329\ menu-display function. You need not call it directly. 330\ 331: menu-create ( -- ) 332 333 \ Print the frame caption at (x,y) 334 s" loader_menu_title" getenv dup -1 = if 335 drop s" Welcome to illumos" 336 then 337 TRUE ( use default alignment ) 338 s" loader_menu_title_align" getenv dup -1 <> if 339 2dup s" left" compare-insensitive 0= if ( 1 ) 340 2drop ( c-addr/u ) drop ( bool ) 341 menuX @ menuY @ 1- 342 FALSE ( don't use default alignment ) 343 else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 ) 344 2drop ( c-addr/u ) drop ( bool ) 345 menuX @ 42 + 4 - over - menuY @ 1- 346 FALSE ( don't use default alignment ) 347 else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then 348 else 349 drop ( getenv cruft ) 350 then 351 if ( use default center alignement? ) 352 menuX @ 19 + over 2 / - menuY @ 1- 353 then 354 at-xy type 355 356 \ If $menu_init is set, evaluate it (allowing for whole menus to be 357 \ constructed dynamically -- as this function could conceivably set 358 \ the remaining environment variables to construct the menu entirely). 359 \ 360 s" menu_init" getenv dup -1 <> if 361 evaluate 362 else 363 drop 364 then 365 366 \ Print our menu options with respective key/variable associations. 367 \ `printmenuitem' ends by adding the decimal ASCII value for the 368 \ numerical prefix to the stack. We store the value left on the stack 369 \ to the key binding variable for later testing against a character 370 \ captured by the `getkey' function. 371 372 \ Note that any menu item beyond 9 will have a numerical prefix on the 373 \ screen consisting of the first digit (ie. 1 for the tenth menu item) 374 \ and the key required to activate that menu item will be the decimal 375 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 376 \ which is misleading and not desirable. 377 \ 378 \ Thus, we do not allow more than 8 configurable items on the menu 379 \ (with "Reboot" as the optional ninth and highest numbered item). 380 381 \ 382 \ Initialize the OsConsole option status. 383 \ 384 0 menuosconsole ! 385 s" menu_osconsole" getenv -1 <> if 386 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 387 dup menuosconsole ! 388 dup osconsole-captions 389 390 s" init_osconsole" evaluate 391 392 \ Get the current cycle state (entry to use) 393 s" osconsole_state" evaluate @ 48 + ( n -- n y ) 394 395 set-captions 396 397 \ Initialize cycle state from stored value 398 48 - ( n y -- n k ) 399 s" init_cyclestate" evaluate ( n k -- n ) 400 401 \ Set $os_console 402 s" activate_osconsole" evaluate ( n -- n ) 403 then 404 drop 405 then 406 407 \ 408 \ Initialize the ACPI option status. 409 \ 410 0 menuacpi ! 411 s" menu_acpi" getenv -1 <> if 412 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 413 dup menuacpi ! 414 dup acpi-captions 415 416 s" init_acpi" evaluate 417 418 \ Get the current cycle state (entry to use) 419 s" acpi_state" evaluate @ 48 + ( n -- n y ) 420 421 set-captions 422 423 \ Initialize cycle state from stored value 424 48 - ( n y -- n k ) 425 s" init_cyclestate" evaluate ( n k -- n ) 426 427 \ Set $acpi-user-options 428 s" activate_acpi" evaluate ( n -- n ) 429 then 430 drop 431 then 432 433 \ 434 \ Initialize the kmdb option status. 435 \ 436 0 menukmdb ! 437 s" menu_kmdb" getenv -1 <> if 438 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 439 dup menukmdb ! 440 dup kmdb-captions 441 442 s" init_kmdb" evaluate 443 444 \ Get the current cycle state (entry to use) 445 s" kmdb_state" evaluate @ 48 + ( n -- n y ) 446 447 set-captions 448 449 \ Initialize cycle state from stored value 450 48 - ( n y -- n k ) 451 s" init_cyclestate" evaluate ( n k -- n ) 452 453 \ Activate the current option 454 s" activate_kmdb" evaluate ( n -- n ) 455 then 456 drop 457 then 458 459 \ 460 \ Initialize the menu_options visual separator. 461 \ 462 0 menuoptions ! 463 s" menu_options" getenv -1 <> if 464 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 465 menuoptions ! 466 else 467 drop 468 then 469 then 470 471 \ Initialize "Reboot" menu state variable (prevents double-entry) 472 false menurebootadded ! 473 474 menu_start 475 1- menuidx ! \ Initialize the starting index for the menu 476 0 menurow ! \ Initialize the starting position for the menu 477 478 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 479 begin 480 \ If the "Options:" separator, print it. 481 dup menuoptions @ = if 482 \ Optionally add a reboot option to the menu 483 s" menu_reboot" getenv -1 <> if 484 drop 485 s" Reboot" printmenuitem menureboot ! 486 true menurebootadded ! 487 then 488 489 menuX @ 490 menurow @ 2 + menurow ! 491 menurow @ menuY @ + 492 at-xy 493 s" menu_optionstext" getenv dup -1 <> if 494 type 495 else 496 drop ." Options:" 497 then 498 then 499 500 \ make sure we have not already initialized this item 501 dup init_stateN dup @ 0= if 502 1 swap ! 503 504 \ If this menuitem has an initializer, run it 505 dup menu_init[x] 506 getenv dup -1 <> if 507 evaluate 508 else 509 drop 510 then 511 else 512 drop 513 then 514 515 dup 516 loader_color? if 517 ansi_caption[x] 518 else 519 menu_caption[x] 520 then 521 522 dup -1 <> if 523 \ test for environment variable 524 getenv dup -1 <> if 525 printmenuitem ( c-addr/u -- n ) 526 dup menukeyN ! 527 else 528 drop 529 then 530 else 531 drop 532 then 533 534 1+ dup 56 > \ add 1 to iterator, continue if less than 57 535 until 536 drop \ iterator 537 538 \ Optionally add a reboot option to the menu 539 menurebootadded @ true <> if 540 s" menu_reboot" getenv -1 <> if 541 drop \ no need for the value 542 s" Reboot" \ menu caption (required by printmenuitem) 543 544 printmenuitem 545 menureboot ! 546 else 547 0 menureboot ! 548 then 549 then 550; 551 552\ Takes an integer on the stack and updates the timeout display. 553\ 554: menu-timeout-update ( N -- ) 555 556 \ Enforce minimum 557 dup 0 < if drop 0 then 558 559 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 560 561 dup 0> if 562 s" Autoboot in " type 563 dup . s" second" type 564 1 > if [char] s emit then 565 s" . [Space] to pause " type 566 else 567 drop 40 spaces \ erase message 568 then 569 570 at-bl 571; 572 573\ This function blocks program flow (loops forever) until a key is pressed. 574\ The key that was pressed is added to the top of the stack in the form of its 575\ decimal ASCII representation. This function is called by the menu-display 576\ function. You need not call it directly. 577\ note, the esc sequences will be dropped, this needs to be changed if 578\ menu is built based on arrow keys. 579\ 580: getkey ( -- ascii_keycode ) 581 582 begin \ loop forever 583 584 menu_timeout_enabled @ 1 = if 585 ( -- ) 586 seconds ( get current time: -- N ) 587 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 588 589 \ At least 1 second has elapsed since last loop 590 \ so we will decrement our "timeout" (really a 591 \ counter, insuring that we do not proceed too 592 \ fast) and update our timeout display. 593 594 menu_time ! ( update time record: N -- ) 595 menu_timeout @ ( "time" remaining: -- N ) 596 dup 0> if ( greater than 0?: N N 0 -- N ) 597 1- ( decrement counter: N -- N ) 598 dup menu_timeout ! 599 ( re-assign: N N Addr -- N ) 600 then 601 ( -- N ) 602 603 dup 0= swap 0< or if ( N <= 0?: N N -- ) 604 \ halt the timer 605 0 menu_timeout ! ( 0 Addr -- ) 606 0 menu_timeout_enabled ! ( 0 Addr -- ) 607 then 608 609 \ update the timer display ( N -- ) 610 menu_timeout @ menu-timeout-update 611 612 menu_timeout @ 0= if 613 \ We've reached the end of the timeout 614 \ (user did not cancel by pressing ANY 615 \ key) 616 617 s" menu_timeout_command" getenv dup 618 -1 = if 619 drop \ clean-up 620 else 621 evaluate 622 then 623 then 624 625 else ( -- N ) 626 \ No [detectable] time has elapsed (in seconds) 627 drop ( N -- ) 628 then 629 ( -- ) 630 then 631 632 key? if \ Was a key pressed? (see loader(8)) 633 634 \ An actual key was pressed (if the timeout is running, 635 \ kill it regardless of which key was pressed) 636 menu_timeout @ 0<> if 637 0 menu_timeout ! 638 0 menu_timeout_enabled ! 639 640 \ clear screen of timeout message 641 0 menu-timeout-update 642 then 643 644 \ get the key that was pressed and exit (if we 645 \ get a non-zero ASCII code) 646 key dup 0<> if 647 dup 0x1b = if 648 key? if ( is it sequence? ) 649 drop 650 begin 651 key? 652 while 653 key drop 654 repeat 655 else 656 exit 657 then 658 else 659 exit 660 then 661 else 662 drop 663 then 664 then 665 50 ms \ sleep for 50 milliseconds (see loader(8)) 666 667 again 668; 669 670: menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1. 671 672 \ Clear the screen area associated with the interactive menu 673 menuX @ menuY @ 674 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 675 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 676 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 677 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 678 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 679 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 680 2drop 681 682 \ Reset the starting index and position for the menu 683 menu_start 1- menuidx ! 684 0 menurow ! 685; 686 687only forth 688also menu-infrastructure 689also menu-namespace 690also menu-command-helpers definitions 691 692: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state 693 694 \ ASCII numeral equal to user-selected menu item must be on the stack. 695 \ We do not modify the stack, so the ASCII numeral is left on top. 696 697 dup init_textN c@ 0= if 698 \ NOTE: no need to check toggle_stateN since the first time we 699 \ are called, we will populate init_textN. Further, we don't 700 \ need to test whether menu_caption[x] (ansi_caption[x] when 701 \ loader_color?=1) is available since we would not have been 702 \ called if the caption was NULL. 703 704 \ base name of environment variable 705 dup ( n -- n n ) \ key pressed 706 loader_color? if 707 ansi_caption[x] 708 else 709 menu_caption[x] 710 then 711 getenv dup -1 <> if 712 713 2 pick ( n c-addr/u -- n c-addr/u n ) 714 init_textN ( n c-addr/u n -- n c-addr/u c-addr ) 715 716 \ now we have the buffer c-addr on top 717 \ ( followed by c-addr/u of current caption ) 718 719 \ Copy the current caption into our buffer 720 2dup c! -rot \ store strlen at first byte 721 begin 722 rot 1+ \ bring alt addr to top and increment 723 -rot -rot \ bring buffer addr to top 724 2dup c@ swap c! \ copy current character 725 1+ \ increment buffer addr 726 rot 1- \ bring buffer len to top and decrement 727 dup 0= \ exit loop if buffer len is zero 728 until 729 2drop \ buffer len/addr 730 drop \ alt addr 731 732 else 733 drop 734 then 735 then 736 737 \ Now we are certain to have init_textN populated with the initial 738 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). 739 \ We can now use init_textN as the untoggled caption and 740 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the 741 \ toggled caption and store the appropriate value into menu_caption[x] 742 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll 743 \ negate the toggled state so that we reverse the flow on subsequent 744 \ calls. 745 746 dup toggle_stateN @ 0= if 747 \ state is OFF, toggle to ON 748 749 dup ( n -- n n ) \ key pressed 750 loader_color? if 751 toggled_ansi[x] 752 else 753 toggled_text[x] 754 then 755 getenv dup -1 <> if 756 \ Assign toggled text to menu caption 757 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 758 loader_color? if 759 ansi_caption[x] 760 else 761 menu_caption[x] 762 then 763 setenv 764 else 765 \ No toggled text, keep the same caption 766 drop ( n -1 -- n ) \ getenv cruft 767 then 768 769 true \ new value of toggle state var (to be stored later) 770 else 771 \ state is ON, toggle to OFF 772 773 dup init_textN count ( n -- n c-addr/u ) 774 775 \ Assign init_textN text to menu caption 776 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 777 loader_color? if 778 ansi_caption[x] 779 else 780 menu_caption[x] 781 then 782 setenv 783 784 false \ new value of toggle state var (to be stored below) 785 then 786 787 \ now we'll store the new toggle state (on top of stack) 788 over toggle_stateN ! 789; 790 791: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem 792 793 \ ASCII numeral equal to user-selected menu item must be on the stack. 794 \ We do not modify the stack, so the ASCII numeral is left on top. 795 796 dup cycle_stateN dup @ 1+ \ get value and increment 797 798 \ Before assigning the (incremented) value back to the pointer, 799 \ let's test for the existence of this particular array element. 800 \ If the element exists, we'll store index value and move on. 801 \ Otherwise, we'll loop around to zero and store that. 802 803 dup 48 + ( n addr k -- n addr k k' ) 804 \ duplicate array index and convert to ASCII numeral 805 806 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y) 807 loader_color? if 808 ansi_caption[x][y] 809 else 810 menu_caption[x][y] 811 then 812 ( n addr k n k' -- n addr k c-addr/u ) 813 814 \ Now test for the existence of our incremented array index in the 815 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color 816 \ enabled) as set in loader.rc(5), et. al. 817 818 getenv dup -1 = if 819 \ No caption set for this array index. Loop back to zero. 820 821 drop ( n addr k -1 -- n addr k ) \ getenv cruft 822 drop 0 ( n addr k -- n addr 0 ) \ new value to store later 823 824 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y) 825 loader_color? if 826 ansi_caption[x][y] 827 else 828 menu_caption[x][y] 829 then 830 ( n addr 0 n 48 -- n addr 0 c-addr/u ) 831 getenv dup -1 = if 832 \ Highly unlikely to occur, but to ensure things move 833 \ along smoothly, allocate a temporary NULL string 834 drop ( cruft ) s" " 835 then 836 then 837 838 \ At this point, we should have the following on the stack (in order, 839 \ from bottom to top): 840 \ 841 \ n - Ascii numeral representing the menu choice (inherited) 842 \ addr - address of our internal cycle_stateN variable 843 \ k - zero-based number we intend to store to the above 844 \ c-addr/u - string value we intend to store to menu_caption[x] 845 \ (or ansi_caption[x] with loader_color enabled) 846 \ 847 \ Let's perform what we need to with the above. 848 849 \ Assign array value text to menu caption 850 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n ) 851 loader_color? if 852 ansi_caption[x] 853 else 854 menu_caption[x] 855 then 856 setenv 857 858 swap ! ( n addr k -- n ) \ update array state variable 859; 860 861only forth definitions also menu-infrastructure 862 863\ Erase and redraw the menu. Useful if you change a caption and want to 864\ update the menu to reflect the new value. 865\ 866: menu-redraw ( -- ) 867 menu-erase 868 menu-create 869; 870 871: menu-box ( -- ) 872 \ Interpret a custom frame type for the menu 873 TRUE ( draw a box? default yes, but might be altered below ) 874 s" loader_menu_frame" getenv dup -1 = if ( 1 ) 875 drop \ no custom frame type 876 else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 ) 877 f_single ( see frames.4th ) 878 else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 ) 879 f_double ( see frames.4th ) 880 else ( 3 ) s" none" compare-insensitive 0= if ( 4 ) 881 drop FALSE \ don't draw a box 882 ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then 883 if 884 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y) 885 then 886; 887 888\ This function initializes the menu. Call this from your `loader.rc' file 889\ before calling any other menu-related functions. 890\ 891: menu-init ( -- ) 892 menu_start 893 1- menuidx ! \ Initialize the starting index for the menu 894 0 menurow ! \ Initialize the starting position for the menu 895 896 \ Assign configuration values 897 s" loader_menu_y" getenv dup -1 = if 898 drop \ no custom row position 899 menu_default_y 900 else 901 \ make sure custom position is a number 902 ?number 0= if 903 menu_default_y \ or use default 904 then 905 then 906 menuY ! 907 s" loader_menu_x" getenv dup -1 = if 908 drop \ no custom column position 909 menu_default_x 910 else 911 \ make sure custom position is a number 912 ?number 0= if 913 menu_default_x \ or use default 914 then 915 then 916 menuX ! 917 918 ['] menu-box console-iterate 919 at-bl 920; 921 922also menu-namespace 923 924\ Main function. Call this from your `loader.rc' file. 925\ 926: menu-display ( -- ) 927 928 0 menu_timeout_enabled ! \ start with automatic timeout disabled 929 930 \ check indication that automatic execution after delay is requested 931 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 932 drop ( just testing existence right now: Addr -- ) 933 934 \ initialize state variables 935 seconds menu_time ! ( store the time we started ) 936 1 menu_timeout_enabled ! ( enable automatic timeout ) 937 938 \ read custom time-duration (if set) 939 s" autoboot_delay" getenv dup -1 = if 940 drop \ no custom duration (remove dup'd bunk -1) 941 menu_timeout_default \ use default setting 942 else 943 2dup ?number 0= if ( if not a number ) 944 \ disable timeout if "NO", else use default 945 s" NO" compare-insensitive 0= if 946 0 menu_timeout_enabled ! 947 0 ( assigned to menu_timeout below ) 948 else 949 menu_timeout_default 950 then 951 else 952 -rot 2drop 953 954 \ boot immediately if less than zero 955 dup 0< if 956 drop 957 menu-create 958 at-bl 959 0 boot 960 then 961 then 962 then 963 menu_timeout ! ( store value on stack from above ) 964 965 menu_timeout_enabled @ 1 = if 966 \ read custom column position (if set) 967 s" loader_menu_timeout_x" getenv dup -1 = if 968 drop \ no custom column position 969 menu_timeout_default_x \ use default setting 970 else 971 \ make sure custom position is a number 972 ?number 0= if 973 menu_timeout_default_x \ or use default 974 then 975 then 976 menu_timeout_x ! ( store value on stack from above ) 977 978 \ read custom row position (if set) 979 s" loader_menu_timeout_y" getenv dup -1 = if 980 drop \ no custom row position 981 menu_timeout_default_y \ use default setting 982 else 983 \ make sure custom position is a number 984 ?number 0= if 985 menu_timeout_default_y \ or use default 986 then 987 then 988 menu_timeout_y ! ( store value on stack from above ) 989 then 990 then 991 992 cursor-invisible cursor-set 993 menu-create 994 995 begin \ Loop forever 996 997 at-bl 998 \ restore cursor for case the getkey ends up in 999 \ booting the kernel. This does restore cursor for 1000 \ serial terminals. 1001 cursor-normal cursor-set 1002 getkey \ Block here, waiting for a key to be pressed 1003 cursor-invisible cursor-set 1004 1005 dup -1 = if 1006 cursor-normal cursor-set 1007 drop exit \ Caught abort (abnormal return) 1008 then 1009 1010 \ Boot if the user pressed Enter/Ctrl-M (13) or 1011 \ Ctrl-Enter/Ctrl-J (10) 1012 dup over 13 = swap 10 = or if 1013 drop ( no longer needed ) 1014 cursor-normal cursor-set 1015 s" boot" evaluate 1016 exit ( pedantic; never reached ) 1017 then 1018 1019 dup menureboot @ = if 0 reboot then 1020 1021 \ Evaluate the decimal ASCII value against known menu item 1022 \ key associations and act accordingly 1023 1024 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1025 begin 1026 dup menukeyN @ 1027 rot tuck = if 1028 1029 \ Adjust for missing ACPI menuitem on non-i386 1030\ arch-i386? true <> menuacpi @ 0<> and if 1031\ menuacpi @ over 2dup < -rot = or 1032\ over 58 < and if 1033\ ( key >= menuacpi && key < 58: N -- N ) 1034\ 1+ 1035\ then 1036\ then 1037 1038 \ Test for the environment variable 1039 dup menu_command[x] 1040 getenv dup -1 <> if 1041 \ Execute the stored procedure 1042 evaluate 1043 1044 \ We expect there to be a non-zero 1045 \ value left on the stack after 1046 \ executing the stored procedure. 1047 \ If so, continue to run, else exit. 1048 1049 0= if 1050 drop \ key pressed 1051 drop \ loop iterator 1052 cursor-normal cursor-set 1053 exit 1054 else 1055 swap \ need iterator on top 1056 then 1057 then 1058 1059 \ Re-adjust for missing ACPI menuitem 1060\ arch-i386? true <> menuacpi @ 0<> and if 1061\ swap 1062\ menuacpi @ 1+ over 2dup < -rot = or 1063\ over 59 < and if 1064\ 1- 1065\ then 1066\ swap 1067\ then 1068 else 1069 swap \ need iterator on top 1070 then 1071 1072 \ 1073 \ Check for menu keycode shortcut(s) 1074 \ 1075 dup menu_keycode[x] 1076 getenv dup -1 = if 1077 drop 1078 else 1079 ?number 0<> if 1080 rot tuck = if 1081 swap 1082 dup menu_command[x] 1083 getenv dup -1 <> if 1084 evaluate 1085 0= if 1086 2drop 1087 cursor-normal 1088 cursor-set 1089 exit 1090 then 1091 else 1092 drop 1093 then 1094 else 1095 swap 1096 then 1097 then 1098 then 1099 1100 1+ dup 56 > \ increment iterator 1101 \ continue if less than 57 1102 until 1103 drop \ loop iterator 1104 drop \ key pressed 1105 1106 again \ Non-operational key was pressed; repeat 1107; 1108 1109\ This function unsets all the possible environment variables associated with 1110\ creating the interactive menu. 1111\ 1112: menu-unset ( -- ) 1113 1114 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1115 begin 1116 dup menu_init[x] unsetenv \ menu initializer 1117 dup menu_command[x] unsetenv \ menu command 1118 dup menu_caption[x] unsetenv \ menu caption 1119 dup ansi_caption[x] unsetenv \ ANSI caption 1120 dup menu_keycode[x] unsetenv \ menu keycode 1121 dup toggled_text[x] unsetenv \ toggle_menuitem caption 1122 dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption 1123 1124 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9') 1125 begin 1126 \ cycle_menuitem caption and ANSI caption 1127 2dup menu_caption[x][y] unsetenv 1128 2dup ansi_caption[x][y] unsetenv 1129 1+ dup 57 > 1130 until 1131 drop \ inner iterator 1132 1133 0 over menukeyN ! \ used by menu-create, menu-display 1134 0 over init_stateN ! \ used by menu-create 1135 0 over toggle_stateN ! \ used by toggle_menuitem 1136 0 over init_textN c! \ used by toggle_menuitem 1137 0 over cycle_stateN ! \ used by cycle_menuitem 1138 1139 1+ dup 56 > \ increment, continue if less than 57 1140 until 1141 drop \ iterator 1142 1143 s" menu_timeout_command" unsetenv \ menu timeout command 1144 s" menu_reboot" unsetenv \ Reboot menu option flag 1145 s" menu_acpi" unsetenv \ ACPI menu option flag 1146 s" menu_kmdb" unsetenv \ kmdb menu option flag 1147 s" menu_osconsole" unsetenv \ osconsole menu option flag 1148 s" menu_options" unsetenv \ Options separator flag 1149 s" menu_optionstext" unsetenv \ separator display text 1150 s" menu_init" unsetenv \ menu initializer 1151 1152 0 menureboot ! 1153 0 menuacpi ! 1154 0 menukmdb ! 1155 0 menuosconsole ! 1156 0 menuoptions ! 1157; 1158 1159only forth definitions also menu-infrastructure 1160 1161\ This function both unsets menu variables and visually erases the menu area 1162\ in-preparation for another menu. 1163\ 1164: menu-clear ( -- ) 1165 menu-unset 1166 menu-erase 1167; 1168 1169bullet menubllt ! 1170 1171also menu-namespace 1172 1173\ Initialize our menu initialization state variables 11740 init_state1 ! 11750 init_state2 ! 11760 init_state3 ! 11770 init_state4 ! 11780 init_state5 ! 11790 init_state6 ! 11800 init_state7 ! 11810 init_state8 ! 1182 1183\ Initialize our boolean state variables 11840 toggle_state1 ! 11850 toggle_state2 ! 11860 toggle_state3 ! 11870 toggle_state4 ! 11880 toggle_state5 ! 11890 toggle_state6 ! 11900 toggle_state7 ! 11910 toggle_state8 ! 1192 1193\ Initialize our array state variables 11940 cycle_state1 ! 11950 cycle_state2 ! 11960 cycle_state3 ! 11970 cycle_state4 ! 11980 cycle_state5 ! 11990 cycle_state6 ! 12000 cycle_state7 ! 12010 cycle_state8 ! 1202 1203\ Initialize string containers 12040 init_text1 c! 12050 init_text2 c! 12060 init_text3 c! 12070 init_text4 c! 12080 init_text5 c! 12090 init_text6 c! 12100 init_text7 c! 12110 init_text8 c! 1212 1213only forth definitions 1214