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