1222417Sjulian\ Copyright (c) 2003 Scott Long <scottl@freebsd.org> 2222417Sjulian\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com> 3254105Sdteske\ Copyright (c) 2006-2013 Devin Teske <dteske@FreeBSD.org> 4222417Sjulian\ All rights reserved. 5222417Sjulian\ 6222417Sjulian\ Redistribution and use in source and binary forms, with or without 7222417Sjulian\ modification, are permitted provided that the following conditions 8222417Sjulian\ are met: 9222417Sjulian\ 1. Redistributions of source code must retain the above copyright 10222417Sjulian\ notice, this list of conditions and the following disclaimer. 11222417Sjulian\ 2. Redistributions in binary form must reproduce the above copyright 12222417Sjulian\ notice, this list of conditions and the following disclaimer in the 13222417Sjulian\ documentation and/or other materials provided with the distribution. 14222417Sjulian\ 15222417Sjulian\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16222417Sjulian\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17222417Sjulian\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18222417Sjulian\ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19222417Sjulian\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20222417Sjulian\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21222417Sjulian\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22222417Sjulian\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23222417Sjulian\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24222417Sjulian\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25222417Sjulian\ SUCH DAMAGE. 26222417Sjulian\ 27222417Sjulian\ $FreeBSD$ 28222417Sjulian 29222417Sjulianmarker task-menu.4th 30222417Sjulian 31222417Sjulian\ Frame drawing 32222417Sjulianinclude /boot/frames.4th 33222417Sjulian 34222417Sjulianf_double \ Set frames to double (see frames.4th). Replace with 35222417Sjulian \ f_single if you want single frames. 36222417Sjulian46 constant dot \ ASCII definition of a period (in decimal) 37222417Sjulian 38254108Sdteske 5 constant menu_default_x \ default column position of timeout 39254108Sdteske10 constant menu_default_y \ default row position of timeout msg 40222417Sjulian 4 constant menu_timeout_default_x \ default column position of timeout 41222417Sjulian23 constant menu_timeout_default_y \ default row position of timeout msg 42222417Sjulian10 constant menu_timeout_default \ default timeout (in seconds) 43222417Sjulian 44222417Sjulian\ Customize the following values with care 45222417Sjulian 46222417Sjulian 1 constant menu_start \ Numerical prefix of first menu item 47222417Sjuliandot constant bullet \ Menu bullet (appears after numerical prefix) 48222417Sjulian 5 constant menu_x \ Row position of the menu (from the top) 49222417Sjulian 10 constant menu_y \ Column position of the menu (from left side) 50222417Sjulian 51222417Sjulian\ Menu Appearance 52222417Sjulianvariable menuidx \ Menu item stack for number prefixes 53222417Sjulianvariable menurow \ Menu item stack for positioning 54222417Sjulianvariable menubllt \ Menu item bullet 55222417Sjulian 56222417Sjulian\ Menu Positioning 57222417Sjulianvariable menuX \ Menu X offset (columns) 58222417Sjulianvariable menuY \ Menu Y offset (rows) 59222417Sjulian 60222417Sjulian\ Menu-item key association/detection 61222417Sjulianvariable menukey1 62222417Sjulianvariable menukey2 63222417Sjulianvariable menukey3 64222417Sjulianvariable menukey4 65222417Sjulianvariable menukey5 66222417Sjulianvariable menukey6 67222417Sjulianvariable menukey7 68222417Sjulianvariable menukey8 69222417Sjulianvariable menureboot 70222417Sjulianvariable menurebootadded 71222417Sjulianvariable menuacpi 72222417Sjulianvariable menuoptions 73262701Sdteskevariable menukernel 74222417Sjulian 75262701Sdteske\ Parsing of kernels into menu-items 76262701Sdteskevariable kernidx 77262701Sdteskevariable kernlen 78262701Sdteskevariable kernmenuidx 79262701Sdteske 80222417Sjulian\ Menu timer [count-down] variables 81222417Sjulianvariable menu_timeout_enabled \ timeout state (internal use only) 82222417Sjulianvariable menu_time \ variable for tracking the passage of time 83222417Sjulianvariable menu_timeout \ determined configurable delay duration 84222417Sjulianvariable menu_timeout_x \ column position of timeout message 85222417Sjulianvariable menu_timeout_y \ row position of timeout message 86222417Sjulian 87241523Sdteske\ Menu initialization status variables 88241523Sdteskevariable init_state1 89241523Sdteskevariable init_state2 90241523Sdteskevariable init_state3 91241523Sdteskevariable init_state4 92241523Sdteskevariable init_state5 93241523Sdteskevariable init_state6 94241523Sdteskevariable init_state7 95241523Sdteskevariable init_state8 96241523Sdteske 97222417Sjulian\ Boolean option status variables 98222417Sjulianvariable toggle_state1 99222417Sjulianvariable toggle_state2 100222417Sjulianvariable toggle_state3 101222417Sjulianvariable toggle_state4 102222417Sjulianvariable toggle_state5 103222417Sjulianvariable toggle_state6 104222417Sjulianvariable toggle_state7 105222417Sjulianvariable toggle_state8 106222417Sjulian 107222417Sjulian\ Array option status variables 108222417Sjulianvariable cycle_state1 109222417Sjulianvariable cycle_state2 110222417Sjulianvariable cycle_state3 111222417Sjulianvariable cycle_state4 112222417Sjulianvariable cycle_state5 113222417Sjulianvariable cycle_state6 114222417Sjulianvariable cycle_state7 115222417Sjulianvariable cycle_state8 116222417Sjulian 117222417Sjulian\ Containers for storing the initial caption text 118262701Sdteskecreate init_text1 64 allot 119262701Sdteskecreate init_text2 64 allot 120262701Sdteskecreate init_text3 64 allot 121262701Sdteskecreate init_text4 64 allot 122262701Sdteskecreate init_text5 64 allot 123262701Sdteskecreate init_text6 64 allot 124262701Sdteskecreate init_text7 64 allot 125262701Sdteskecreate init_text8 64 allot 126222417Sjulian 127262701Sdteske\ Containers for parsing kernels into menu-items 128262701Sdteskecreate kerncapbuf 64 allot 129262701Sdteskecreate kerndefault 64 allot 130262701Sdteskecreate kernelsbuf 256 allot 131262701Sdteske 132243114Sdteske: +c! ( N C-ADDR/U K -- C-ADDR/U ) 133243114Sdteske 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr ) 134243114Sdteske rot + c! ( n c-addr/u k n c-addr -- n c-addr/u ) 135243114Sdteske rot drop ( n c-addr/u -- c-addr/u ) 136243114Sdteske; 137243114Sdteske 138262701Sdteske: delim? ( C -- BOOL ) 139262701Sdteske dup 32 = ( c -- c bool ) \ [sp] space 140262701Sdteske over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab 141262701Sdteske over 10 = or ( c bool -- c bool ) \ [nl] newline 142262701Sdteske over 13 = or ( c bool -- c bool ) \ [cr] carriage return 143262701Sdteske over [char] , = or ( c bool -- c bool ) \ comma 144262701Sdteske swap drop ( c bool -- bool ) \ return boolean 145262701Sdteske; 146262701Sdteske 147243114Sdteske: menukeyN ( N -- ADDR ) s" menukeyN" 7 +c! evaluate ; 148243114Sdteske: init_stateN ( N -- ADDR ) s" init_stateN" 10 +c! evaluate ; 149243114Sdteske: toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 +c! evaluate ; 150243114Sdteske: cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 +c! evaluate ; 151243114Sdteske: init_textN ( N -- C-ADDR ) s" init_textN" 9 +c! evaluate ; 152243114Sdteske 153262701Sdteske: kernel[x] ( N -- C-ADDR/U ) s" kernel[x]" 7 +c! ; 154262701Sdteske: menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ; 155262701Sdteske: menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ; 156262701Sdteske: menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ; 157262701Sdteske: ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ; 158262701Sdteske: menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ; 159262701Sdteske: toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ; 160262701Sdteske: toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ; 161262701Sdteske: menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ; 162262701Sdteske: ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ; 163243114Sdteske 164222417Sjulian: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise. 165222417Sjulian s" arch-i386" environment? dup if 166222417Sjulian drop 167222417Sjulian then 168222417Sjulian; 169222417Sjulian 170222417Sjulian\ This function prints a menu item at menuX (row) and menuY (column), returns 171222417Sjulian\ the incremental decimal ASCII value associated with the menu item, and 172222417Sjulian\ increments the cursor position to the next row for the creation of the next 173222417Sjulian\ menu item. This function is called by the menu-create function. You need not 174222417Sjulian\ call it directly. 175222417Sjulian\ 176222417Sjulian: printmenuitem ( menu_item_str -- ascii_keycode ) 177222417Sjulian 178222417Sjulian menurow dup @ 1+ swap ! ( increment menurow ) 179222417Sjulian menuidx dup @ 1+ swap ! ( increment menuidx ) 180222417Sjulian 181222417Sjulian \ Calculate the menuitem row position 182222417Sjulian menurow @ menuY @ + 183222417Sjulian 184222417Sjulian \ Position the cursor at the menuitem position 185222417Sjulian dup menuX @ swap at-xy 186222417Sjulian 187222417Sjulian \ Print the value of menuidx 188222417Sjulian loader_color? if 189228985Spluknet ." [1m" ( [22m ) 190222417Sjulian then 191222417Sjulian menuidx @ . 192222417Sjulian loader_color? if 193228985Spluknet ." [37m" ( [39m ) 194222417Sjulian then 195222417Sjulian 196222417Sjulian \ Move the cursor forward 1 column 197222417Sjulian dup menuX @ 1+ swap at-xy 198222417Sjulian 199222417Sjulian menubllt @ emit \ Print the menu bullet using the emit function 200222417Sjulian 201222417Sjulian \ Move the cursor to the 3rd column from the current position 202222417Sjulian \ to allow for a space between the numerical prefix and the 203222417Sjulian \ text caption 204222417Sjulian menuX @ 3 + swap at-xy 205222417Sjulian 206222417Sjulian \ Print the menu caption (we expect a string to be on the stack 207222417Sjulian \ prior to invoking this function) 208222417Sjulian type 209222417Sjulian 210222417Sjulian \ Here we will add the ASCII decimal of the numerical prefix 211222417Sjulian \ to the stack (decimal ASCII for `1' is 49) as a "return value" 212222417Sjulian menuidx @ 48 + 213222417Sjulian; 214222417Sjulian 215222417Sjulian: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state 216222417Sjulian 217222417Sjulian \ ASCII numeral equal to user-selected menu item must be on the stack. 218222417Sjulian \ We do not modify the stack, so the ASCII numeral is left on top. 219222417Sjulian 220243114Sdteske dup init_textN c@ 0= if 221222417Sjulian \ NOTE: no need to check toggle_stateN since the first time we 222222417Sjulian \ are called, we will populate init_textN. Further, we don't 223222417Sjulian \ need to test whether menu_caption[x] (ansi_caption[x] when 224254105Sdteske \ loader_color?=1) is available since we would not have been 225222417Sjulian \ called if the caption was NULL. 226222417Sjulian 227222417Sjulian \ base name of environment variable 228243114Sdteske dup ( n -- n n ) \ key pressed 229222417Sjulian loader_color? if 230243114Sdteske ansi_caption[x] 231222417Sjulian else 232243114Sdteske menu_caption[x] 233222417Sjulian then 234222417Sjulian getenv dup -1 <> if 235222417Sjulian 236243114Sdteske 2 pick ( n c-addr/u -- n c-addr/u n ) 237243114Sdteske init_textN ( n c-addr/u n -- n c-addr/u c-addr ) 238222417Sjulian 239222417Sjulian \ now we have the buffer c-addr on top 240222417Sjulian \ ( followed by c-addr/u of current caption ) 241222417Sjulian 242222417Sjulian \ Copy the current caption into our buffer 243222417Sjulian 2dup c! -rot \ store strlen at first byte 244222417Sjulian begin 245222417Sjulian rot 1+ \ bring alt addr to top and increment 246222417Sjulian -rot -rot \ bring buffer addr to top 247222417Sjulian 2dup c@ swap c! \ copy current character 248222417Sjulian 1+ \ increment buffer addr 249222417Sjulian rot 1- \ bring buffer len to top and decrement 250222417Sjulian dup 0= \ exit loop if buffer len is zero 251222417Sjulian until 252222417Sjulian 2drop \ buffer len/addr 253222417Sjulian drop \ alt addr 254222417Sjulian 255222417Sjulian else 256222417Sjulian drop 257222417Sjulian then 258222417Sjulian then 259222417Sjulian 260222417Sjulian \ Now we are certain to have init_textN populated with the initial 261222417Sjulian \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled). 262222417Sjulian \ We can now use init_textN as the untoggled caption and 263222417Sjulian \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the 264222417Sjulian \ toggled caption and store the appropriate value into menu_caption[x] 265222417Sjulian \ (again, ansi_caption[x] with loader_color enabled). Last, we'll 266222417Sjulian \ negate the toggled state so that we reverse the flow on subsequent 267222417Sjulian \ calls. 268222417Sjulian 269243114Sdteske dup toggle_stateN @ 0= if 270222417Sjulian \ state is OFF, toggle to ON 271222417Sjulian 272243114Sdteske dup ( n -- n n ) \ key pressed 273222417Sjulian loader_color? if 274243114Sdteske toggled_ansi[x] 275222417Sjulian else 276243114Sdteske toggled_text[x] 277222417Sjulian then 278222417Sjulian getenv dup -1 <> if 279222417Sjulian \ Assign toggled text to menu caption 280243114Sdteske 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 281222417Sjulian loader_color? if 282243114Sdteske ansi_caption[x] 283222417Sjulian else 284243114Sdteske menu_caption[x] 285222417Sjulian then 286243114Sdteske setenv 287222417Sjulian else 288222417Sjulian \ No toggled text, keep the same caption 289243114Sdteske drop ( n -1 -- n ) \ getenv cruft 290222417Sjulian then 291222417Sjulian 292222417Sjulian true \ new value of toggle state var (to be stored later) 293222417Sjulian else 294222417Sjulian \ state is ON, toggle to OFF 295222417Sjulian 296243114Sdteske dup init_textN count ( n -- n c-addr/u ) 297222417Sjulian 298243114Sdteske \ Assign init_textN text to menu caption 299243114Sdteske 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed 300222417Sjulian loader_color? if 301243114Sdteske ansi_caption[x] 302222417Sjulian else 303243114Sdteske menu_caption[x] 304222417Sjulian then 305243114Sdteske setenv 306222417Sjulian 307243114Sdteske false \ new value of toggle state var (to be stored below) 308222417Sjulian then 309222417Sjulian 310222417Sjulian \ now we'll store the new toggle state (on top of stack) 311243114Sdteske over toggle_stateN ! 312222417Sjulian; 313222417Sjulian 314222417Sjulian: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem 315222417Sjulian 316222417Sjulian \ ASCII numeral equal to user-selected menu item must be on the stack. 317222417Sjulian \ We do not modify the stack, so the ASCII numeral is left on top. 318222417Sjulian 319243114Sdteske dup cycle_stateN dup @ 1+ \ get value and increment 320222417Sjulian 321222417Sjulian \ Before assigning the (incremented) value back to the pointer, 322222417Sjulian \ let's test for the existence of this particular array element. 323222417Sjulian \ If the element exists, we'll store index value and move on. 324222417Sjulian \ Otherwise, we'll loop around to zero and store that. 325222417Sjulian 326243114Sdteske dup 48 + ( n addr k -- n addr k k' ) 327243114Sdteske \ duplicate array index and convert to ASCII numeral 328222417Sjulian 329243114Sdteske 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y) 330222417Sjulian loader_color? if 331243114Sdteske ansi_caption[x][y] 332222417Sjulian else 333243114Sdteske menu_caption[x][y] 334222417Sjulian then 335243114Sdteske ( n addr k n k' -- n addr k c-addr/u ) 336222417Sjulian 337222417Sjulian \ Now test for the existence of our incremented array index in the 338222417Sjulian \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color 339222417Sjulian \ enabled) as set in loader.rc(5), et. al. 340222417Sjulian 341222417Sjulian getenv dup -1 = if 342222417Sjulian \ No caption set for this array index. Loop back to zero. 343222417Sjulian 344243114Sdteske drop ( n addr k -1 -- n addr k ) \ getenv cruft 345243114Sdteske drop 0 ( n addr k -- n addr 0 ) \ new value to store later 346222417Sjulian 347243114Sdteske 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y) 348222417Sjulian loader_color? if 349243114Sdteske ansi_caption[x][y] 350222417Sjulian else 351243114Sdteske menu_caption[x][y] 352222417Sjulian then 353243114Sdteske ( n addr 0 n 48 -- n addr 0 c-addr/u ) 354222417Sjulian getenv dup -1 = if 355262701Sdteske \ Highly unlikely to occur, but to ensure things move 356262701Sdteske \ along smoothly, allocate a temporary NULL string 357262701Sdteske drop ( cruft ) s" " 358222417Sjulian then 359222417Sjulian then 360222417Sjulian 361222417Sjulian \ At this point, we should have the following on the stack (in order, 362222417Sjulian \ from bottom to top): 363222417Sjulian \ 364243114Sdteske \ n - Ascii numeral representing the menu choice (inherited) 365243114Sdteske \ addr - address of our internal cycle_stateN variable 366243114Sdteske \ k - zero-based number we intend to store to the above 367243114Sdteske \ c-addr/u - string value we intend to store to menu_caption[x] 368243114Sdteske \ (or ansi_caption[x] with loader_color enabled) 369222417Sjulian \ 370222417Sjulian \ Let's perform what we need to with the above. 371222417Sjulian 372243114Sdteske \ Assign array value text to menu caption 373243114Sdteske 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n ) 374222417Sjulian loader_color? if 375243114Sdteske ansi_caption[x] 376222417Sjulian else 377243114Sdteske menu_caption[x] 378222417Sjulian then 379243114Sdteske setenv 380222417Sjulian 381243114Sdteske swap ! ( n addr k -- n ) \ update array state variable 382222417Sjulian; 383222417Sjulian 384222417Sjulian: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise 385222417Sjulian s" hint.acpi.0.rsdp" getenv 386222417Sjulian dup -1 = if 387222417Sjulian drop false exit 388222417Sjulian then 389222417Sjulian 2drop 390222417Sjulian true 391222417Sjulian; 392222417Sjulian 393222417Sjulian: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise 394222417Sjulian s" hint.acpi.0.disabled" getenv 395222417Sjulian dup -1 <> if 396222417Sjulian s" 0" compare 0<> if 397222417Sjulian false exit 398222417Sjulian then 399222417Sjulian else 400222417Sjulian drop 401222417Sjulian then 402222417Sjulian true 403222417Sjulian; 404222417Sjulian 405222417Sjulian\ This function prints the appropriate menuitem basename to the stack if an 406222417Sjulian\ ACPI option is to be presented to the user, otherwise returns -1. Used 407222417Sjulian\ internally by menu-create, you need not (nor should you) call this directly. 408222417Sjulian\ 409241310Sdteske: acpimenuitem ( -- C-Addr/U | -1 ) 410222417Sjulian 411222417Sjulian arch-i386? if 412222417Sjulian acpipresent? if 413222417Sjulian acpienabled? if 414222417Sjulian loader_color? if 415262701Sdteske s" toggled_ansi[x]" 416222417Sjulian else 417262701Sdteske s" toggled_text[x]" 418222417Sjulian then 419222417Sjulian else 420222417Sjulian loader_color? if 421262701Sdteske s" ansi_caption[x]" 422222417Sjulian else 423262701Sdteske s" menu_caption[x]" 424222417Sjulian then 425222417Sjulian then 426222417Sjulian else 427222417Sjulian menuidx dup @ 1+ swap ! ( increment menuidx ) 428222417Sjulian -1 429222417Sjulian then 430222417Sjulian else 431222417Sjulian -1 432222417Sjulian then 433222417Sjulian; 434222417Sjulian 435262701Sdteske\ This function parses $kernels into variables that are used by the menu to 436262701Sdteske\ display wich kernel to boot when the [overloaded] `boot' word is interpreted. 437262701Sdteske\ Used internally by menu-create, you need not (nor should you) call this 438262701Sdteske\ directly. 439262701Sdteske\ 440262701Sdteske: parse-kernels ( N -- ) \ kernidx 441262701Sdteske kernidx ! ( n -- ) \ store provided `x' value 442262701Sdteske [char] 0 kernmenuidx ! \ initialize `y' value for menu_caption[x][y] 443262701Sdteske 444262701Sdteske \ Attempt to get a list of kernels, fall back to sensible default 445262701Sdteske s" kernels" getenv dup -1 = if 446262701Sdteske drop ( cruft ) 447262701Sdteske s" kernel kernel.old" 448262701Sdteske then ( -- c-addr/u ) 449262701Sdteske 450262701Sdteske \ Check to see if the user has altered $kernel by comparing it against 451262701Sdteske \ $kernel[N] where N is kernel_state (the actively displayed kernel). 452262701Sdteske s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv 453262701Sdteske dup -1 <> if 454262701Sdteske s" kernel" getenv dup -1 = if 455262701Sdteske drop ( cruft ) s" " 456262701Sdteske then 457262701Sdteske 2swap 2over compare 0= if 458262701Sdteske 2drop FALSE ( skip below conditional ) 459262701Sdteske else \ User has changed $kernel 460262701Sdteske TRUE ( slurp in new value ) 461262701Sdteske then 462262701Sdteske else \ We haven't yet parsed $kernels into $kernel[N] 463262701Sdteske drop ( getenv cruft ) 464262701Sdteske s" kernel" getenv dup -1 = if 465262701Sdteske drop ( cruft ) s" " 466262701Sdteske then 467262701Sdteske TRUE ( slurp in initial value ) 468262701Sdteske then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 ) 469262701Sdteske if \ slurp new value into kerndefault 470262701Sdteske kerndefault 1+ 0 2swap strcat swap 1- c! 471262701Sdteske then 472262701Sdteske 473262701Sdteske \ Clear out existing parsed-kernels 474262701Sdteske kernidx @ [char] 0 475262701Sdteske begin 476262701Sdteske dup kernel[x] unsetenv 477262701Sdteske 2dup menu_caption[x][y] unsetenv 478262701Sdteske 2dup ansi_caption[x][y] unsetenv 479262701Sdteske 1+ dup [char] 8 > 480262701Sdteske until 481262701Sdteske 2drop 482262701Sdteske 483262701Sdteske \ Step through the string until we find the end 484262701Sdteske begin 485262701Sdteske 0 kernlen ! \ initialize length of value 486262701Sdteske 487262701Sdteske \ Skip leading whitespace and/or comma delimiters 488262701Sdteske begin 489262701Sdteske dup 0<> if 490262701Sdteske over c@ delim? ( c-addr/u -- c-addr/u bool ) 491262701Sdteske else 492262701Sdteske false ( c-addr/u -- c-addr/u bool ) 493262701Sdteske then 494262701Sdteske while 495262701Sdteske 1- swap 1+ swap ( c-addr/u -- c-addr'/u' ) 496262701Sdteske repeat 497262701Sdteske ( c-addr/u -- c-addr'/u' ) 498262701Sdteske 499262701Sdteske dup 0= if \ end of string while eating whitespace 500262701Sdteske 2drop ( c-addr/u -- ) 501262701Sdteske kernmenuidx @ [char] 0 <> if \ found at least one 502262701Sdteske exit \ all done 503262701Sdteske then 504262701Sdteske 505262701Sdteske \ No entries in $kernels; use $kernel instead 506262701Sdteske s" kernel" getenv dup -1 = if 507262701Sdteske drop ( cruft ) s" " 508262701Sdteske then ( -- c-addr/u ) 509262701Sdteske dup kernlen ! \ store entire value length as kernlen 510262701Sdteske else 511262701Sdteske \ We're still within $kernels parsing toward the end; 512262701Sdteske \ find delimiter/end to determine kernlen 513262701Sdteske 2dup ( c-addr/u -- c-addr/u c-addr/u ) 514262701Sdteske begin dup 0<> while 515262701Sdteske over c@ delim? if 516262701Sdteske drop 0 ( break ) \ found delimiter 517262701Sdteske else 518262701Sdteske kernlen @ 1+ kernlen ! \ incrememnt 519262701Sdteske 1- swap 1+ swap \ c-addr++ u-- 520262701Sdteske then 521262701Sdteske repeat 522262701Sdteske 2drop ( c-addr/u c-addr'/u' -- c-addr/u ) 523262701Sdteske 524262701Sdteske \ If this is the first entry, compare it to $kernel 525262701Sdteske \ If different, then insert $kernel beforehand 526262701Sdteske kernmenuidx @ [char] 0 = if 527262701Sdteske over kernlen @ kerndefault count compare if 528262701Sdteske kernelsbuf 0 kerndefault count strcat 529262701Sdteske s" ," strcat 2swap strcat 530262701Sdteske kerndefault count swap drop kernlen ! 531262701Sdteske then 532262701Sdteske then 533262701Sdteske then 534262701Sdteske ( c-addr/u -- c-addr'/u' ) 535262701Sdteske 536262701Sdteske \ At this point, we should have something on the stack to store 537262701Sdteske \ as the next kernel menu option; start assembling variables 538262701Sdteske 539262701Sdteske over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 ) 540262701Sdteske 541262701Sdteske \ Assign first to kernel[x] 542262701Sdteske 2dup kernmenuidx @ kernel[x] setenv 543262701Sdteske 544262701Sdteske \ Assign second to menu_caption[x][y] 545262701Sdteske kerncapbuf 0 s" [K]ernel: " strcat 546262701Sdteske 2over strcat 547262701Sdteske kernidx @ kernmenuidx @ menu_caption[x][y] 548262701Sdteske setenv 549262701Sdteske 550262701Sdteske \ Assign third to ansi_caption[x][y] 551262701Sdteske kerncapbuf 0 s" [1mK[37mernel: " strcat 552262701Sdteske kernmenuidx @ [char] 0 = if 553262701Sdteske s" default/[32m" 554262701Sdteske else 555262701Sdteske s" [34;1m" 556262701Sdteske then strcat 557262701Sdteske 2over strcat 558262701Sdteske s" [37m" strcat 559262701Sdteske kernidx @ kernmenuidx @ ansi_caption[x][y] 560262701Sdteske setenv 561262701Sdteske 562262701Sdteske 2drop ( c-addr/u c-addr/u2 -- c-addr/u ) 563262701Sdteske 564262701Sdteske kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if 565262701Sdteske 2drop ( c-addr/u -- ) exit 566262701Sdteske then 567262701Sdteske 568262701Sdteske kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' ) 569262701Sdteske again 570262701Sdteske; 571262701Sdteske 572262701Sdteske\ This function goes through the kernels that were discovered by the 573262701Sdteske\ parse-kernels function [above], adding " (# of #)" text to the end of each 574262701Sdteske\ caption. 575262701Sdteske\ 576262701Sdteske: tag-kernels ( -- ) 577262701Sdteske kernidx @ ( -- x ) dup 0= if exit then 578262701Sdteske [char] 0 s" (Y of Z)" ( x -- x y c-addr/u ) 579262701Sdteske kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed 580262701Sdteske begin 581262701Sdteske 2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num 582262701Sdteske 583262701Sdteske 2over menu_caption[x][y] getenv dup -1 <> if 584262701Sdteske 2dup + 1- c@ [char] ) = if 585262701Sdteske 2drop \ Already tagged 586262701Sdteske else 587262701Sdteske kerncapbuf 0 2swap strcat 588262701Sdteske 2over strcat 589262701Sdteske 5 pick 5 pick menu_caption[x][y] setenv 590262701Sdteske then 591262701Sdteske else 592262701Sdteske drop ( getenv cruft ) 593262701Sdteske then 594262701Sdteske 595262701Sdteske 2over ansi_caption[x][y] getenv dup -1 <> if 596262701Sdteske 2dup + 1- c@ [char] ) = if 597262701Sdteske 2drop \ Already tagged 598262701Sdteske else 599262701Sdteske kerncapbuf 0 2swap strcat 600262701Sdteske 2over strcat 601262701Sdteske 5 pick 5 pick ansi_caption[x][y] setenv 602262701Sdteske then 603262701Sdteske else 604262701Sdteske drop ( getenv cruft ) 605262701Sdteske then 606262701Sdteske 607262701Sdteske rot 1+ dup [char] 8 > if 608262701Sdteske -rot 2drop TRUE ( break ) 609262701Sdteske else 610262701Sdteske -rot FALSE 611262701Sdteske then 612262701Sdteske until 613262701Sdteske 2drop ( x y -- ) 614262701Sdteske; 615262701Sdteske 616222417Sjulian\ This function creates the list of menu items. This function is called by the 617222417Sjulian\ menu-display function. You need not be call it directly. 618222417Sjulian\ 619222417Sjulian: menu-create ( -- ) 620222417Sjulian 621222417Sjulian \ Print the frame caption at (x,y) 622262701Sdteske s" loader_menu_title" getenv dup -1 = if 623222417Sjulian drop s" Welcome to FreeBSD" 624222417Sjulian then 625254108Sdteske TRUE ( use default alignment ) 626262701Sdteske s" loader_menu_title_align" getenv dup -1 <> if 627254108Sdteske 2dup s" left" compare-insensitive 0= if ( 1 ) 628254108Sdteske 2drop ( c-addr/u ) drop ( bool ) 629254108Sdteske menuX @ menuY @ 1- 630254108Sdteske FALSE ( don't use default alignment ) 631254108Sdteske else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 ) 632254108Sdteske 2drop ( c-addr/u ) drop ( bool ) 633254108Sdteske menuX @ 42 + 4 - over - menuY @ 1- 634254108Sdteske FALSE ( don't use default alignment ) 635254108Sdteske else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then 636254108Sdteske else 637254108Sdteske drop ( getenv cruft ) 638254108Sdteske then 639254108Sdteske if ( use default center alignement? ) 640254108Sdteske menuX @ 19 + over 2 / - menuY @ 1- 641254108Sdteske then 642254108Sdteske at-xy type 643222417Sjulian 644241523Sdteske \ If $menu_init is set, evaluate it (allowing for whole menus to be 645241523Sdteske \ constructed dynamically -- as this function could conceivably set 646241523Sdteske \ the remaining environment variables to construct the menu entirely). 647241523Sdteske \ 648262701Sdteske s" menu_init" getenv dup -1 <> if 649241523Sdteske evaluate 650241523Sdteske else 651241523Sdteske drop 652241523Sdteske then 653241523Sdteske 654222417Sjulian \ Print our menu options with respective key/variable associations. 655222417Sjulian \ `printmenuitem' ends by adding the decimal ASCII value for the 656222417Sjulian \ numerical prefix to the stack. We store the value left on the stack 657222417Sjulian \ to the key binding variable for later testing against a character 658222417Sjulian \ captured by the `getkey' function. 659222417Sjulian 660222417Sjulian \ Note that any menu item beyond 9 will have a numerical prefix on the 661222417Sjulian \ screen consisting of the first digit (ie. 1 for the tenth menu item) 662222417Sjulian \ and the key required to activate that menu item will be the decimal 663222417Sjulian \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:') 664222417Sjulian \ which is misleading and not desirable. 665222417Sjulian \ 666222417Sjulian \ Thus, we do not allow more than 8 configurable items on the menu 667222417Sjulian \ (with "Reboot" as the optional ninth and highest numbered item). 668222417Sjulian 669222417Sjulian \ 670222417Sjulian \ Initialize the ACPI option status. 671222417Sjulian \ 672222417Sjulian 0 menuacpi ! 673262701Sdteske s" menu_acpi" getenv -1 <> if 674222417Sjulian c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 675222417Sjulian menuacpi ! 676222417Sjulian arch-i386? if acpipresent? if 677222417Sjulian \ 678222417Sjulian \ Set menu toggle state to active state 679222417Sjulian \ (required by generic toggle_menuitem) 680222417Sjulian \ 681243114Sdteske acpienabled? menuacpi @ toggle_stateN ! 682222417Sjulian then then 683222417Sjulian else 684222417Sjulian drop 685222417Sjulian then 686222417Sjulian then 687222417Sjulian 688222417Sjulian \ 689262701Sdteske \ Initialize kernel captions after parsing $kernels 690262701Sdteske \ 691262701Sdteske 0 menukernel ! 692262701Sdteske s" menu_kernel" getenv -1 <> if 693262701Sdteske c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 694262701Sdteske dup menukernel ! 695262701Sdteske dup parse-kernels tag-kernels 696262701Sdteske 697262701Sdteske \ Get the current cycle state (entry to use) 698262701Sdteske s" kernel_state" evaluate @ 48 + ( n -- n y ) 699262701Sdteske 700262701Sdteske \ If state is invalid, reset 701262701Sdteske dup kernmenuidx @ 1- > if 702262701Sdteske drop [char] 0 ( n y -- n 48 ) 703262701Sdteske 0 s" kernel_state" evaluate ! 704262701Sdteske over s" init_kernel" evaluate drop 705262701Sdteske then 706262701Sdteske 707262701Sdteske \ Set the current non-ANSI caption 708262701Sdteske 2dup swap dup ( n y -- n y y n n ) 709262701Sdteske s" set menu_caption[x]=$menu_caption[x][y]" 710262701Sdteske 17 +c! 34 +c! 37 +c! evaluate 711262701Sdteske ( n y y n n c-addr/u -- n y ) 712262701Sdteske 713262701Sdteske \ Set the current ANSI caption 714262701Sdteske 2dup swap dup ( n y -- n y y n n ) 715262701Sdteske s" set ansi_caption[x]=$ansi_caption[x][y]" 716262701Sdteske 17 +c! 34 +c! 37 +c! evaluate 717262701Sdteske ( n y y n n c-addr/u -- n y ) 718262701Sdteske 719262701Sdteske \ Initialize cycle state from stored value 720262701Sdteske 48 - ( n y -- n k ) 721262701Sdteske s" init_cyclestate" evaluate ( n k -- n ) 722262701Sdteske 723262701Sdteske \ Set $kernel to $kernel[y] 724262701Sdteske s" activate_kernel" evaluate ( n -- n ) 725262701Sdteske then 726262701Sdteske drop 727262701Sdteske then 728262701Sdteske 729262701Sdteske \ 730222417Sjulian \ Initialize the menu_options visual separator. 731222417Sjulian \ 732222417Sjulian 0 menuoptions ! 733262701Sdteske s" menu_options" getenv -1 <> if 734222417Sjulian c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' ) 735222417Sjulian menuoptions ! 736222417Sjulian else 737222417Sjulian drop 738222417Sjulian then 739222417Sjulian then 740222417Sjulian 741222417Sjulian \ Initialize "Reboot" menu state variable (prevents double-entry) 742222417Sjulian false menurebootadded ! 743222417Sjulian 744242667Sdteske menu_start 745242667Sdteske 1- menuidx ! \ Initialize the starting index for the menu 746242667Sdteske 0 menurow ! \ Initialize the starting position for the menu 747242667Sdteske 748222417Sjulian 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 749222417Sjulian begin 750222417Sjulian \ If the "Options:" separator, print it. 751222417Sjulian dup menuoptions @ = if 752222417Sjulian \ Optionally add a reboot option to the menu 753262701Sdteske s" menu_reboot" getenv -1 <> if 754222417Sjulian drop 755222417Sjulian s" Reboot" printmenuitem menureboot ! 756222417Sjulian true menurebootadded ! 757222417Sjulian then 758222417Sjulian 759222417Sjulian menuX @ 760222417Sjulian menurow @ 2 + menurow ! 761222417Sjulian menurow @ menuY @ + 762222417Sjulian at-xy 763262701Sdteske s" menu_optionstext" getenv dup -1 <> if 764241363Sdteske type 765241363Sdteske else 766241363Sdteske drop ." Options:" 767241363Sdteske then 768222417Sjulian then 769222417Sjulian 770222417Sjulian \ If this is the ACPI menu option, act accordingly. 771222417Sjulian dup menuacpi @ = if 772243114Sdteske dup acpimenuitem ( n -- n n c-addr/u | n n -1 ) 773243114Sdteske dup -1 <> if 774243114Sdteske 13 +c! ( n n c-addr/u -- n c-addr/u ) 775243114Sdteske \ replace 'x' with n 776243114Sdteske else 777243114Sdteske swap drop ( n n -1 -- n -1 ) 778243114Sdteske over menu_command[x] unsetenv 779243114Sdteske then 780222417Sjulian else 781241523Sdteske \ make sure we have not already initialized this item 782243114Sdteske dup init_stateN dup @ 0= if 783241523Sdteske 1 swap ! 784241523Sdteske 785241523Sdteske \ If this menuitem has an initializer, run it 786243114Sdteske dup menu_init[x] 787241523Sdteske getenv dup -1 <> if 788241523Sdteske evaluate 789241523Sdteske else 790241523Sdteske drop 791241523Sdteske then 792241523Sdteske else 793241523Sdteske drop 794241523Sdteske then 795241523Sdteske 796243114Sdteske dup 797222417Sjulian loader_color? if 798243114Sdteske ansi_caption[x] 799222417Sjulian else 800243114Sdteske menu_caption[x] 801222417Sjulian then 802222417Sjulian then 803222417Sjulian 804222417Sjulian dup -1 <> if 805222417Sjulian \ test for environment variable 806222417Sjulian getenv dup -1 <> if 807243114Sdteske printmenuitem ( c-addr/u -- n ) 808243114Sdteske dup menukeyN ! 809222417Sjulian else 810222417Sjulian drop 811222417Sjulian then 812222417Sjulian else 813222417Sjulian drop 814222417Sjulian then 815222417Sjulian 816222417Sjulian 1+ dup 56 > \ add 1 to iterator, continue if less than 57 817222417Sjulian until 818222417Sjulian drop \ iterator 819222417Sjulian 820222417Sjulian \ Optionally add a reboot option to the menu 821222417Sjulian menurebootadded @ true <> if 822262701Sdteske s" menu_reboot" getenv -1 <> if 823222417Sjulian drop \ no need for the value 824222417Sjulian s" Reboot" \ menu caption (required by printmenuitem) 825222417Sjulian 826222417Sjulian printmenuitem 827222417Sjulian menureboot ! 828222417Sjulian else 829222417Sjulian 0 menureboot ! 830222417Sjulian then 831222417Sjulian then 832222417Sjulian; 833222417Sjulian 834222417Sjulian\ Takes a single integer on the stack and updates the timeout display. The 835222417Sjulian\ integer must be between 0 and 9 (we will only update a single digit in the 836222417Sjulian\ source message). 837222417Sjulian\ 838222417Sjulian: menu-timeout-update ( N -- ) 839222417Sjulian 840243114Sdteske \ Enforce minimum/maximum 841243114Sdteske dup 9 > if drop 9 then 842243114Sdteske dup 0 < if drop 0 then 843222417Sjulian 844243114Sdteske s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u ) 845222417Sjulian 846243114Sdteske 2 pick 0> if 847243114Sdteske rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII 848243114Sdteske 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above 849222417Sjulian 850243114Sdteske menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 851243114Sdteske type ( c-addr/u -- ) \ print message 852243114Sdteske else 853243114Sdteske menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor 854243114Sdteske spaces ( n c-addr/u -- n c-addr ) \ erase message 855243114Sdteske 2drop ( n c-addr -- ) 856222417Sjulian then 857222417Sjulian 858222417Sjulian 0 25 at-xy ( position cursor back at bottom-left ) 859222417Sjulian; 860222417Sjulian 861222417Sjulian\ This function blocks program flow (loops forever) until a key is pressed. 862222417Sjulian\ The key that was pressed is added to the top of the stack in the form of its 863222417Sjulian\ decimal ASCII representation. This function is called by the menu-display 864222417Sjulian\ function. You need not call it directly. 865222417Sjulian\ 866222417Sjulian: getkey ( -- ascii_keycode ) 867222417Sjulian 868222417Sjulian begin \ loop forever 869222417Sjulian 870222417Sjulian menu_timeout_enabled @ 1 = if 871222417Sjulian ( -- ) 872222417Sjulian seconds ( get current time: -- N ) 873222417Sjulian dup menu_time @ <> if ( has time elapsed?: N N N -- N ) 874222417Sjulian 875222417Sjulian \ At least 1 second has elapsed since last loop 876222417Sjulian \ so we will decrement our "timeout" (really a 877222417Sjulian \ counter, insuring that we do not proceed too 878222417Sjulian \ fast) and update our timeout display. 879222417Sjulian 880222417Sjulian menu_time ! ( update time record: N -- ) 881222417Sjulian menu_timeout @ ( "time" remaining: -- N ) 882222417Sjulian dup 0> if ( greater than 0?: N N 0 -- N ) 883222417Sjulian 1- ( decrement counter: N -- N ) 884222417Sjulian dup menu_timeout ! 885222417Sjulian ( re-assign: N N Addr -- N ) 886222417Sjulian then 887222417Sjulian ( -- N ) 888222417Sjulian 889222417Sjulian dup 0= swap 0< or if ( N <= 0?: N N -- ) 890222417Sjulian \ halt the timer 891222417Sjulian 0 menu_timeout ! ( 0 Addr -- ) 892222417Sjulian 0 menu_timeout_enabled ! ( 0 Addr -- ) 893222417Sjulian then 894222417Sjulian 895222417Sjulian \ update the timer display ( N -- ) 896222417Sjulian menu_timeout @ menu-timeout-update 897222417Sjulian 898222417Sjulian menu_timeout @ 0= if 899222417Sjulian \ We've reached the end of the timeout 900222417Sjulian \ (user did not cancel by pressing ANY 901222417Sjulian \ key) 902222417Sjulian 903262701Sdteske s" menu_timeout_command" getenv dup 904222417Sjulian -1 = if 905222417Sjulian drop \ clean-up 906222417Sjulian else 907222417Sjulian evaluate 908222417Sjulian then 909222417Sjulian then 910222417Sjulian 911222417Sjulian else ( -- N ) 912222417Sjulian \ No [detectable] time has elapsed (in seconds) 913222417Sjulian drop ( N -- ) 914222417Sjulian then 915222417Sjulian ( -- ) 916222417Sjulian then 917222417Sjulian 918222417Sjulian key? if \ Was a key pressed? (see loader(8)) 919222417Sjulian 920222417Sjulian \ An actual key was pressed (if the timeout is running, 921222417Sjulian \ kill it regardless of which key was pressed) 922222417Sjulian menu_timeout @ 0<> if 923222417Sjulian 0 menu_timeout ! 924222417Sjulian 0 menu_timeout_enabled ! 925222417Sjulian 926222417Sjulian \ clear screen of timeout message 927222417Sjulian 0 menu-timeout-update 928222417Sjulian then 929222417Sjulian 930222417Sjulian \ get the key that was pressed and exit (if we 931222417Sjulian \ get a non-zero ASCII code) 932222417Sjulian key dup 0<> if 933222417Sjulian exit 934222417Sjulian else 935222417Sjulian drop 936222417Sjulian then 937222417Sjulian then 938222417Sjulian 50 ms \ sleep for 50 milliseconds (see loader(8)) 939222417Sjulian 940222417Sjulian again 941222417Sjulian; 942222417Sjulian 943222417Sjulian: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1. 944222417Sjulian 945222417Sjulian \ Clear the screen area associated with the interactive menu 946222417Sjulian menuX @ menuY @ 947222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 948222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 949222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 950222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 951222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+ 952222417Sjulian 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 953222417Sjulian 2drop 954222417Sjulian 955222417Sjulian \ Reset the starting index and position for the menu 956222417Sjulian menu_start 1- menuidx ! 957222417Sjulian 0 menurow ! 958222417Sjulian; 959222417Sjulian 960222417Sjulian\ Erase and redraw the menu. Useful if you change a caption and want to 961222417Sjulian\ update the menu to reflect the new value. 962222417Sjulian\ 963222417Sjulian: menu-redraw ( -- ) 964222417Sjulian menu-erase 965222417Sjulian menu-create 966222417Sjulian; 967222417Sjulian 968222417Sjulian\ This function initializes the menu. Call this from your `loader.rc' file 969222417Sjulian\ before calling any other menu-related functions. 970222417Sjulian\ 971222417Sjulian: menu-init ( -- ) 972222417Sjulian menu_start 973222417Sjulian 1- menuidx ! \ Initialize the starting index for the menu 974222417Sjulian 0 menurow ! \ Initialize the starting position for the menu 975254108Sdteske 976254108Sdteske \ Assign configuration values 977262701Sdteske s" loader_menu_y" getenv dup -1 = if 978254108Sdteske drop \ no custom row position 979254108Sdteske menu_default_y 980254108Sdteske else 981254108Sdteske \ make sure custom position is a number 982254108Sdteske ?number 0= if 983254108Sdteske menu_default_y \ or use default 984254108Sdteske then 985254108Sdteske then 986254108Sdteske menuY ! 987262701Sdteske s" loader_menu_x" getenv dup -1 = if 988254108Sdteske drop \ no custom column position 989254108Sdteske menu_default_x 990254108Sdteske else 991254108Sdteske \ make sure custom position is a number 992254108Sdteske ?number 0= if 993254108Sdteske menu_default_x \ or use default 994254108Sdteske then 995254108Sdteske then 996254108Sdteske menuX ! 997254108Sdteske 998254108Sdteske \ Interpret a custom frame type for the menu 999254108Sdteske TRUE ( draw a box? default yes, but might be altered below ) 1000262701Sdteske s" loader_menu_frame" getenv dup -1 = if ( 1 ) 1001254108Sdteske drop \ no custom frame type 1002254108Sdteske else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 ) 1003254108Sdteske f_single ( see frames.4th ) 1004254108Sdteske else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 ) 1005254108Sdteske f_double ( see frames.4th ) 1006254108Sdteske else ( 3 ) s" none" compare-insensitive 0= if ( 4 ) 1007254108Sdteske drop FALSE \ don't draw a box 1008254108Sdteske ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then 1009254108Sdteske if 1010254108Sdteske 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y) 1011254108Sdteske then 1012254108Sdteske 1013254108Sdteske 0 25 at-xy \ Move cursor to the bottom for output 1014222417Sjulian; 1015222417Sjulian 1016222417Sjulian\ Main function. Call this from your `loader.rc' file. 1017222417Sjulian\ 1018222417Sjulian: menu-display ( -- ) 1019222417Sjulian 1020222417Sjulian 0 menu_timeout_enabled ! \ start with automatic timeout disabled 1021222417Sjulian 1022222417Sjulian \ check indication that automatic execution after delay is requested 1023262701Sdteske s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr ) 1024222417Sjulian drop ( just testing existence right now: Addr -- ) 1025222417Sjulian 1026222417Sjulian \ initialize state variables 1027222417Sjulian seconds menu_time ! ( store the time we started ) 1028222417Sjulian 1 menu_timeout_enabled ! ( enable automatic timeout ) 1029222417Sjulian 1030222417Sjulian \ read custom time-duration (if set) 1031222417Sjulian s" autoboot_delay" getenv dup -1 = if 1032222417Sjulian drop \ no custom duration (remove dup'd bunk -1) 1033222417Sjulian menu_timeout_default \ use default setting 1034222417Sjulian else 1035222417Sjulian 2dup ?number 0= if ( if not a number ) 1036222417Sjulian \ disable timeout if "NO", else use default 1037222417Sjulian s" NO" compare-insensitive 0= if 1038222417Sjulian 0 menu_timeout_enabled ! 1039222417Sjulian 0 ( assigned to menu_timeout below ) 1040222417Sjulian else 1041222417Sjulian menu_timeout_default 1042222417Sjulian then 1043222417Sjulian else 1044222417Sjulian -rot 2drop 1045222417Sjulian 1046225353Sjh \ boot immediately if less than zero 1047222417Sjulian dup 0< if 1048222417Sjulian drop 1049225353Sjh menu-create 1050225353Sjh 0 25 at-xy 1051225353Sjh 0 boot 1052222417Sjulian then 1053222417Sjulian then 1054222417Sjulian then 1055222417Sjulian menu_timeout ! ( store value on stack from above ) 1056222417Sjulian 1057222417Sjulian menu_timeout_enabled @ 1 = if 1058222417Sjulian \ read custom column position (if set) 1059262701Sdteske s" loader_menu_timeout_x" getenv dup -1 = if 1060222417Sjulian drop \ no custom column position 1061222417Sjulian menu_timeout_default_x \ use default setting 1062222417Sjulian else 1063222417Sjulian \ make sure custom position is a number 1064222417Sjulian ?number 0= if 1065222417Sjulian menu_timeout_default_x \ or use default 1066222417Sjulian then 1067222417Sjulian then 1068222417Sjulian menu_timeout_x ! ( store value on stack from above ) 1069222417Sjulian 1070222417Sjulian \ read custom row position (if set) 1071262701Sdteske s" loader_menu_timeout_y" getenv dup -1 = if 1072222417Sjulian drop \ no custom row position 1073222417Sjulian menu_timeout_default_y \ use default setting 1074222417Sjulian else 1075222417Sjulian \ make sure custom position is a number 1076222417Sjulian ?number 0= if 1077222417Sjulian menu_timeout_default_y \ or use default 1078222417Sjulian then 1079222417Sjulian then 1080222417Sjulian menu_timeout_y ! ( store value on stack from above ) 1081222417Sjulian then 1082222417Sjulian then 1083222417Sjulian 1084222417Sjulian menu-create 1085222417Sjulian 1086222417Sjulian begin \ Loop forever 1087222417Sjulian 1088222417Sjulian 0 25 at-xy \ Move cursor to the bottom for output 1089222417Sjulian getkey \ Block here, waiting for a key to be pressed 1090222417Sjulian 1091222417Sjulian dup -1 = if 1092222417Sjulian drop exit \ Caught abort (abnormal return) 1093222417Sjulian then 1094222417Sjulian 1095222417Sjulian \ Boot if the user pressed Enter/Ctrl-M (13) or 1096222417Sjulian \ Ctrl-Enter/Ctrl-J (10) 1097222417Sjulian dup over 13 = swap 10 = or if 1098222417Sjulian drop ( no longer needed ) 1099222417Sjulian s" boot" evaluate 1100222417Sjulian exit ( pedantic; never reached ) 1101222417Sjulian then 1102222417Sjulian 1103242667Sdteske dup menureboot @ = if 0 reboot then 1104242667Sdteske 1105222417Sjulian \ Evaluate the decimal ASCII value against known menu item 1106222417Sjulian \ key associations and act accordingly 1107222417Sjulian 1108222417Sjulian 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1109222417Sjulian begin 1110243114Sdteske dup menukeyN @ 1111243114Sdteske rot tuck = if 1112222417Sjulian 1113222417Sjulian \ Adjust for missing ACPI menuitem on non-i386 1114222417Sjulian arch-i386? true <> menuacpi @ 0<> and if 1115222417Sjulian menuacpi @ over 2dup < -rot = or 1116222417Sjulian over 58 < and if 1117222417Sjulian ( key >= menuacpi && key < 58: N -- N ) 1118222417Sjulian 1+ 1119222417Sjulian then 1120222417Sjulian then 1121222417Sjulian 1122222417Sjulian \ Test for the environment variable 1123243114Sdteske dup menu_command[x] 1124222417Sjulian getenv dup -1 <> if 1125222417Sjulian \ Execute the stored procedure 1126222417Sjulian evaluate 1127222417Sjulian 1128222417Sjulian \ We expect there to be a non-zero 1129222417Sjulian \ value left on the stack after 1130222417Sjulian \ executing the stored procedure. 1131222417Sjulian \ If so, continue to run, else exit. 1132222417Sjulian 1133222417Sjulian 0= if 1134222417Sjulian drop \ key pressed 1135222417Sjulian drop \ loop iterator 1136222417Sjulian exit 1137222417Sjulian else 1138222417Sjulian swap \ need iterator on top 1139222417Sjulian then 1140222417Sjulian then 1141222417Sjulian 1142222417Sjulian \ Re-adjust for missing ACPI menuitem 1143222417Sjulian arch-i386? true <> menuacpi @ 0<> and if 1144222417Sjulian swap 1145222417Sjulian menuacpi @ 1+ over 2dup < -rot = or 1146222417Sjulian over 59 < and if 1147222417Sjulian 1- 1148222417Sjulian then 1149222417Sjulian swap 1150222417Sjulian then 1151222417Sjulian else 1152222417Sjulian swap \ need iterator on top 1153222417Sjulian then 1154222417Sjulian 1155222417Sjulian \ 1156222417Sjulian \ Check for menu keycode shortcut(s) 1157222417Sjulian \ 1158243114Sdteske dup menu_keycode[x] 1159222417Sjulian getenv dup -1 = if 1160222417Sjulian drop 1161222417Sjulian else 1162222417Sjulian ?number 0<> if 1163222417Sjulian rot tuck = if 1164222417Sjulian swap 1165243114Sdteske dup menu_command[x] 1166222417Sjulian getenv dup -1 <> if 1167222417Sjulian evaluate 1168222417Sjulian 0= if 1169222417Sjulian 2drop 1170222417Sjulian exit 1171222417Sjulian then 1172222417Sjulian else 1173222417Sjulian drop 1174222417Sjulian then 1175222417Sjulian else 1176222417Sjulian swap 1177222417Sjulian then 1178222417Sjulian then 1179222417Sjulian then 1180222417Sjulian 1181222417Sjulian 1+ dup 56 > \ increment iterator 1182222417Sjulian \ continue if less than 57 1183222417Sjulian until 1184222417Sjulian drop \ loop iterator 1185242667Sdteske drop \ key pressed 1186222417Sjulian 1187222417Sjulian again \ Non-operational key was pressed; repeat 1188222417Sjulian; 1189222417Sjulian 1190222417Sjulian\ This function unsets all the possible environment variables associated with 1191228985Spluknet\ creating the interactive menu. 1192222417Sjulian\ 1193228985Spluknet: menu-unset ( -- ) 1194222417Sjulian 1195222417Sjulian 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8') 1196222417Sjulian begin 1197243114Sdteske dup menu_init[x] unsetenv \ menu initializer 1198243114Sdteske dup menu_command[x] unsetenv \ menu command 1199243114Sdteske dup menu_caption[x] unsetenv \ menu caption 1200243114Sdteske dup ansi_caption[x] unsetenv \ ANSI caption 1201243114Sdteske dup menu_keycode[x] unsetenv \ menu keycode 1202243114Sdteske dup toggled_text[x] unsetenv \ toggle_menuitem caption 1203243114Sdteske dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption 1204228985Spluknet 1205243114Sdteske 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9') 1206228985Spluknet begin 1207243114Sdteske \ cycle_menuitem caption and ANSI caption 1208243114Sdteske 2dup menu_caption[x][y] unsetenv 1209243114Sdteske 2dup ansi_caption[x][y] unsetenv 1210243114Sdteske 1+ dup 57 > 1211228985Spluknet until 1212243114Sdteske drop \ inner iterator 1213228985Spluknet 1214243114Sdteske 0 over menukeyN ! \ used by menu-create, menu-display 1215243114Sdteske 0 over init_stateN ! \ used by menu-create 1216243114Sdteske 0 over toggle_stateN ! \ used by toggle_menuitem 1217243114Sdteske 0 over init_textN c! \ used by toggle_menuitem 1218243114Sdteske 0 over cycle_stateN ! \ used by cycle_menuitem 1219228985Spluknet 1220222417Sjulian 1+ dup 56 > \ increment, continue if less than 57 1221222417Sjulian until 1222222417Sjulian drop \ iterator 1223222417Sjulian 1224262701Sdteske s" menu_timeout_command" unsetenv \ menu timeout command 1225262701Sdteske s" menu_reboot" unsetenv \ Reboot menu option flag 1226262701Sdteske s" menu_acpi" unsetenv \ ACPI menu option flag 1227262701Sdteske s" menu_kernel" unsetenv \ Kernel menu option flag 1228262701Sdteske s" menu_options" unsetenv \ Options separator flag 1229262701Sdteske s" menu_optionstext" unsetenv \ separator display text 1230262701Sdteske s" menu_init" unsetenv \ menu initializer 1231228985Spluknet 1232222417Sjulian 0 menureboot ! 1233222417Sjulian 0 menuacpi ! 1234222417Sjulian 0 menuoptions ! 1235228985Spluknet; 1236228985Spluknet 1237228985Spluknet\ This function both unsets menu variables and visually erases the menu area 1238228985Spluknet\ in-preparation for another menu. 1239228985Spluknet\ 1240228985Spluknet: menu-clear ( -- ) 1241228985Spluknet menu-unset 1242222417Sjulian menu-erase 1243222417Sjulian; 1244222417Sjulian 1245222417Sjulianbullet menubllt ! 1246222417Sjulian 1247241523Sdteske\ Initialize our menu initialization state variables 1248241523Sdteske0 init_state1 ! 1249241523Sdteske0 init_state2 ! 1250241523Sdteske0 init_state3 ! 1251241523Sdteske0 init_state4 ! 1252241523Sdteske0 init_state5 ! 1253241523Sdteske0 init_state6 ! 1254241523Sdteske0 init_state7 ! 1255241523Sdteske0 init_state8 ! 1256241523Sdteske 1257222417Sjulian\ Initialize our boolean state variables 1258222417Sjulian0 toggle_state1 ! 1259222417Sjulian0 toggle_state2 ! 1260222417Sjulian0 toggle_state3 ! 1261222417Sjulian0 toggle_state4 ! 1262222417Sjulian0 toggle_state5 ! 1263222417Sjulian0 toggle_state6 ! 1264222417Sjulian0 toggle_state7 ! 1265222417Sjulian0 toggle_state8 ! 1266222417Sjulian 1267222417Sjulian\ Initialize our array state variables 1268222417Sjulian0 cycle_state1 ! 1269222417Sjulian0 cycle_state2 ! 1270222417Sjulian0 cycle_state3 ! 1271222417Sjulian0 cycle_state4 ! 1272222417Sjulian0 cycle_state5 ! 1273222417Sjulian0 cycle_state6 ! 1274222417Sjulian0 cycle_state7 ! 1275222417Sjulian0 cycle_state8 ! 1276222417Sjulian 1277222417Sjulian\ Initialize string containers 1278222417Sjulian0 init_text1 c! 1279222417Sjulian0 init_text2 c! 1280222417Sjulian0 init_text3 c! 1281222417Sjulian0 init_text4 c! 1282222417Sjulian0 init_text5 c! 1283222417Sjulian0 init_text6 c! 1284222417Sjulian0 init_text7 c! 1285222417Sjulian0 init_text8 c! 1286