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