File:  [ELWIX - Embedded LightWeight unIX -] / elwix / config / boot / menu.4th
Revision 1.2.6.1: download - view: text, annotated - select for diffs - revision graph
Sun Jan 27 22:15:08 2013 UTC (11 years, 5 months ago) by misho
Branches: elwix1_7
Diff to: branchpoint 1.2: preferred, unified
patch freebsd forth part of boot

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

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