File:  [ELWIX - Embedded LightWeight unIX -] / elwix / config / boot / menu.4th
Revision 1.2: download - view: text, annotated - select for diffs - revision graph
Tue Oct 18 09:02:06 2011 UTC (12 years, 8 months ago) by misho
Branches: MAIN
CVS tags: elwix1_7, elwix1_6, elwix1_5, HEAD, ELWIX1_6, ELWIX1_5
ver 1.5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>