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