1 \ Copyright (c) 2003 Scott Long <scottl@freebsd.org>
2 \ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
3 \ Copyright (c) 2006-2013 Devin Teske <dteske@FreeBSD.org>
6 \ Redistribution and use in source and binary forms, with or without
7 \ modification, are permitted provided that the following conditions
9 \ 1. Redistributions of source code must retain the above copyright
10 \ notice, this list of conditions and the following disclaimer.
11 \ 2. Redistributions in binary form must reproduce the above copyright
12 \ notice, this list of conditions and the following disclaimer in the
13 \ documentation and/or other materials provided with the distribution.
15 \ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 \ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 \ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 \ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 \ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 \ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 \ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 \ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 \ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 \ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 include /boot/frames.4th
34 f_double \ Set frames to double (see frames.4th). Replace with
35 \ f_single if you want single frames.
36 46 constant dot \ ASCII definition of a period (in decimal)
38 5 constant menu_default_x \ default column position of timeout
39 10 constant menu_default_y \ default row position of timeout msg
40 4 constant menu_timeout_default_x \ default column position of timeout
41 23 constant menu_timeout_default_y \ default row position of timeout msg
42 10 constant menu_timeout_default \ default timeout (in seconds)
44 \ Customize the following values with care
46 1 constant menu_start \ Numerical prefix of first menu item
47 dot constant bullet \ Menu bullet (appears after numerical prefix)
48 5 constant menu_x \ Row position of the menu (from the top)
49 10 constant menu_y \ Column position of the menu (from left side)
52 variable menuidx \ Menu item stack for number prefixes
53 variable menurow \ Menu item stack for positioning
54 variable menubllt \ Menu item bullet
57 variable menuX \ Menu X offset (columns)
58 variable menuY \ Menu Y offset (rows)
60 \ Menu-item key association/detection
70 variable menurebootadded
75 \ Parsing of kernels into menu-items
80 \ Menu timer [count-down] variables
81 variable menu_timeout_enabled \ timeout state (internal use only)
82 variable menu_time \ variable for tracking the passage of time
83 variable menu_timeout \ determined configurable delay duration
84 variable menu_timeout_x \ column position of timeout message
85 variable menu_timeout_y \ row position of timeout message
87 \ Menu initialization status variables
97 \ Boolean option status variables
98 variable toggle_state1
99 variable toggle_state2
100 variable toggle_state3
101 variable toggle_state4
102 variable toggle_state5
103 variable toggle_state6
104 variable toggle_state7
105 variable toggle_state8
107 \ Array option status variables
108 variable cycle_state1
109 variable cycle_state2
110 variable cycle_state3
111 variable cycle_state4
112 variable cycle_state5
113 variable cycle_state6
114 variable cycle_state7
115 variable cycle_state8
117 \ Containers for storing the initial caption text
118 create init_text1 64 allot
119 create init_text2 64 allot
120 create init_text3 64 allot
121 create init_text4 64 allot
122 create init_text5 64 allot
123 create init_text6 64 allot
124 create init_text7 64 allot
125 create init_text8 64 allot
127 \ Containers for parsing kernels into menu-items
128 create kerncapbuf 64 allot
129 create kerndefault 64 allot
130 create kernelsbuf 256 allot
132 : +c! ( N C-ADDR/U K -- C-ADDR/U )
133 3 pick 3 pick ( n c-addr/u k -- n c-addr/u k n c-addr )
134 rot + c! ( n c-addr/u k n c-addr -- n c-addr/u )
135 rot drop ( n c-addr/u -- c-addr/u )
138 : delim? ( C -- BOOL )
139 dup 32 = ( c -- c bool ) \ [sp] space
140 over 9 = or ( c bool -- c bool ) \ [ht] horizontal tab
141 over 10 = or ( c bool -- c bool ) \ [nl] newline
142 over 13 = or ( c bool -- c bool ) \ [cr] carriage return
143 over [char] , = or ( c bool -- c bool ) \ comma
144 swap drop ( c bool -- bool ) \ return boolean
147 : menukeyN ( N -- ADDR ) s" menukeyN" 7 +c! evaluate ;
148 : init_stateN ( N -- ADDR ) s" init_stateN" 10 +c! evaluate ;
149 : toggle_stateN ( N -- ADDR ) s" toggle_stateN" 12 +c! evaluate ;
150 : cycle_stateN ( N -- ADDR ) s" cycle_stateN" 11 +c! evaluate ;
151 : init_textN ( N -- C-ADDR ) s" init_textN" 9 +c! evaluate ;
153 : kernel[x] ( N -- C-ADDR/U ) s" kernel[x]" 7 +c! ;
154 : menu_init[x] ( N -- C-ADDR/U ) s" menu_init[x]" 10 +c! ;
155 : menu_command[x] ( N -- C-ADDR/U ) s" menu_command[x]" 13 +c! ;
156 : menu_caption[x] ( N -- C-ADDR/U ) s" menu_caption[x]" 13 +c! ;
157 : ansi_caption[x] ( N -- C-ADDR/U ) s" ansi_caption[x]" 13 +c! ;
158 : menu_keycode[x] ( N -- C-ADDR/U ) s" menu_keycode[x]" 13 +c! ;
159 : toggled_text[x] ( N -- C-ADDR/U ) s" toggled_text[x]" 13 +c! ;
160 : toggled_ansi[x] ( N -- C-ADDR/U ) s" toggled_ansi[x]" 13 +c! ;
161 : menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ;
162 : ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ;
164 : arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
165 s" arch-i386" environment? dup if
170 \ This function prints a menu item at menuX (row) and menuY (column), returns
171 \ the incremental decimal ASCII value associated with the menu item, and
172 \ increments the cursor position to the next row for the creation of the next
173 \ menu item. This function is called by the menu-create function. You need not
176 : printmenuitem ( menu_item_str -- ascii_keycode )
178 menurow dup @ 1+ swap ! ( increment menurow )
179 menuidx dup @ 1+ swap ! ( increment menuidx )
181 \ Calculate the menuitem row position
184 \ Position the cursor at the menuitem position
185 dup menuX @ swap at-xy
187 \ Print the value of menuidx
193 ."
\e[37m" (
\e[39m )
196 \ Move the cursor forward 1 column
197 dup menuX @ 1+ swap at-xy
199 menubllt @ emit \ Print the menu bullet using the emit function
201 \ Move the cursor to the 3rd column from the current position
202 \ to allow for a space between the numerical prefix and the
204 menuX @ 3 + swap at-xy
206 \ Print the menu caption (we expect a string to be on the stack
207 \ prior to invoking this function)
210 \ Here we will add the ASCII decimal of the numerical prefix
211 \ to the stack (decimal ASCII for `1' is 49) as a "return value"
215 : toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
217 \ ASCII numeral equal to user-selected menu item must be on the stack.
218 \ We do not modify the stack, so the ASCII numeral is left on top.
220 dup init_textN c@ 0= if
221 \ NOTE: no need to check toggle_stateN since the first time we
222 \ are called, we will populate init_textN. Further, we don't
223 \ need to test whether menu_caption[x] (ansi_caption[x] when
224 \ loader_color?=1) is available since we would not have been
225 \ called if the caption was NULL.
227 \ base name of environment variable
228 dup ( n -- n n ) \ key pressed
236 2 pick ( n c-addr/u -- n c-addr/u n )
237 init_textN ( n c-addr/u n -- n c-addr/u c-addr )
239 \ now we have the buffer c-addr on top
240 \ ( followed by c-addr/u of current caption )
242 \ Copy the current caption into our buffer
243 2dup c! -rot \ store strlen at first byte
245 rot 1+ \ bring alt addr to top and increment
246 -rot -rot \ bring buffer addr to top
247 2dup c@ swap c! \ copy current character
248 1+ \ increment buffer addr
249 rot 1- \ bring buffer len to top and decrement
250 dup 0= \ exit loop if buffer len is zero
252 2drop \ buffer len/addr
260 \ Now we are certain to have init_textN populated with the initial
261 \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
262 \ We can now use init_textN as the untoggled caption and
263 \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
264 \ toggled caption and store the appropriate value into menu_caption[x]
265 \ (again, ansi_caption[x] with loader_color enabled). Last, we'll
266 \ negate the toggled state so that we reverse the flow on subsequent
269 dup toggle_stateN @ 0= if
270 \ state is OFF, toggle to ON
272 dup ( n -- n n ) \ key pressed
279 \ Assign toggled text to menu caption
280 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
288 \ No toggled text, keep the same caption
289 drop ( n -1 -- n ) \ getenv cruft
292 true \ new value of toggle state var (to be stored later)
294 \ state is ON, toggle to OFF
296 dup init_textN count ( n -- n c-addr/u )
298 \ Assign init_textN text to menu caption
299 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
307 false \ new value of toggle state var (to be stored below)
310 \ now we'll store the new toggle state (on top of stack)
314 : cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
316 \ ASCII numeral equal to user-selected menu item must be on the stack.
317 \ We do not modify the stack, so the ASCII numeral is left on top.
319 dup cycle_stateN dup @ 1+ \ get value and increment
321 \ Before assigning the (incremented) value back to the pointer,
322 \ let's test for the existence of this particular array element.
323 \ If the element exists, we'll store index value and move on.
324 \ Otherwise, we'll loop around to zero and store that.
326 dup 48 + ( n addr k -- n addr k k' )
327 \ duplicate array index and convert to ASCII numeral
329 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y)
335 ( n addr k n k' -- n addr k c-addr/u )
337 \ Now test for the existence of our incremented array index in the
338 \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
339 \ enabled) as set in loader.rc(5), et. al.
342 \ No caption set for this array index. Loop back to zero.
344 drop ( n addr k -1 -- n addr k ) \ getenv cruft
345 drop 0 ( n addr k -- n addr 0 ) \ new value to store later
347 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y)
353 ( n addr 0 n 48 -- n addr 0 c-addr/u )
355 \ Highly unlikely to occur, but to ensure things move
356 \ along smoothly, allocate a temporary NULL string
361 \ At this point, we should have the following on the stack (in order,
362 \ from bottom to top):
364 \ n - Ascii numeral representing the menu choice (inherited)
365 \ addr - address of our internal cycle_stateN variable
366 \ k - zero-based number we intend to store to the above
367 \ c-addr/u - string value we intend to store to menu_caption[x]
368 \ (or ansi_caption[x] with loader_color enabled)
370 \ Let's perform what we need to with the above.
372 \ Assign array value text to menu caption
373 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n )
381 swap ! ( n addr k -- n ) \ update array state variable
384 : acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
385 s" hint.acpi.0.rsdp" getenv
393 : acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
394 s" hint.acpi.0.disabled" getenv
405 \ This function prints the appropriate menuitem basename to the stack if an
406 \ ACPI option is to be presented to the user, otherwise returns -1. Used
407 \ internally by menu-create, you need not (nor should you) call this directly.
409 : acpimenuitem ( -- C-Addr/U | -1 )
427 menuidx dup @ 1+ swap ! ( increment menuidx )
435 \ This function parses $kernels into variables that are used by the menu to
436 \ display wich kernel to boot when the [overloaded] `boot' word is interpreted.
437 \ Used internally by menu-create, you need not (nor should you) call this
440 : parse-kernels ( N -- ) \ kernidx
441 kernidx ! ( n -- ) \ store provided `x' value
442 [char] 0 kernmenuidx ! \ initialize `y' value for menu_caption[x][y]
444 \ Attempt to get a list of kernels, fall back to sensible default
445 s" kernels" getenv dup -1 = if
447 s" kernel kernel.old"
450 \ Check to see if the user has altered $kernel by comparing it against
451 \ $kernel[N] where N is kernel_state (the actively displayed kernel).
452 s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv
454 s" kernel" getenv dup -1 = if
457 2swap 2over compare 0= if
458 2drop FALSE ( skip below conditional )
459 else \ User has changed $kernel
460 TRUE ( slurp in new value )
462 else \ We haven't yet parsed $kernels into $kernel[N]
463 drop ( getenv cruft )
464 s" kernel" getenv dup -1 = if
467 TRUE ( slurp in initial value )
468 then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 )
469 if \ slurp new value into kerndefault
470 kerndefault 1+ 0 2swap strcat swap 1- c!
473 \ Clear out existing parsed-kernels
476 dup kernel[x] unsetenv
477 2dup menu_caption[x][y] unsetenv
478 2dup ansi_caption[x][y] unsetenv
483 \ Step through the string until we find the end
485 0 kernlen ! \ initialize length of value
487 \ Skip leading whitespace and/or comma delimiters
490 over c@ delim? ( c-addr/u -- c-addr/u bool )
492 false ( c-addr/u -- c-addr/u bool )
495 1- swap 1+ swap ( c-addr/u -- c-addr'/u' )
497 ( c-addr/u -- c-addr'/u' )
499 dup 0= if \ end of string while eating whitespace
500 2drop ( c-addr/u -- )
501 kernmenuidx @ [char] 0 <> if \ found at least one
505 \ No entries in $kernels; use $kernel instead
506 s" kernel" getenv dup -1 = if
509 dup kernlen ! \ store entire value length as kernlen
511 \ We're still within $kernels parsing toward the end;
512 \ find delimiter/end to determine kernlen
513 2dup ( c-addr/u -- c-addr/u c-addr/u )
516 drop 0 ( break ) \ found delimiter
518 kernlen @ 1+ kernlen ! \ incrememnt
519 1- swap 1+ swap \ c-addr++ u--
522 2drop ( c-addr/u c-addr'/u' -- c-addr/u )
524 \ If this is the first entry, compare it to $kernel
525 \ If different, then insert $kernel beforehand
526 kernmenuidx @ [char] 0 = if
527 over kernlen @ kerndefault count compare if
528 kernelsbuf 0 kerndefault count strcat
529 s" ," strcat 2swap strcat
530 kerndefault count swap drop kernlen !
534 ( c-addr/u -- c-addr'/u' )
536 \ At this point, we should have something on the stack to store
537 \ as the next kernel menu option; start assembling variables
539 over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 )
541 \ Assign first to kernel[x]
542 2dup kernmenuidx @ kernel[x] setenv
544 \ Assign second to menu_caption[x][y]
545 kerncapbuf 0 s" [K]ernel: " strcat
547 kernidx @ kernmenuidx @ menu_caption[x][y]
550 \ Assign third to ansi_caption[x][y]
551 kerncapbuf 0 s"
\e[1mK
\e[37mernel: " strcat
552 kernmenuidx @ [char] 0 = if
559 kernidx @ kernmenuidx @ ansi_caption[x][y]
562 2drop ( c-addr/u c-addr/u2 -- c-addr/u )
564 kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if
565 2drop ( c-addr/u -- ) exit
568 kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' )
572 \ This function goes through the kernels that were discovered by the
573 \ parse-kernels function [above], adding " (# of #)" text to the end of each
577 kernidx @ ( -- x ) dup 0= if exit then
578 [char] 0 s" (Y of Z)" ( x -- x y c-addr/u )
579 kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed
581 2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num
583 2over menu_caption[x][y] getenv dup -1 <> if
584 2dup + 1- c@ [char] ) = if
585 2drop \ Already tagged
587 kerncapbuf 0 2swap strcat
589 5 pick 5 pick menu_caption[x][y] setenv
592 drop ( getenv cruft )
595 2over ansi_caption[x][y] getenv dup -1 <> if
596 2dup + 1- c@ [char] ) = if
597 2drop \ Already tagged
599 kerncapbuf 0 2swap strcat
601 5 pick 5 pick ansi_caption[x][y] setenv
604 drop ( getenv cruft )
607 rot 1+ dup [char] 8 > if
608 -rot 2drop TRUE ( break )
616 \ This function creates the list of menu items. This function is called by the
617 \ menu-display function. You need not be call it directly.
621 \ Print the frame caption at (x,y)
622 s" loader_menu_title" getenv dup -1 = if
623 drop s" Welcome to FreeBSD"
625 TRUE ( use default alignment )
626 s" loader_menu_title_align" getenv dup -1 <> if
627 2dup s" left" compare-insensitive 0= if ( 1 )
628 2drop ( c-addr/u ) drop ( bool )
630 FALSE ( don't use default alignment )
631 else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 )
632 2drop ( c-addr/u ) drop ( bool )
633 menuX @ 42 + 4 - over - menuY @ 1-
634 FALSE ( don't use default alignment )
635 else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then
637 drop ( getenv cruft )
639 if ( use default center alignement? )
640 menuX @ 19 + over 2 / - menuY @ 1-
644 \ If $menu_init is set, evaluate it (allowing for whole menus to be
645 \ constructed dynamically -- as this function could conceivably set
646 \ the remaining environment variables to construct the menu entirely).
648 s" menu_init" getenv dup -1 <> if
654 \ Print our menu options with respective key/variable associations.
655 \ `printmenuitem' ends by adding the decimal ASCII value for the
656 \ numerical prefix to the stack. We store the value left on the stack
657 \ to the key binding variable for later testing against a character
658 \ captured by the `getkey' function.
660 \ Note that any menu item beyond 9 will have a numerical prefix on the
661 \ screen consisting of the first digit (ie. 1 for the tenth menu item)
662 \ and the key required to activate that menu item will be the decimal
663 \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
664 \ which is misleading and not desirable.
666 \ Thus, we do not allow more than 8 configurable items on the menu
667 \ (with "Reboot" as the optional ninth and highest numbered item).
670 \ Initialize the ACPI option status.
673 s" menu_acpi" getenv -1 <> if
674 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
676 arch-i386? if acpipresent? if
678 \ Set menu toggle state to active state
679 \ (required by generic toggle_menuitem)
681 acpienabled? menuacpi @ toggle_stateN !
689 \ Initialize kernel captions after parsing $kernels
692 s" menu_kernel" getenv -1 <> if
693 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
695 dup parse-kernels tag-kernels
697 \ Get the current cycle state (entry to use)
698 s" kernel_state" evaluate @ 48 + ( n -- n y )
700 \ If state is invalid, reset
701 dup kernmenuidx @ 1- > if
702 drop [char] 0 ( n y -- n 48 )
703 0 s" kernel_state" evaluate !
704 over s" init_kernel" evaluate drop
707 \ Set the current non-ANSI caption
708 2dup swap dup ( n y -- n y y n n )
709 s" set menu_caption[x]=$menu_caption[x][y]"
710 17 +c! 34 +c! 37 +c! evaluate
711 ( n y y n n c-addr/u -- n y )
713 \ Set the current ANSI caption
714 2dup swap dup ( n y -- n y y n n )
715 s" set ansi_caption[x]=$ansi_caption[x][y]"
716 17 +c! 34 +c! 37 +c! evaluate
717 ( n y y n n c-addr/u -- n y )
719 \ Initialize cycle state from stored value
721 s" init_cyclestate" evaluate ( n k -- n )
723 \ Set $kernel to $kernel[y]
724 s" activate_kernel" evaluate ( n -- n )
730 \ Initialize the menu_options visual separator.
733 s" menu_options" getenv -1 <> if
734 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
741 \ Initialize "Reboot" menu state variable (prevents double-entry)
742 false menurebootadded !
745 1- menuidx ! \ Initialize the starting index for the menu
746 0 menurow ! \ Initialize the starting position for the menu
748 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
750 \ If the "Options:" separator, print it.
751 dup menuoptions @ = if
752 \ Optionally add a reboot option to the menu
753 s" menu_reboot" getenv -1 <> if
755 s" Reboot" printmenuitem menureboot !
756 true menurebootadded !
760 menurow @ 2 + menurow !
763 s" menu_optionstext" getenv dup -1 <> if
770 \ If this is the ACPI menu option, act accordingly.
772 dup acpimenuitem ( n -- n n c-addr/u | n n -1 )
774 13 +c! ( n n c-addr/u -- n c-addr/u )
777 swap drop ( n n -1 -- n -1 )
778 over menu_command[x] unsetenv
781 \ make sure we have not already initialized this item
782 dup init_stateN dup @ 0= if
785 \ If this menuitem has an initializer, run it
805 \ test for environment variable
807 printmenuitem ( c-addr/u -- n )
816 1+ dup 56 > \ add 1 to iterator, continue if less than 57
820 \ Optionally add a reboot option to the menu
821 menurebootadded @ true <> if
822 s" menu_reboot" getenv -1 <> if
823 drop \ no need for the value
824 s" Reboot" \ menu caption (required by printmenuitem)
834 \ Takes a single integer on the stack and updates the timeout display. The
835 \ integer must be between 0 and 9 (we will only update a single digit in the
838 : menu-timeout-update ( N -- )
840 \ Enforce minimum/maximum
841 dup 9 > if drop 9 then
842 dup 0 < if drop 0 then
844 s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
847 rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII
848 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above
850 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
851 type ( c-addr/u -- ) \ print message
853 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
854 spaces ( n c-addr/u -- n c-addr ) \ erase message
855 2drop ( n c-addr -- )
858 0 25 at-xy ( position cursor back at bottom-left )
861 \ This function blocks program flow (loops forever) until a key is pressed.
862 \ The key that was pressed is added to the top of the stack in the form of its
863 \ decimal ASCII representation. This function is called by the menu-display
864 \ function. You need not call it directly.
866 : getkey ( -- ascii_keycode )
870 menu_timeout_enabled @ 1 = if
872 seconds ( get current time: -- N )
873 dup menu_time @ <> if ( has time elapsed?: N N N -- N )
875 \ At least 1 second has elapsed since last loop
876 \ so we will decrement our "timeout" (really a
877 \ counter, insuring that we do not proceed too
878 \ fast) and update our timeout display.
880 menu_time ! ( update time record: N -- )
881 menu_timeout @ ( "time" remaining: -- N )
882 dup 0> if ( greater than 0?: N N 0 -- N )
883 1- ( decrement counter: N -- N )
885 ( re-assign: N N Addr -- N )
889 dup 0= swap 0< or if ( N <= 0?: N N -- )
891 0 menu_timeout ! ( 0 Addr -- )
892 0 menu_timeout_enabled ! ( 0 Addr -- )
895 \ update the timer display ( N -- )
896 menu_timeout @ menu-timeout-update
899 \ We've reached the end of the timeout
900 \ (user did not cancel by pressing ANY
903 s" menu_timeout_command" getenv dup
912 \ No [detectable] time has elapsed (in seconds)
918 key? if \ Was a key pressed? (see loader(8))
920 \ An actual key was pressed (if the timeout is running,
921 \ kill it regardless of which key was pressed)
922 menu_timeout @ 0<> if
924 0 menu_timeout_enabled !
926 \ clear screen of timeout message
927 0 menu-timeout-update
930 \ get the key that was pressed and exit (if we
931 \ get a non-zero ASCII code)
938 50 ms \ sleep for 50 milliseconds (see loader(8))
943 : menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1.
945 \ Clear the screen area associated with the interactive menu
947 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
948 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
949 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
950 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
951 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
952 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces
955 \ Reset the starting index and position for the menu
956 menu_start 1- menuidx !
960 \ Erase and redraw the menu. Useful if you change a caption and want to
961 \ update the menu to reflect the new value.
968 \ This function initializes the menu. Call this from your `loader.rc' file
969 \ before calling any other menu-related functions.
973 1- menuidx ! \ Initialize the starting index for the menu
974 0 menurow ! \ Initialize the starting position for the menu
976 \ Assign configuration values
977 s" loader_menu_y" getenv dup -1 = if
978 drop \ no custom row position
981 \ make sure custom position is a number
983 menu_default_y \ or use default
987 s" loader_menu_x" getenv dup -1 = if
988 drop \ no custom column position
991 \ make sure custom position is a number
993 menu_default_x \ or use default
998 \ Interpret a custom frame type for the menu
999 TRUE ( draw a box? default yes, but might be altered below )
1000 s" loader_menu_frame" getenv dup -1 = if ( 1 )
1001 drop \ no custom frame type
1002 else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
1003 f_single ( see frames.4th )
1004 else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
1005 f_double ( see frames.4th )
1006 else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
1007 drop FALSE \ don't draw a box
1008 ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
1010 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
1013 0 25 at-xy \ Move cursor to the bottom for output
1016 \ Main function. Call this from your `loader.rc' file.
1018 : menu-display ( -- )
1020 0 menu_timeout_enabled ! \ start with automatic timeout disabled
1022 \ check indication that automatic execution after delay is requested
1023 s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
1024 drop ( just testing existence right now: Addr -- )
1026 \ initialize state variables
1027 seconds menu_time ! ( store the time we started )
1028 1 menu_timeout_enabled ! ( enable automatic timeout )
1030 \ read custom time-duration (if set)
1031 s" autoboot_delay" getenv dup -1 = if
1032 drop \ no custom duration (remove dup'd bunk -1)
1033 menu_timeout_default \ use default setting
1035 2dup ?number 0= if ( if not a number )
1036 \ disable timeout if "NO", else use default
1037 s" NO" compare-insensitive 0= if
1038 0 menu_timeout_enabled !
1039 0 ( assigned to menu_timeout below )
1041 menu_timeout_default
1046 \ boot immediately if less than zero
1055 menu_timeout ! ( store value on stack from above )
1057 menu_timeout_enabled @ 1 = if
1058 \ read custom column position (if set)
1059 s" loader_menu_timeout_x" getenv dup -1 = if
1060 drop \ no custom column position
1061 menu_timeout_default_x \ use default setting
1063 \ make sure custom position is a number
1065 menu_timeout_default_x \ or use default
1068 menu_timeout_x ! ( store value on stack from above )
1070 \ read custom row position (if set)
1071 s" loader_menu_timeout_y" getenv dup -1 = if
1072 drop \ no custom row position
1073 menu_timeout_default_y \ use default setting
1075 \ make sure custom position is a number
1077 menu_timeout_default_y \ or use default
1080 menu_timeout_y ! ( store value on stack from above )
1086 begin \ Loop forever
1088 0 25 at-xy \ Move cursor to the bottom for output
1089 getkey \ Block here, waiting for a key to be pressed
1092 drop exit \ Caught abort (abnormal return)
1095 \ Boot if the user pressed Enter/Ctrl-M (13) or
1096 \ Ctrl-Enter/Ctrl-J (10)
1097 dup over 13 = swap 10 = or if
1098 drop ( no longer needed )
1100 exit ( pedantic; never reached )
1103 dup menureboot @ = if 0 reboot then
1105 \ Evaluate the decimal ASCII value against known menu item
1106 \ key associations and act accordingly
1108 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
1113 \ Adjust for missing ACPI menuitem on non-i386
1114 arch-i386? true <> menuacpi @ 0<> and if
1115 menuacpi @ over 2dup < -rot = or
1117 ( key >= menuacpi && key < 58: N -- N )
1122 \ Test for the environment variable
1125 \ Execute the stored procedure
1128 \ We expect there to be a non-zero
1129 \ value left on the stack after
1130 \ executing the stored procedure.
1131 \ If so, continue to run, else exit.
1135 drop \ loop iterator
1138 swap \ need iterator on top
1142 \ Re-adjust for missing ACPI menuitem
1143 arch-i386? true <> menuacpi @ 0<> and if
1145 menuacpi @ 1+ over 2dup < -rot = or
1152 swap \ need iterator on top
1156 \ Check for menu keycode shortcut(s)
1181 1+ dup 56 > \ increment iterator
1182 \ continue if less than 57
1184 drop \ loop iterator
1187 again \ Non-operational key was pressed; repeat
1190 \ This function unsets all the possible environment variables associated with
1191 \ creating the interactive menu.
1195 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
1197 dup menu_init[x] unsetenv \ menu initializer
1198 dup menu_command[x] unsetenv \ menu command
1199 dup menu_caption[x] unsetenv \ menu caption
1200 dup ansi_caption[x] unsetenv \ ANSI caption
1201 dup menu_keycode[x] unsetenv \ menu keycode
1202 dup toggled_text[x] unsetenv \ toggle_menuitem caption
1203 dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption
1205 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9')
1207 \ cycle_menuitem caption and ANSI caption
1208 2dup menu_caption[x][y] unsetenv
1209 2dup ansi_caption[x][y] unsetenv
1212 drop \ inner iterator
1214 0 over menukeyN ! \ used by menu-create, menu-display
1215 0 over init_stateN ! \ used by menu-create
1216 0 over toggle_stateN ! \ used by toggle_menuitem
1217 0 over init_textN c! \ used by toggle_menuitem
1218 0 over cycle_stateN ! \ used by cycle_menuitem
1220 1+ dup 56 > \ increment, continue if less than 57
1224 s" menu_timeout_command" unsetenv \ menu timeout command
1225 s" menu_reboot" unsetenv \ Reboot menu option flag
1226 s" menu_acpi" unsetenv \ ACPI menu option flag
1227 s" menu_kernel" unsetenv \ Kernel menu option flag
1228 s" menu_options" unsetenv \ Options separator flag
1229 s" menu_optionstext" unsetenv \ separator display text
1230 s" menu_init" unsetenv \ menu initializer
1237 \ This function both unsets menu variables and visually erases the menu area
1238 \ in-preparation for another menu.
1247 \ Initialize our menu initialization state variables
1257 \ Initialize our boolean state variables
1267 \ Initialize our array state variables
1277 \ Initialize string containers