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 ()