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