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