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\ 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$ 28 29marker task-menu.4th 30 31\ Frame drawing 32include /boot/forth/frames.4th 33 34vocabulary menu-infrastructure 35vocabulary menu-namespace 36vocabulary menu-command-helpers 37 38only forth also menu-infrastructure definitions 39 40f_double \ Set frames to double (see frames.4th). Replace with 41 \ f_single if you want single frames. 4246 constant dot \ ASCII definition of a period (in decimal) 43 44 5 constant menu_default_x \ default column position of timeout 4510 constant menu_default_y \ default row position of timeout msg 46 4 constant menu_timeout_default_x \ default column position of timeout 4723 constant menu_timeout_default_y \ default row position of timeout msg 4810 constant menu_timeout_default \ default timeout (in seconds) 49 50\ Customize the following values with care 51 52 1 constant menu_start \ Numerical prefix of first menu item 53dot constant bullet \ Menu bullet (appears after numerical prefix) 54 5 constant menu_x \ Row position of the menu (from the top) 55 10 constant menu_y \ Column position of the menu (from left side) 56 57\ Menu Appearance 58variable menuidx \ Menu item stack for number prefixes 59variable menurow \ Menu item stack for positioning 60variable menubllt \ Menu item bullet 61 62\ Menu Positioning 63variable menuX \ Menu X offset (columns) 64variable menuY \ Menu Y offset (rows) 65 66\ Menu-item elements 67variable menurebootadded 68 69\ Parsing of kernels into menu-items 70variable kernidx 71variable kernlen 72variable kernmenuidx 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\ Containers for parsing kernels into menu-items 82create kerncapbuf 64 allot 83create kerndefault 64 allot 84create kernelsbuf 256 allot 85 86only forth also menu-namespace definitions 87 88\ Menu-item key association/detection 89variable menukey1 90variable menukey2 91variable menukey3 92variable menukey4 93variable menukey5 94variable menukey6 95variable menukey7 96variable menukey8 97variable menureboot 98variable menuacpi 99variable menuosconsole 100variable menuoptions 101variable menukernel 102 103\ Menu initialization status variables 104variable init_state1 105variable init_state2 106variable init_state3 107variable init_state4 108variable init_state5 109variable init_state6 110variable init_state7 111variable init_state8 112 113\ Boolean option status variables 114variable toggle_state1 115variable toggle_state2 116variable toggle_state3 117variable toggle_state4 118variable toggle_state5 119variable toggle_state6 120variable toggle_state7 121variable toggle_state8 122 123\ Array option status variables 124variable cycle_state1 125variable cycle_state2 126variable cycle_state3 127variable cycle_state4 128variable cycle_state5 129variable cycle_state6 130variable cycle_state7 131variable cycle_state8 132 133\ Containers for storing the initial caption text 134create init_text1 64 allot 135create init_text2 64 allot 136create init_text3 64 allot 137create init_text4 64 allot 138create init_text5 64 allot 139create init_text6 64 allot 140create init_text7 64 allot 141create init_text8 64 allot 142 143only forth definitions 144 145: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. 146 s" arch-i386" environment? dup if 147 drop 148 then 149; 150 151: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 152 s" hint.acpi.0.rsdp" getenv 153 dup -1 = if 154 drop false exit 155 then 156 2drop 157 true 158; 159 160: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 161 s" hint.acpi.0.disabled" getenv 162 dup -1 <> if 163 s" 0" compare 0<> if 164 false exit 165 then 166 else 167 drop 168 then 169 true 170; 171 172: +c! ( N C-ADDR/U K -- C-ADDR/U ) 173 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr ) 174 rot + c! ( n c-addr/u k n c-addr -- n c-addr/u ) 175 rot drop ( n c-addr/u -- c-addr/u ) 176; 177 178only forth also menu-namespace definitions 179 180\ Forth variables 181: namespace ( C-ADDR/U N -- ) also menu-namespace +c! evaluate previous ; 182: menukeyN ( N -- ADDR ) s" menukeyN" 7 namespace ; 183: init_stateN ( N -- ADDR ) s" init_stateN" 10 namespace ; 184: toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 namespace ; 185: cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 namespace ; 186: init_textN ( N -- C-ADDR ) s" init_textN" 9 namespace ; 187 188\ Environment variables 189: kernel[x] ( N -- C-ADDR/U ) s" kernel[x]" 7 +c! ; 190: menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ; 191: menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ; 192: menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ; 193: ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ; 194: menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ; 195: toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ; 196: toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ; 197: menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ; 198: ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ; 199 200also menu-infrastructure definitions 201 202\ This function prints a menu item at menuX (row) and menuY (column), returns 203\ the incremental decimal ASCII value associated with the menu item, and 204\ increments the cursor position to the next row for the creation of the next 205\ menu item. This function is called by the menu-create function. You need not 206\ call it directly. 207\ 208: printmenuitem ( menu_item_str -- ascii_keycode ) 209 210 loader_color? if [char] ^ escc! then 211 212 menurow dup @ 1+ swap ! ( increment menurow ) 213 menuidx dup @ 1+ swap ! ( increment menuidx ) 214 215 \ Calculate the menuitem row position 216 menurow @ menuY @ + 217 218 \ Position the cursor at the menuitem position 219 dup menuX @ swap at-xy 220 221 \ Print the value of menuidx 222 loader_color? dup ( -- bool bool ) 223 if b then 224 menuidx @ . 225 if me then 226 227 \ Move the cursor forward 1 column 228 dup menuX @ 1+ swap at-xy 229 230 menubllt @ emit \ Print the menu bullet using the emit function 231 232 \ Move the cursor to the 3rd column from the current position 233 \ to allow for a space between the numerical prefix and the 234 \ text caption 235 menuX @ 3 + swap at-xy 236 237 \ Print the menu caption (we expect a string to be on the stack 238 \ prior to invoking this function) 239 type 240 241 \ Here we will add the ASCII decimal of the numerical prefix 242 \ to the stack (decimal ASCII for `1' is 49) as a "return value" 243 menuidx @ 48 + 244; 245 246: delim? ( C -- BOOL ) 247 dup 32 = ( c -- c bool ) \ [sp] space 248 over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab 249 over 10 = or ( c bool -- c bool ) \ [nl] newline 250 over 13 = or ( c bool -- c bool ) \ [cr] carriage return 251 over [char] , = or ( c bool -- c bool ) \ comma 252 swap drop ( c bool -- bool ) \ return boolean 253; 254 255\ This function parses $kernels into variables that are used by the menu to 256\ display which kernel to boot when the [overloaded] `boot' word is interpreted. 257\ Used internally by menu-create, you need not (nor should you) call this 258\ directly. 259\ 260: parse-kernels ( N -- ) \ kernidx 261 kernidx ! ( n -- ) \ store provided `x' value 262 [char] 0 kernmenuidx ! \ initialize `y' value for menu_caption[x][y] 263 264 \ Attempt to get a list of kernels, fall back to sensible default 265 s" kernels" getenv dup -1 = if 266 drop ( cruft ) 267 s" kernel kernel.old" 268 then ( -- c-addr/u ) 269 270 \ Check to see if the user has altered $kernel by comparing it against 271 \ $kernel[N] where N is kernel_state (the actively displayed kernel). 272 s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv 273 dup -1 <> if 274 s" kernel" getenv dup -1 = if 275 drop ( cruft ) s" " 276 then 277 2swap 2over compare 0= if 278 2drop FALSE ( skip below conditional ) 279 else \ User has changed $kernel 280 TRUE ( slurp in new value ) 281 then 282 else \ We haven't yet parsed $kernels into $kernel[N] 283 drop ( getenv cruft ) 284 s" kernel" getenv dup -1 = if 285 drop ( cruft ) s" " 286 then 287 TRUE ( slurp in initial value ) 288 then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 ) 289 if \ slurp new value into kerndefault 290 kerndefault 1+ 0 2swap strcat swap 1- c! 291 then 292 293 \ Clear out existing parsed-kernels 294 kernidx @ [char] 0 295 begin 296 dup kernel[x] unsetenv 297 2dup menu_caption[x][y] unsetenv 298 2dup ansi_caption[x][y] unsetenv 299 1+ dup [char] 8 > 300 until 301 2drop 302 303 \ Step through the string until we find the end 304 begin 305 0 kernlen ! \ initialize length of value 306 307 \ Skip leading whitespace and/or comma delimiters 308 begin 309 dup 0<> if 310 over c@ delim? ( c-addr/u -- c-addr/u bool ) 311 else 312 false ( c-addr/u -- c-addr/u bool ) 313 then 314 while 315 1- swap 1+ swap ( c-addr/u -- c-addr'/u' ) 316 repeat 317 ( c-addr/u -- c-addr'/u' ) 318 319 dup 0= if \ end of string while eating whitespace 320 2drop ( c-addr/u -- ) 321 kernmenuidx @ [char] 0 <> if \ found at least one 322 exit \ all done 323 then 324 325 \ No entries in $kernels; use $kernel instead 326 s" kernel" getenv dup -1 = if 327 drop ( cruft ) s" " 328 then ( -- c-addr/u ) 329 dup kernlen ! \ store entire value length as kernlen 330 else 331 \ We're still within $kernels parsing toward the end; 332 \ find delimiter/end to determine kernlen 333 2dup ( c-addr/u -- c-addr/u c-addr/u ) 334 begin dup 0<> while 335 over c@ delim? if 336 drop 0 ( break ) \ found delimiter 337 else 338 kernlen @ 1+ kernlen ! \ incrememnt 339 1- swap 1+ swap \ c-addr++ u-- 340 then 341 repeat 342 2drop ( c-addr/u c-addr'/u' -- c-addr/u ) 343 344 \ If this is the first entry, compare it to $kernel 345 \ If different, then insert $kernel beforehand 346 kernmenuidx @ [char] 0 = if 347 over kernlen @ kerndefault count compare if 348 kernelsbuf 0 kerndefault count strcat 349 s" ," strcat 2swap strcat 350 kerndefault count swap drop kernlen ! 351 then 352 then 353 then 354 ( c-addr/u -- c-addr'/u' ) 355 356 \ At this point, we should have something on the stack to store 357 \ as the next kernel menu option; start assembling variables 358 359 over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 ) 360 361 \ Assign first to kernel[x] 362 2dup kernmenuidx @ kernel[x] setenv 363 364 \ Assign second to menu_caption[x][y] 365 kerncapbuf 0 s" [K]ernel: " strcat 366 2over strcat 367 kernidx @ kernmenuidx @ menu_caption[x][y] 368 setenv 369 370 \ Assign third to ansi_caption[x][y] 371 kerncapbuf 0 s" @[1mK@[37mernel: " [char] @ escc! strcat 372 kernmenuidx @ [char] 0 = if 373 s" default/@[32m" 374 else 375 s" @[34;1m" 376 then 377 [char] @ escc! strcat 378 2over strcat 379 s" @[37m" [char] @ escc! strcat 380 kernidx @ kernmenuidx @ ansi_caption[x][y] 381 setenv 382 383 2drop ( c-addr/u c-addr/u2 -- c-addr/u ) 384 385 kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if 386 2drop ( c-addr/u -- ) exit 387 then 388 389 kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' ) 390 again 391; 392 393\ This function goes through the kernels that were discovered by the 394\ parse-kernels function [above], adding " (# of #)" text to the end of each 395\ caption. 396\ 397: tag-kernels ( -- ) 398 kernidx @ ( -- x ) dup 0= if exit then 399 [char] 0 s" (Y of Z)" ( x -- x y c-addr/u ) 400 kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed 401 begin 402 2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num 403 404 2over menu_caption[x][y] getenv dup -1 <> if 405 2dup + 1- c@ [char] ) = if 406 2drop \ Already tagged 407 else 408 kerncapbuf 0 2swap strcat 409 2over strcat 410 5 pick 5 pick menu_caption[x][y] setenv 411 then 412 else 413 drop ( getenv cruft ) 414 then 415 416 2over ansi_caption[x][y] getenv dup -1 <> if 417 2dup + 1- c@ [char] ) = if 418 2drop \ Already tagged 419 else 420 kerncapbuf 0 2swap strcat 421 2over strcat 422 5 pick 5 pick ansi_caption[x][y] setenv 423 then 424 else 425 drop ( getenv cruft ) 426 then 427 428 rot 1+ dup [char] 8 > if 429 -rot 2drop TRUE ( break ) 430 else 431 -rot FALSE 432 then 433 until 434 2drop ( x y -- ) 435; 436 437\ Illumos kernel acpi-user-options has following values: 438\ default: 0 - system will enable acpi based on bios date 439\ on: 1 - acpi is set on 440\ off: 2 - acpi is set off 441\ madt: 4 - use only MADT 442\ legacy: 8 - use legacy mode 443 444: acpi-captions ( N -- ) 445 \ first entry 446 dup s" [A]CPI.... default" rot 48 menu_caption[x][y] setenv 447 dup s" ^[1mA^[mCPI.... ^[32;7mdefault^[m" rot 48 ansi_caption[x][y] setenv 448 449 dup s" [A]CPI........ On" rot 49 menu_caption[x][y] setenv 450 dup s" ^[1mA^[mCPI........ ^[34;1mOn^[m" rot 49 ansi_caption[x][y] setenv 451 452 dup s" [A]CPI........ Off" rot 50 menu_caption[x][y] setenv 453 dup s" ^[1mA^[mCPI........ ^[34;1mOff^[m" rot 50 ansi_caption[x][y] setenv 454 455 dup s" [A]CPI....... MADT" rot 51 menu_caption[x][y] setenv 456 dup s" ^[1mA^[mCPI....... ^[34;1mMADT^[m" rot 51 ansi_caption[x][y] setenv 457 458 dup s" [A]CPI..... Legacy" rot 52 menu_caption[x][y] setenv 459 s" ^[1mA^[mCPI..... ^[34;1mLegacy^[m" rot 52 ansi_caption[x][y] setenv 460; 461 462\ Illumos console has following values: 463\ text, ttya, ttyb, ttyc, ttyd 464 465: osconsole-captions ( N -- ) 466 \ first entry 467 dup s" Os[C]onsole.. text" rot 48 menu_caption[x][y] setenv 468 dup s" Os^[1mC^[monsole.. ^[32;7mtext^[m" rot 48 ansi_caption[x][y] setenv 469 470 dup s" Os[C]onsole.. ttya" rot 49 menu_caption[x][y] setenv 471 dup s" Os^[1mC^[monsole.. ^[34;1mttya^[m" rot 49 ansi_caption[x][y] setenv 472 473 dup s" Os[C]onsole.. ttyb" rot 50 menu_caption[x][y] setenv 474 dup s" Os^[1mC^[monsole.. ^[34;1mttyb^[m" rot 50 ansi_caption[x][y] setenv 475 476 dup s" Os[C]onsole.. ttyc" rot 51 menu_caption[x][y] setenv 477 dup s" Os^[1mC^[monsole.. ^[34;1mttyc^[m" rot 51 ansi_caption[x][y] setenv 478 479 dup s" Os[C]onsole.. ttyd" rot 52 menu_caption[x][y] setenv 480 s" Os^[1mC^[monsole.. ^[34;1mttyd^[m" rot 52 ansi_caption[x][y] setenv 481; 482 483\ This function creates the list of menu items. This function is called by the 484\ menu-display function. You need not call it directly. 485\ 486: menu-create ( -- ) 487 488 \ Print the frame caption at (x,y) 489 s" loader_menu_title" getenv dup -1 = if 490 drop s" Welcome on board " 491 then 492 TRUE ( use default alignment ) 493 s" loader_menu_title_align" getenv dup -1 <> if 494 2dup s" left" compare-insensitive 0= if ( 1 ) 495 2drop ( c-addr/u ) drop ( bool ) 496 menuX @ menuY @ 1- 497 FALSE ( don't use default alignment ) 498 else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 ) 499 2drop ( c-addr/u ) drop ( bool ) 500 menuX @ 42 + 4 - over - menuY @ 1- 501 FALSE ( don't use default alignment ) 502 else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then 503 else 504 drop ( getenv cruft ) 505 then 506 if ( use default center alignement? ) 507 menuX @ 19 + over 2 / - menuY @ 1- 508 then 509 at-xy type 510 511 \ If $menu_init is set, evaluate it (allowing for whole menus to be 512 \ constructed dynamically -- as this function could conceivably set 513 \ the remaining environment variables to construct the menu entirely). 514 \ 515 s" menu_init" getenv dup -1 <> if 516 evaluate 517 else 518 drop 519 then 520 521 \ Print our menu options with respective key/variable associations. 522 \ `printmenuitem' ends by adding the decimal ASCII value for the 523 \ numerical prefix to the stack. We store the value left on the stack 524 \ to the key binding variable for later testing against a character 525 \ captured by the `getkey' function. 526 527 \ Note that any menu item beyond 9 will have a numerical prefix on the 528 \ screen consisting of the first digit (ie. 1 for the tenth menu item) 529 \ and the key required to activate that menu item will be the decimal 530 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 531 \ which is misleading and not desirable. 532 \ 533 \ Thus, we do not allow more than 8 configurable items on the menu 534 \ (with "Reboot" as the optional ninth and highest numbered item). 535 536 \ 537 \ Initialize the OsConsole option status. 538 \ 539 0 menuosconsole ! 540 s" menu_osconsole" getenv -1 <> if 541 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 542 dup menuosconsole ! 543 dup osconsole-captions 544 545 s" init_osconsole" evaluate 546 547 \ Get the current cycle state (entry to use) 548 s" osconsole_state" evaluate @ 48 + ( n -- n y ) 549 550 \ Set the current non-ANSI caption 551 2dup swap dup ( n y -- n y y n n ) 552 s" set menu_caption[x]=$menu_caption[x][y]" 553 17 +c! 34 +c! 37 +c! evaluate 554 ( n y y n n c-addr/u -- n y ) 555 556 \ Set the current ANSI caption 557 2dup swap dup ( n y -- n y y n n ) 558 s" set ansi_caption[x]=$ansi_caption[x][y]" 559 17 +c! 34 +c! 37 +c! evaluate 560 ( n y y n n c-addr/u -- n y ) 561 562 \ Initialize cycle state from stored value 563 48 - ( n y -- n k ) 564 s" init_cyclestate" evaluate ( n k -- n ) 565 566 \ Set $os_console 567 s" activate_osconsole" evaluate ( n -- n ) 568 then 569 drop 570 then 571 572 \ 573 \ Initialize the ACPI option status. 574 \ 575 0 menuacpi ! 576 s" menu_acpi" getenv -1 <> if 577 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 578 dup menuacpi ! 579 dup acpi-captions 580 581 s" init_acpi" evaluate 582 583 \ Get the current cycle state (entry to use) 584 s" acpi_state" evaluate @ 48 + ( n -- n y ) 585 586 \ Set the current non-ANSI caption 587 2dup swap dup ( n y -- n y y n n ) 588 s" set menu_caption[x]=$menu_caption[x][y]" 589 17 +c! 34 +c! 37 +c! evaluate 590 ( n y y n n c-addr/u -- n y ) 591 592 \ Set the current ANSI caption 593 2dup swap dup ( n y -- n y y n n ) 594 s" set ansi_caption[x]=$ansi_caption[x][y]" 595 17 +c! 34 +c! 37 +c! evaluate 596 ( n y y n n c-addr/u -- n y ) 597 598 \ Initialize cycle state from stored value 599 48 - ( n y -- n k ) 600 s" init_cyclestate" evaluate ( n k -- n ) 601 602 \ Set $acpi-user-options 603 s" activate_acpi" evaluate ( n -- n ) 604 then 605 drop 606 then 607 608 \ 609 \ Initialize kernel captions after parsing $kernels 610 \ 611 0 menukernel ! 612 s" menu_kernel" getenv -1 <> if 613 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 614 dup menukernel ! 615 dup parse-kernels tag-kernels 616 617 \ Get the current cycle state (entry to use) 618 s" kernel_state" evaluate @ 48 + ( n -- n y ) 619 620 \ If state is invalid, reset 621 dup kernmenuidx @ 1- > if 622 drop [char] 0 ( n y -- n 48 ) 623 0 s" kernel_state" evaluate ! 624 over s" init_kernel" evaluate drop 625 then 626 627 \ Set the current non-ANSI caption 628 2dup swap dup ( n y -- n y y n n ) 629 s" set menu_caption[x]=$menu_caption[x][y]" 630 17 +c! 34 +c! 37 +c! evaluate 631 ( n y y n n c-addr/u -- n y ) 632 633 \ Set the current ANSI caption 634 2dup swap dup ( n y -- n y y n n ) 635 s" set ansi_caption[x]=$ansi_caption[x][y]" 636 17 +c! 34 +c! 37 +c! evaluate 637 ( n y y n n c-addr/u -- n y ) 638 639 \ Initialize cycle state from stored value 640 48 - ( n y -- n k ) 641 s" init_cyclestate" evaluate ( n k -- n ) 642 643 \ Set $kernel to $kernel[y] 644 s" activate_kernel" evaluate ( n -- n ) 645 then 646 drop 647 then 648 649 \ 650 \ Initialize the menu_options visual separator. 651 \ 652 0 menuoptions ! 653 s" menu_options" getenv -1 <> if 654 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 655 menuoptions ! 656 else 657 drop 658 then 659 then 660 661 \ Initialize "Reboot" menu state variable (prevents double-entry) 662 false menurebootadded ! 663 664 menu_start 665 1- menuidx ! \ Initialize the starting index for the menu 666 0 menurow ! \ Initialize the starting position for the menu 667 668 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 669 begin 670 \ If the "Options:" separator, print it. 671 dup menuoptions @ = if 672 \ Optionally add a reboot option to the menu 673 s" menu_reboot" getenv -1 <> if 674 drop 675 s" Reboot" printmenuitem menureboot ! 676 true menurebootadded ! 677 then 678 679 menuX @ 680 menurow @ 2 + menurow ! 681 menurow @ menuY @ + 682 at-xy 683 s" menu_optionstext" getenv dup -1 <> if 684 type 685 else 686 drop ." Options:" 687 then 688 then 689 690 \ make sure we have not already initialized this item 691 dup init_stateN dup @ 0= if 692 1 swap ! 693 694 \ If this menuitem has an initializer, run it 695 dup menu_init[x] 696 getenv dup -1 <> if 697 evaluate 698 else 699 drop 700 then 701 else 702 drop 703 then 704 705 dup 706 loader_color? if 707 ansi_caption[x] 708 else 709 menu_caption[x] 710 then 711 712 dup -1 <> if 713 \ test for environment variable 714 getenv dup -1 <> if 715 printmenuitem ( c-addr/u -- n ) 716 dup menukeyN ! 717 else 718 drop 719 then 720 else 721 drop 722 then 723 724 1+ dup 56 > \ add 1 to iterator, continue if less than 57 725 until 726 drop \ iterator 727 728 \ Optionally add a reboot option to the menu 729 menurebootadded @ true <> if 730 s" menu_reboot" getenv -1 <> if 731 drop \ no need for the value 732 s" Reboot" \ menu caption (required by printmenuitem) 733 734 printmenuitem 735 menureboot ! 736 else 737 0 menureboot ! 738 then 739 then 740; 741 742\ Takes a single integer on the stack and updates the timeout display. The 743\ integer must be between 0 and 9 (we will only update a single digit in the 744\ source message). 745\ 746: menu-timeout-update ( N -- ) 747 748 \ Enforce minimum/maximum 749 dup 9 > if drop 9 then 750 dup 0 < if drop 0 then 751 752 s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u ) 753 754 2 pick 0> if 755 rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII 756 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above 757 758 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 759 type ( c-addr/u -- ) \ print message 760 else 761 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 762 spaces ( n c-addr/u -- n c-addr ) \ erase message 763 2drop ( n c-addr -- ) 764 then 765 766 0 25 at-xy ( position cursor back at bottom-left ) 767; 768 769\ This function blocks program flow (loops forever) until a key is pressed. 770\ The key that was pressed is added to the top of the stack in the form of its 771\ decimal ASCII representation. This function is called by the menu-display 772\ function. You need not call it directly. 773\ note, the esc sequences will be dropped, this needs to be changed if 774\ menu is built based on arrow keys. 775\ 776: getkey ( -- ascii_keycode ) 777 778 begin \ loop forever 779 780 menu_timeout_enabled @ 1 = if 781 ( -- ) 782 seconds ( get current time: -- N ) 783 dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 784 785 \ At least 1 second has elapsed since last loop 786 \ so we will decrement our "timeout" (really a 787 \ counter, insuring that we do not proceed too 788 \ fast) and update our timeout display. 789 790 menu_time ! ( update time record: N -- ) 791 menu_timeout @ ( "time" remaining: -- N ) 792 dup 0> if ( greater than 0?: N N 0 -- N ) 793 1- ( decrement counter: N -- N ) 794 dup menu_timeout ! 795 ( re-assign: N N Addr -- N ) 796 then 797 ( -- N ) 798 799 dup 0= swap 0< or if ( N <= 0?: N N -- ) 800 \ halt the timer 801 0 menu_timeout ! ( 0 Addr -- ) 802 0 menu_timeout_enabled ! ( 0 Addr -- ) 803 then 804 805 \ update the timer display ( N -- ) 806 menu_timeout @ menu-timeout-update 807 808 menu_timeout @ 0= if 809 \ We've reached the end of the timeout 810 \ (user did not cancel by pressing ANY 811 \ key) 812 813 s" menu_timeout_command" getenv dup 814 -1 = if 815 drop \ clean-up 816 else 817 evaluate 818 then 819 then 820 821 else ( -- N ) 822 \ No [detectable] time has elapsed (in seconds) 823 drop ( N -- ) 824 then 825 ( -- ) 826 then 827 828 key? if \ Was a key pressed? (see loader(8)) 829 830 \ An actual key was pressed (if the timeout is running, 831 \ kill it regardless of which key was pressed) 832 menu_timeout @ 0<> if 833 0 menu_timeout ! 834 0 menu_timeout_enabled ! 835 836 \ clear screen of timeout message 837 0 menu-timeout-update 838 then 839 840 \ get the key that was pressed and exit (if we 841 \ get a non-zero ASCII code) 842 key dup 0<> if 843 dup 0x1b = if 844 key? if ( is it sequence? ) 845 drop 846 begin 847 key? 848 while 849 key drop 850 repeat 851 else 852 exit 853 then 854 else 855 exit 856 then 857 else 858 drop 859 then 860 then 861 50 ms \ sleep for 50 milliseconds (see loader(8)) 862 863 again 864; 865 866: menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1. 867 868 \ Clear the screen area associated with the interactive menu 869 menuX @ menuY @ 870 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 871 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 872 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 873 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 874 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 875 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 876 2drop 877 878 \ Reset the starting index and position for the menu 879 menu_start 1- menuidx ! 880 0 menurow ! 881; 882 883only forth 884also menu-infrastructure 885also menu-namespace 886also menu-command-helpers definitions 887 888: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state 889 890 \ ASCII numeral equal to user-selected menu item must be on the stack. 891 \ We do not modify the stack, so the ASCII numeral is left on top. 892 893 dup init_textN c@ 0= if 894 \ NOTE: no need to check toggle_stateN since the first time we 895 \ are called, we will populate init_textN. Further, we don't 896 \ need to test whether menu_caption[x] (ansi_caption[x] when 897 \ loader_color?=1) is available since we would not have been 898 \ called if the caption was NULL. 899 900 \ base name of environment variable 901 dup ( n -- n n ) \ key pressed 902 loader_color? if 903 ansi_caption[x] 904 else 905 menu_caption[x] 906 then 907 getenv dup -1 <> if 908 909 2 pick ( n c-addr/u -- n c-addr/u n ) 910 init_textN ( n c-addr/u n -- n c-addr/u c-addr ) 911 912 \ now we have the buffer c-addr on top 913 \ ( followed by c-addr/u of current caption ) 914 915 \ Copy the current caption into our buffer 916 2dup c! -rot \ store strlen at first byte 917 begin 918 rot 1+ \ bring alt addr to top and increment 919 -rot -rot \ bring buffer addr to top 920 2dup c@ swap c! \ copy current character 921 1+ \ increment buffer addr 922 rot 1- \ bring buffer len to top and decrement 923 dup 0= \ exit loop if buffer len is zero 924 until 925 2drop \ buffer len/addr 926 drop \ alt addr 927 928 else 929 drop 930 then 931 then 932 933 \ Now we are certain to have init_textN populated with the initial 934 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). 935 \ We can now use init_textN as the untoggled caption and 936 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the 937 \ toggled caption and store the appropriate value into menu_caption[x] 938 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll 939 \ negate the toggled state so that we reverse the flow on subsequent 940 \ calls. 941 942 dup toggle_stateN @ 0= if 943 \ state is OFF, toggle to ON 944 945 dup ( n -- n n ) \ key pressed 946 loader_color? if 947 toggled_ansi[x] 948 else 949 toggled_text[x] 950 then 951 getenv dup -1 <> if 952 \ Assign toggled text to menu caption 953 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 954 loader_color? if 955 ansi_caption[x] 956 else 957 menu_caption[x] 958 then 959 setenv 960 else 961 \ No toggled text, keep the same caption 962 drop ( n -1 -- n ) \ getenv cruft 963 then 964 965 true \ new value of toggle state var (to be stored later) 966 else 967 \ state is ON, toggle to OFF 968 969 dup init_textN count ( n -- n c-addr/u ) 970 971 \ Assign init_textN text to menu caption 972 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 973 loader_color? if 974 ansi_caption[x] 975 else 976 menu_caption[x] 977 then 978 setenv 979 980 false \ new value of toggle state var (to be stored below) 981 then 982 983 \ now we'll store the new toggle state (on top of stack) 984 over toggle_stateN ! 985; 986 987: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem 988 989 \ ASCII numeral equal to user-selected menu item must be on the stack. 990 \ We do not modify the stack, so the ASCII numeral is left on top. 991 992 dup cycle_stateN dup @ 1+ \ get value and increment 993 994 \ Before assigning the (incremented) value back to the pointer, 995 \ let's test for the existence of this particular array element. 996 \ If the element exists, we'll store index value and move on. 997 \ Otherwise, we'll loop around to zero and store that. 998 999 dup 48 + ( n addr k -- n addr k k' ) 1000 \ duplicate array index and convert to ASCII numeral 1001 1002 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y) 1003 loader_color? if 1004 ansi_caption[x][y] 1005 else 1006 menu_caption[x][y] 1007 then 1008 ( n addr k n k' -- n addr k c-addr/u ) 1009 1010 \ Now test for the existence of our incremented array index in the 1011 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color 1012 \ enabled) as set in loader.rc(5), et. al. 1013 1014 getenv dup -1 = if 1015 \ No caption set for this array index. Loop back to zero. 1016 1017 drop ( n addr k -1 -- n addr k ) \ getenv cruft 1018 drop 0 ( n addr k -- n addr 0 ) \ new value to store later 1019 1020 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y) 1021 loader_color? if 1022 ansi_caption[x][y] 1023 else 1024 menu_caption[x][y] 1025 then 1026 ( n addr 0 n 48 -- n addr 0 c-addr/u ) 1027 getenv dup -1 = if 1028 \ Highly unlikely to occur, but to ensure things move 1029 \ along smoothly, allocate a temporary NULL string 1030 drop ( cruft ) s" " 1031 then 1032 then 1033 1034 \ At this point, we should have the following on the stack (in order, 1035 \ from bottom to top): 1036 \ 1037 \ n - Ascii numeral representing the menu choice (inherited) 1038 \ addr - address of our internal cycle_stateN variable 1039 \ k - zero-based number we intend to store to the above 1040 \ c-addr/u - string value we intend to store to menu_caption[x] 1041 \ (or ansi_caption[x] with loader_color enabled) 1042 \ 1043 \ Let's perform what we need to with the above. 1044 1045 \ Assign array value text to menu caption 1046 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n ) 1047 loader_color? if 1048 ansi_caption[x] 1049 else 1050 menu_caption[x] 1051 then 1052 setenv 1053 1054 swap ! ( n addr k -- n ) \ update array state variable 1055; 1056 1057only forth definitions also menu-infrastructure 1058 1059\ Erase and redraw the menu. Useful if you change a caption and want to 1060\ update the menu to reflect the new value. 1061\ 1062: menu-redraw ( -- ) 1063 menu-erase 1064 menu-create 1065; 1066 1067\ This function initializes the menu. Call this from your `loader.rc' file 1068\ before calling any other menu-related functions. 1069\ 1070: menu-init ( -- ) 1071 menu_start 1072 1- menuidx ! \ Initialize the starting index for the menu 1073 0 menurow ! \ Initialize the starting position for the menu 1074 1075 \ Assign configuration values 1076 s" loader_menu_y" getenv dup -1 = if 1077 drop \ no custom row position 1078 menu_default_y 1079 else 1080 \ make sure custom position is a number 1081 ?number 0= if 1082 menu_default_y \ or use default 1083 then 1084 then 1085 menuY ! 1086 s" loader_menu_x" getenv dup -1 = if 1087 drop \ no custom column position 1088 menu_default_x 1089 else 1090 \ make sure custom position is a number 1091 ?number 0= if 1092 menu_default_x \ or use default 1093 then 1094 then 1095 menuX ! 1096 1097 \ Interpret a custom frame type for the menu 1098 TRUE ( draw a box? default yes, but might be altered below ) 1099 s" loader_menu_frame" getenv dup -1 = if ( 1 ) 1100 drop \ no custom frame type 1101 else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 ) 1102 f_single ( see frames.4th ) 1103 else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 ) 1104 f_double ( see frames.4th ) 1105 else ( 3 ) s" none" compare-insensitive 0= if ( 4 ) 1106 drop FALSE \ don't draw a box 1107 ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then 1108 if 1109 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y) 1110 then 1111 1112 0 25 at-xy \ Move cursor to the bottom for output 1113; 1114 1115also menu-namespace 1116 1117\ Main function. Call this from your `loader.rc' file. 1118\ 1119: menu-display ( -- ) 1120 1121 0 menu_timeout_enabled ! \ start with automatic timeout disabled 1122 1123 \ check indication that automatic execution after delay is requested 1124 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 1125 drop ( just testing existence right now: Addr -- ) 1126 1127 \ initialize state variables 1128 seconds menu_time ! ( store the time we started ) 1129 1 menu_timeout_enabled ! ( enable automatic timeout ) 1130 1131 \ read custom time-duration (if set) 1132 s" autoboot_delay" getenv dup -1 = if 1133 drop \ no custom duration (remove dup'd bunk -1) 1134 menu_timeout_default \ use default setting 1135 else 1136 2dup ?number 0= if ( if not a number ) 1137 \ disable timeout if "NO", else use default 1138 s" NO" compare-insensitive 0= if 1139 0 menu_timeout_enabled ! 1140 0 ( assigned to menu_timeout below ) 1141 else 1142 menu_timeout_default 1143 then 1144 else 1145 -rot 2drop 1146 1147 \ boot immediately if less than zero 1148 dup 0< if 1149 drop 1150 menu-create 1151 0 25 at-xy 1152 0 boot 1153 then 1154 then 1155 then 1156 menu_timeout ! ( store value on stack from above ) 1157 1158 menu_timeout_enabled @ 1 = if 1159 \ read custom column position (if set) 1160 s" loader_menu_timeout_x" getenv dup -1 = if 1161 drop \ no custom column position 1162 menu_timeout_default_x \ use default setting 1163 else 1164 \ make sure custom position is a number 1165 ?number 0= if 1166 menu_timeout_default_x \ or use default 1167 then 1168 then 1169 menu_timeout_x ! ( store value on stack from above ) 1170 1171 \ read custom row position (if set) 1172 s" loader_menu_timeout_y" getenv dup -1 = if 1173 drop \ no custom row position 1174 menu_timeout_default_y \ use default setting 1175 else 1176 \ make sure custom position is a number 1177 ?number 0= if 1178 menu_timeout_default_y \ or use default 1179 then 1180 then 1181 menu_timeout_y ! ( store value on stack from above ) 1182 then 1183 then 1184 1185 menu-create 1186 1187 begin \ Loop forever 1188 1189 0 25 at-xy \ Move cursor to the bottom for output 1190 getkey \ Block here, waiting for a key to be pressed 1191 1192 dup -1 = if 1193 drop exit \ Caught abort (abnormal return) 1194 then 1195 1196 \ Boot if the user pressed Enter/Ctrl-M (13) or 1197 \ Ctrl-Enter/Ctrl-J (10) 1198 dup over 13 = swap 10 = or if 1199 drop ( no longer needed ) 1200 s" boot" evaluate 1201 exit ( pedantic; never reached ) 1202 then 1203 1204 dup menureboot @ = if 0 reboot then 1205 1206 \ Evaluate the decimal ASCII value against known menu item 1207 \ key associations and act accordingly 1208 1209 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1210 begin 1211 dup menukeyN @ 1212 rot tuck = if 1213 1214 \ Adjust for missing ACPI menuitem on non-i386 1215\ arch-i386? true <> menuacpi @ 0<> and if 1216\ menuacpi @ over 2dup < -rot = or 1217\ over 58 < and if 1218\ ( key >= menuacpi && key < 58: N -- N ) 1219\ 1+ 1220\ then 1221\ then 1222 1223 \ Test for the environment variable 1224 dup menu_command[x] 1225 getenv dup -1 <> if 1226 \ Execute the stored procedure 1227 evaluate 1228 1229 \ We expect there to be a non-zero 1230 \ value left on the stack after 1231 \ executing the stored procedure. 1232 \ If so, continue to run, else exit. 1233 1234 0= if 1235 drop \ key pressed 1236 drop \ loop iterator 1237 exit 1238 else 1239 swap \ need iterator on top 1240 then 1241 then 1242 1243 \ Re-adjust for missing ACPI menuitem 1244\ arch-i386? true <> menuacpi @ 0<> and if 1245\ swap 1246\ menuacpi @ 1+ over 2dup < -rot = or 1247\ over 59 < and if 1248\ 1- 1249\ then 1250\ swap 1251\ then 1252 else 1253 swap \ need iterator on top 1254 then 1255 1256 \ 1257 \ Check for menu keycode shortcut(s) 1258 \ 1259 dup menu_keycode[x] 1260 getenv dup -1 = if 1261 drop 1262 else 1263 ?number 0<> if 1264 rot tuck = if 1265 swap 1266 dup menu_command[x] 1267 getenv dup -1 <> if 1268 evaluate 1269 0= if 1270 2drop 1271 exit 1272 then 1273 else 1274 drop 1275 then 1276 else 1277 swap 1278 then 1279 then 1280 then 1281 1282 1+ dup 56 > \ increment iterator 1283 \ continue if less than 57 1284 until 1285 drop \ loop iterator 1286 drop \ key pressed 1287 1288 again \ Non-operational key was pressed; repeat 1289; 1290 1291\ This function unsets all the possible environment variables associated with 1292\ creating the interactive menu. 1293\ 1294: menu-unset ( -- ) 1295 1296 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1297 begin 1298 dup menu_init[x] unsetenv \ menu initializer 1299 dup menu_command[x] unsetenv \ menu command 1300 dup menu_caption[x] unsetenv \ menu caption 1301 dup ansi_caption[x] unsetenv \ ANSI caption 1302 dup menu_keycode[x] unsetenv \ menu keycode 1303 dup toggled_text[x] unsetenv \ toggle_menuitem caption 1304 dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption 1305 1306 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9') 1307 begin 1308 \ cycle_menuitem caption and ANSI caption 1309 2dup menu_caption[x][y] unsetenv 1310 2dup ansi_caption[x][y] unsetenv 1311 1+ dup 57 > 1312 until 1313 drop \ inner iterator 1314 1315 0 over menukeyN ! \ used by menu-create, menu-display 1316 0 over init_stateN ! \ used by menu-create 1317 0 over toggle_stateN ! \ used by toggle_menuitem 1318 0 over init_textN c! \ used by toggle_menuitem 1319 0 over cycle_stateN ! \ used by cycle_menuitem 1320 1321 1+ dup 56 > \ increment, continue if less than 57 1322 until 1323 drop \ iterator 1324 1325 s" menu_timeout_command" unsetenv \ menu timeout command 1326 s" menu_reboot" unsetenv \ Reboot menu option flag 1327 s" menu_acpi" unsetenv \ ACPI menu option flag 1328 s" menu_osconsole" unsetenv \ osconsole menu option flag 1329 s" menu_kernel" unsetenv \ Kernel menu option flag 1330 s" menu_options" unsetenv \ Options separator flag 1331 s" menu_optionstext" unsetenv \ separator display text 1332 s" menu_init" unsetenv \ menu initializer 1333 1334 0 menureboot ! 1335 0 menuacpi ! 1336 0 menuosconsole ! 1337 0 menuoptions ! 1338; 1339 1340only forth definitions also menu-infrastructure 1341 1342\ This function both unsets menu variables and visually erases the menu area 1343\ in-preparation for another menu. 1344\ 1345: menu-clear ( -- ) 1346 menu-unset 1347 menu-erase 1348; 1349 1350bullet menubllt ! 1351 1352also menu-namespace 1353 1354\ Initialize our menu initialization state variables 13550 init_state1 ! 13560 init_state2 ! 13570 init_state3 ! 13580 init_state4 ! 13590 init_state5 ! 13600 init_state6 ! 13610 init_state7 ! 13620 init_state8 ! 1363 1364\ Initialize our boolean state variables 13650 toggle_state1 ! 13660 toggle_state2 ! 13670 toggle_state3 ! 13680 toggle_state4 ! 13690 toggle_state5 ! 13700 toggle_state6 ! 13710 toggle_state7 ! 13720 toggle_state8 ! 1373 1374\ Initialize our array state variables 13750 cycle_state1 ! 13760 cycle_state2 ! 13770 cycle_state3 ! 13780 cycle_state4 ! 13790 cycle_state5 ! 13800 cycle_state6 ! 13810 cycle_state7 ! 13820 cycle_state8 ! 1383 1384\ Initialize string containers 13850 init_text1 c! 13860 init_text2 c! 13870 init_text3 c! 13880 init_text4 c! 13890 init_text5 c! 13900 init_text6 c! 13910 init_text7 c! 13920 init_text8 c! 1393 1394only forth definitions 1395