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