menu.4th revision 222417
1222417Sjulian\ Copyright (c) 2003 Scott Long <scottl@freebsd.org>
2222417Sjulian\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
3222417Sjulian\ Copyright (c) 2006-2011 Devin Teske <devinteske@hotmail.com>
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: head/sys/boot/forth/menu.4th 222417 2011-05-28 08:50:38Z julian $
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
38222417Sjulian 4 constant menu_timeout_default_x \ default column position of timeout
39222417Sjulian23 constant menu_timeout_default_y \ default row position of timeout msg
40222417Sjulian10 constant menu_timeout_default   \ default timeout (in seconds)
41222417Sjulian
42222417Sjulian\ Customize the following values with care
43222417Sjulian
44222417Sjulian  1 constant menu_start \ Numerical prefix of first menu item
45222417Sjuliandot constant bullet     \ Menu bullet (appears after numerical prefix)
46222417Sjulian  5 constant menu_x     \ Row position of the menu (from the top)
47222417Sjulian 10 constant menu_y     \ Column position of the menu (from left side)
48222417Sjulian
49222417Sjulian\ Menu Appearance
50222417Sjulianvariable menuidx   \ Menu item stack for number prefixes
51222417Sjulianvariable menurow   \ Menu item stack for positioning
52222417Sjulianvariable menubllt  \ Menu item bullet
53222417Sjulian
54222417Sjulian\ Menu Positioning
55222417Sjulianvariable menuX     \ Menu X offset (columns)
56222417Sjulianvariable menuY     \ Menu Y offset (rows)
57222417Sjulian
58222417Sjulian\ Menu-item key association/detection
59222417Sjulianvariable menukey1
60222417Sjulianvariable menukey2
61222417Sjulianvariable menukey3
62222417Sjulianvariable menukey4
63222417Sjulianvariable menukey5
64222417Sjulianvariable menukey6
65222417Sjulianvariable menukey7
66222417Sjulianvariable menukey8
67222417Sjulianvariable menureboot
68222417Sjulianvariable menurebootadded
69222417Sjulianvariable menuacpi
70222417Sjulianvariable menuoptions
71222417Sjulian
72222417Sjulian\ Menu timer [count-down] variables
73222417Sjulianvariable menu_timeout_enabled \ timeout state (internal use only)
74222417Sjulianvariable menu_time            \ variable for tracking the passage of time
75222417Sjulianvariable menu_timeout         \ determined configurable delay duration
76222417Sjulianvariable menu_timeout_x       \ column position of timeout message
77222417Sjulianvariable menu_timeout_y       \ row position of timeout message
78222417Sjulian
79222417Sjulian\ Boolean option status variables
80222417Sjulianvariable toggle_state1
81222417Sjulianvariable toggle_state2
82222417Sjulianvariable toggle_state3
83222417Sjulianvariable toggle_state4
84222417Sjulianvariable toggle_state5
85222417Sjulianvariable toggle_state6
86222417Sjulianvariable toggle_state7
87222417Sjulianvariable toggle_state8
88222417Sjulian
89222417Sjulian\ Array option status variables
90222417Sjulianvariable cycle_state1
91222417Sjulianvariable cycle_state2
92222417Sjulianvariable cycle_state3
93222417Sjulianvariable cycle_state4
94222417Sjulianvariable cycle_state5
95222417Sjulianvariable cycle_state6
96222417Sjulianvariable cycle_state7
97222417Sjulianvariable cycle_state8
98222417Sjulian
99222417Sjulian\ Containers for storing the initial caption text
100222417Sjuliancreate init_text1 255 allot
101222417Sjuliancreate init_text2 255 allot
102222417Sjuliancreate init_text3 255 allot
103222417Sjuliancreate init_text4 255 allot
104222417Sjuliancreate init_text5 255 allot
105222417Sjuliancreate init_text6 255 allot
106222417Sjuliancreate init_text7 255 allot
107222417Sjuliancreate init_text8 255 allot
108222417Sjulian
109222417Sjulian: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
110222417Sjulian	s" arch-i386" environment? dup if
111222417Sjulian		drop
112222417Sjulian	then
113222417Sjulian;
114222417Sjulian
115222417Sjulian\ This function prints a menu item at menuX (row) and menuY (column), returns
116222417Sjulian\ the incremental decimal ASCII value associated with the menu item, and
117222417Sjulian\ increments the cursor position to the next row for the creation of the next
118222417Sjulian\ menu item. This function is called by the menu-create function. You need not
119222417Sjulian\ call it directly.
120222417Sjulian\ 
121222417Sjulian: printmenuitem ( menu_item_str -- ascii_keycode )
122222417Sjulian
123222417Sjulian	menurow dup @ 1+ swap ! ( increment menurow )
124222417Sjulian	menuidx dup @ 1+ swap ! ( increment menuidx )
125222417Sjulian
126222417Sjulian	\ Calculate the menuitem row position
127222417Sjulian	menurow @ menuY @ +
128222417Sjulian
129222417Sjulian	\ Position the cursor at the menuitem position
130222417Sjulian	dup menuX @ swap at-xy
131222417Sjulian
132222417Sjulian	\ Print the value of menuidx
133222417Sjulian	loader_color? if
134222417Sjulian		." [1m"
135222417Sjulian	then
136222417Sjulian	menuidx @ .
137222417Sjulian	loader_color? if
138222417Sjulian		." [37m"
139222417Sjulian	then
140222417Sjulian
141222417Sjulian	\ Move the cursor forward 1 column
142222417Sjulian	dup menuX @ 1+ swap at-xy
143222417Sjulian
144222417Sjulian	menubllt @ emit	\ Print the menu bullet using the emit function
145222417Sjulian
146222417Sjulian	\ Move the cursor to the 3rd column from the current position
147222417Sjulian	\ to allow for a space between the numerical prefix and the
148222417Sjulian	\ text caption
149222417Sjulian	menuX @ 3 + swap at-xy
150222417Sjulian
151222417Sjulian	\ Print the menu caption (we expect a string to be on the stack
152222417Sjulian	\ prior to invoking this function)
153222417Sjulian	type
154222417Sjulian
155222417Sjulian	\ Here we will add the ASCII decimal of the numerical prefix
156222417Sjulian	\ to the stack (decimal ASCII for `1' is 49) as a "return value"
157222417Sjulian	menuidx @ 48 +
158222417Sjulian;
159222417Sjulian
160222417Sjulian: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
161222417Sjulian
162222417Sjulian	\ ASCII numeral equal to user-selected menu item must be on the stack.
163222417Sjulian	\ We do not modify the stack, so the ASCII numeral is left on top.
164222417Sjulian
165222417Sjulian	s" init_textN"          \ base name of buffer
166222417Sjulian	-rot 2dup 9 + c! rot    \ replace 'N' with ASCII num
167222417Sjulian
168222417Sjulian	evaluate c@ 0= if
169222417Sjulian		\ NOTE: no need to check toggle_stateN since the first time we
170222417Sjulian		\ are called, we will populate init_textN. Further, we don't
171222417Sjulian		\ need to test whether menu_caption[x] (ansi_caption[x] when
172222417Sjulian		\ loader_color=1) is available since we would not have been
173222417Sjulian		\ called if the caption was NULL.
174222417Sjulian
175222417Sjulian		\ base name of environment variable
176222417Sjulian		loader_color? if
177222417Sjulian			s" ansi_caption[x]"
178222417Sjulian		else
179222417Sjulian			s" menu_caption[x]"
180222417Sjulian		then	
181222417Sjulian		-rot 2dup 13 + c! rot    \ replace 'x' with ASCII numeral
182222417Sjulian
183222417Sjulian		getenv dup -1 <> if
184222417Sjulian
185222417Sjulian			s" init_textN"          \ base name of buffer
186222417Sjulian			4 pick                  \ copy ASCII num to top
187222417Sjulian			rot tuck 9 + c! swap    \ replace 'N' with ASCII num
188222417Sjulian			evaluate
189222417Sjulian
190222417Sjulian			\ now we have the buffer c-addr on top
191222417Sjulian			\ ( followed by c-addr/u of current caption )
192222417Sjulian
193222417Sjulian			\ Copy the current caption into our buffer
194222417Sjulian			2dup c! -rot \ store strlen at first byte
195222417Sjulian			begin
196222417Sjulian				rot 1+    \ bring alt addr to top and increment
197222417Sjulian				-rot -rot \ bring buffer addr to top
198222417Sjulian				2dup c@ swap c! \ copy current character
199222417Sjulian				1+     \ increment buffer addr
200222417Sjulian				rot 1- \ bring buffer len to top and decrement
201222417Sjulian				dup 0= \ exit loop if buffer len is zero
202222417Sjulian			until
203222417Sjulian			2drop \ buffer len/addr
204222417Sjulian			drop  \ alt addr
205222417Sjulian
206222417Sjulian		else
207222417Sjulian			drop
208222417Sjulian		then
209222417Sjulian	then
210222417Sjulian
211222417Sjulian	\ Now we are certain to have init_textN populated with the initial
212222417Sjulian	\ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
213222417Sjulian	\ We can now use init_textN as the untoggled caption and
214222417Sjulian	\ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
215222417Sjulian	\ toggled caption and store the appropriate value into menu_caption[x]
216222417Sjulian	\ (again, ansi_caption[x] with loader_color enabled). Last, we'll
217222417Sjulian	\ negate the toggled state so that we reverse the flow on subsequent
218222417Sjulian	\ calls.
219222417Sjulian
220222417Sjulian	s" toggle_stateN @"      \ base name of toggle state var
221222417Sjulian	-rot 2dup 12 + c! rot    \ replace 'N' with ASCII numeral
222222417Sjulian
223222417Sjulian	evaluate 0= if
224222417Sjulian		\ state is OFF, toggle to ON
225222417Sjulian
226222417Sjulian		\ base name of toggled text var
227222417Sjulian		loader_color? if
228222417Sjulian			s" toggled_ansi[x]"
229222417Sjulian		else
230222417Sjulian			s" toggled_text[x]"
231222417Sjulian		then
232222417Sjulian		-rot 2dup 13 + c! rot    \ replace 'x' with ASCII num
233222417Sjulian
234222417Sjulian		getenv dup -1 <> if
235222417Sjulian			\ Assign toggled text to menu caption
236222417Sjulian
237222417Sjulian			\ base name of caption var
238222417Sjulian			loader_color? if
239222417Sjulian				s" ansi_caption[x]"
240222417Sjulian			else
241222417Sjulian				s" menu_caption[x]"
242222417Sjulian			then
243222417Sjulian			4 pick                   \ copy ASCII num to top
244222417Sjulian			rot tuck 13 + c! swap    \ replace 'x' with ASCII num
245222417Sjulian
246222417Sjulian			setenv \ set new caption
247222417Sjulian		else
248222417Sjulian			\ No toggled text, keep the same caption
249222417Sjulian
250222417Sjulian			drop
251222417Sjulian		then
252222417Sjulian
253222417Sjulian		true \ new value of toggle state var (to be stored later)
254222417Sjulian	else
255222417Sjulian		\ state is ON, toggle to OFF
256222417Sjulian
257222417Sjulian		s" init_textN"           \ base name of initial text buffer
258222417Sjulian		-rot 2dup 9 + c! rot     \ replace 'N' with ASCII numeral
259222417Sjulian		evaluate                 \ convert string to c-addr
260222417Sjulian		count                    \ convert c-addr to c-addr/u
261222417Sjulian
262222417Sjulian		\ base name of caption var
263222417Sjulian		loader_color? if
264222417Sjulian			s" ansi_caption[x]"
265222417Sjulian		else
266222417Sjulian			s" menu_caption[x]"
267222417Sjulian		then
268222417Sjulian		4 pick                   \ copy ASCII num to top
269222417Sjulian		rot tuck 13 + c! swap    \ replace 'x' with ASCII numeral
270222417Sjulian
271222417Sjulian		setenv    \ set new caption
272222417Sjulian		false     \ new value of toggle state var (to be stored below)
273222417Sjulian	then
274222417Sjulian
275222417Sjulian	\ now we'll store the new toggle state (on top of stack)
276222417Sjulian	s" toggle_stateN"        \ base name of toggle state var
277222417Sjulian	3 pick                   \ copy ASCII numeral to top
278222417Sjulian	rot tuck 12 + c! swap    \ replace 'N' with ASCII numeral
279222417Sjulian	evaluate                 \ convert string to addr
280222417Sjulian	!                        \ store new value
281222417Sjulian;
282222417Sjulian
283222417Sjulian: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
284222417Sjulian
285222417Sjulian	\ ASCII numeral equal to user-selected menu item must be on the stack.
286222417Sjulian	\ We do not modify the stack, so the ASCII numeral is left on top.
287222417Sjulian
288222417Sjulian	s" cycle_stateN"         \ base name of array state var
289222417Sjulian	-rot 2dup 11 + c! rot    \ replace 'N' with ASCII numeral
290222417Sjulian
291222417Sjulian	evaluate    \ we now have a pointer to the proper variable
292222417Sjulian	dup @       \ resolve the pointer (but leave it on the stack)
293222417Sjulian	1+          \ increment the value
294222417Sjulian
295222417Sjulian	\ Before assigning the (incremented) value back to the pointer,
296222417Sjulian	\ let's test for the existence of this particular array element.
297222417Sjulian	\ If the element exists, we'll store index value and move on.
298222417Sjulian	\ Otherwise, we'll loop around to zero and store that.
299222417Sjulian
300222417Sjulian	dup 48 + \ duplicate Array index and convert to ASCII numeral
301222417Sjulian
302222417Sjulian	\ base name of array caption text
303222417Sjulian	loader_color? if
304222417Sjulian		s" ansi_caption[x][y]"          
305222417Sjulian	else
306222417Sjulian		s" menu_caption[x][y]"          
307222417Sjulian	then
308222417Sjulian	-rot tuck 16 + c! swap          \ replace 'y' with Array index
309222417Sjulian	4 pick rot tuck 13 + c! swap    \ replace 'x' with menu choice
310222417Sjulian
311222417Sjulian	\ Now test for the existence of our incremented array index in the
312222417Sjulian	\ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
313222417Sjulian	\ enabled) as set in loader.rc(5), et. al.
314222417Sjulian
315222417Sjulian	getenv dup -1 = if
316222417Sjulian		\ No caption set for this array index. Loop back to zero.
317222417Sjulian
318222417Sjulian		drop    ( getenv cruft )
319222417Sjulian		drop    ( incremented array index )
320222417Sjulian		0       ( new array index that will be stored later )
321222417Sjulian
322222417Sjulian		\ base name of caption var
323222417Sjulian		loader_color? if
324222417Sjulian			s" ansi_caption[x][0]"
325222417Sjulian		else
326222417Sjulian			s" menu_caption[x][0]"
327222417Sjulian		then
328222417Sjulian		4 pick rot tuck 13 + c! swap    \ replace 'x' with menu choice
329222417Sjulian
330222417Sjulian		getenv dup -1 = if
331222417Sjulian			\ This is highly unlikely to occur, but to make
332222417Sjulian			\ sure that things move along smoothly, allocate
333222417Sjulian			\ a temporary NULL string
334222417Sjulian
335222417Sjulian			s" "
336222417Sjulian		then
337222417Sjulian	then
338222417Sjulian
339222417Sjulian	\ At this point, we should have the following on the stack (in order,
340222417Sjulian	\ from bottom to top):
341222417Sjulian	\ 
342222417Sjulian	\    N      - Ascii numeral representing the menu choice (inherited)
343222417Sjulian	\    Addr   - address of our internal cycle_stateN variable
344222417Sjulian	\    N      - zero-based number we intend to store to the above
345222417Sjulian	\    C-Addr - string value we intend to store to menu_caption[x]
346222417Sjulian	\             (or ansi_caption[x] with loader_color enabled)
347222417Sjulian	\ 
348222417Sjulian	\ Let's perform what we need to with the above.
349222417Sjulian
350222417Sjulian	\ base name of menuitem caption var
351222417Sjulian	loader_color? if
352222417Sjulian		s" ansi_caption[x]"
353222417Sjulian	else
354222417Sjulian		s" menu_caption[x]"
355222417Sjulian	then
356222417Sjulian	6 pick rot tuck 13 + c! swap    \ replace 'x' with menu choice
357222417Sjulian	setenv                          \ set the new caption
358222417Sjulian
359222417Sjulian	swap ! \ update array state variable
360222417Sjulian;
361222417Sjulian
362222417Sjulian: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
363222417Sjulian	s" hint.acpi.0.rsdp" getenv
364222417Sjulian	dup -1 = if
365222417Sjulian		drop false exit
366222417Sjulian	then
367222417Sjulian	2drop
368222417Sjulian	true
369222417Sjulian;
370222417Sjulian
371222417Sjulian: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
372222417Sjulian	s" hint.acpi.0.disabled" getenv
373222417Sjulian	dup -1 <> if
374222417Sjulian		s" 0" compare 0<> if
375222417Sjulian			false exit
376222417Sjulian		then
377222417Sjulian	else
378222417Sjulian		drop
379222417Sjulian	then
380222417Sjulian	true
381222417Sjulian;
382222417Sjulian
383222417Sjulian\ This function prints the appropriate menuitem basename to the stack if an
384222417Sjulian\ ACPI option is to be presented to the user, otherwise returns -1. Used
385222417Sjulian\ internally by menu-create, you need not (nor should you) call this directly.
386222417Sjulian\ 
387222417Sjulian: acpimenuitem ( -- C-Addr | -1 )
388222417Sjulian
389222417Sjulian	arch-i386? if
390222417Sjulian		acpipresent? if
391222417Sjulian			acpienabled? if
392222417Sjulian				loader_color? if
393222417Sjulian					s" toggled_ansi[x]"
394222417Sjulian				else
395222417Sjulian					s" toggled_text[x]"
396222417Sjulian				then
397222417Sjulian			else
398222417Sjulian				loader_color? if
399222417Sjulian					s" ansi_caption[x]"
400222417Sjulian				else
401222417Sjulian					s" menu_caption[x]"
402222417Sjulian				then
403222417Sjulian			then
404222417Sjulian		else
405222417Sjulian			menuidx dup @ 1+ swap ! ( increment menuidx )
406222417Sjulian			-1
407222417Sjulian		then
408222417Sjulian	else
409222417Sjulian		-1
410222417Sjulian	then
411222417Sjulian;
412222417Sjulian
413222417Sjulian\ This function creates the list of menu items. This function is called by the
414222417Sjulian\ menu-display function. You need not be call it directly.
415222417Sjulian\ 
416222417Sjulian: menu-create ( -- )
417222417Sjulian
418222417Sjulian	\ Print the frame caption at (x,y)
419222417Sjulian	s" loader_menu_title" getenv dup -1 = if
420222417Sjulian		drop s" Welcome to FreeBSD"
421222417Sjulian	then
422222417Sjulian	24 over 2 / - 9 at-xy type 
423222417Sjulian
424222417Sjulian	\ Print our menu options with respective key/variable associations.
425222417Sjulian	\ `printmenuitem' ends by adding the decimal ASCII value for the
426222417Sjulian	\ numerical prefix to the stack. We store the value left on the stack
427222417Sjulian	\ to the key binding variable for later testing against a character
428222417Sjulian	\ captured by the `getkey' function.
429222417Sjulian
430222417Sjulian	\ Note that any menu item beyond 9 will have a numerical prefix on the
431222417Sjulian	\ screen consisting of the first digit (ie. 1 for the tenth menu item)
432222417Sjulian	\ and the key required to activate that menu item will be the decimal
433222417Sjulian	\ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
434222417Sjulian	\ which is misleading and not desirable.
435222417Sjulian	\ 
436222417Sjulian	\ Thus, we do not allow more than 8 configurable items on the menu
437222417Sjulian	\ (with "Reboot" as the optional ninth and highest numbered item).
438222417Sjulian
439222417Sjulian	\ 
440222417Sjulian	\ Initialize the ACPI option status.
441222417Sjulian	\ 
442222417Sjulian	0 menuacpi !
443222417Sjulian	s" menu_acpi" getenv -1 <> if
444222417Sjulian		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
445222417Sjulian			menuacpi !
446222417Sjulian			arch-i386? if acpipresent? if
447222417Sjulian				\ 
448222417Sjulian				\ Set menu toggle state to active state
449222417Sjulian				\ (required by generic toggle_menuitem)
450222417Sjulian				\ 
451222417Sjulian				menuacpi @
452222417Sjulian				s" acpienabled? toggle_stateN !"
453222417Sjulian				-rot tuck 25 + c! swap
454222417Sjulian				evaluate
455222417Sjulian			then then
456222417Sjulian		else
457222417Sjulian			drop
458222417Sjulian		then
459222417Sjulian	then
460222417Sjulian
461222417Sjulian	\ 
462222417Sjulian	\ Initialize the menu_options visual separator.
463222417Sjulian	\ 
464222417Sjulian	0 menuoptions !
465222417Sjulian	s" menu_options" getenv -1 <> if
466222417Sjulian		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
467222417Sjulian			menuoptions !
468222417Sjulian		else
469222417Sjulian			drop
470222417Sjulian		then
471222417Sjulian	then
472222417Sjulian
473222417Sjulian	\ Initialize "Reboot" menu state variable (prevents double-entry)
474222417Sjulian	false menurebootadded !
475222417Sjulian
476222417Sjulian	49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
477222417Sjulian	begin
478222417Sjulian		\ If the "Options:" separator, print it.
479222417Sjulian		dup menuoptions @ = if
480222417Sjulian			\ Optionally add a reboot option to the menu
481222417Sjulian			s" menu_reboot" getenv -1 <> if
482222417Sjulian				drop
483222417Sjulian				s" Reboot" printmenuitem menureboot !
484222417Sjulian				true menurebootadded !
485222417Sjulian			then
486222417Sjulian
487222417Sjulian			menuX @
488222417Sjulian			menurow @ 2 + menurow !
489222417Sjulian			menurow @ menuY @ +
490222417Sjulian			at-xy
491222417Sjulian			." Options:"
492222417Sjulian		then
493222417Sjulian
494222417Sjulian		\ If this is the ACPI menu option, act accordingly.
495222417Sjulian		dup menuacpi @ = if
496222417Sjulian			acpimenuitem ( -- C-Addr | -1 )
497222417Sjulian		else
498222417Sjulian			loader_color? if
499222417Sjulian				s" ansi_caption[x]"
500222417Sjulian			else
501222417Sjulian				s" menu_caption[x]"
502222417Sjulian			then
503222417Sjulian		then
504222417Sjulian
505222417Sjulian		( C-Addr | -1 )
506222417Sjulian		dup -1 <> if
507222417Sjulian			\ replace 'x' with current iteration
508222417Sjulian			-rot 2dup 13 + c! rot
509222417Sjulian        
510222417Sjulian			\ test for environment variable
511222417Sjulian			getenv dup -1 <> if
512222417Sjulian				printmenuitem ( C-Addr -- N )
513222417Sjulian        
514222417Sjulian				s" menukeyN !" \ generate cmd to store result
515222417Sjulian				-rot 2dup 7 + c! rot
516222417Sjulian        
517222417Sjulian				evaluate
518222417Sjulian			else
519222417Sjulian				drop
520222417Sjulian			then
521222417Sjulian		else
522222417Sjulian			drop
523222417Sjulian
524222417Sjulian			s" menu_command[x]"
525222417Sjulian			-rot 2dup 13 + c! rot ( replace 'x' )
526222417Sjulian			unsetenv
527222417Sjulian		then
528222417Sjulian
529222417Sjulian		1+ dup 56 > \ add 1 to iterator, continue if less than 57
530222417Sjulian	until
531222417Sjulian	drop \ iterator
532222417Sjulian
533222417Sjulian	\ Optionally add a reboot option to the menu
534222417Sjulian	menurebootadded @ true <> if
535222417Sjulian		s" menu_reboot" getenv -1 <> if
536222417Sjulian			drop       \ no need for the value
537222417Sjulian			s" Reboot" \ menu caption (required by printmenuitem)
538222417Sjulian
539222417Sjulian			printmenuitem
540222417Sjulian			menureboot !
541222417Sjulian		else
542222417Sjulian			0 menureboot !
543222417Sjulian		then
544222417Sjulian	then
545222417Sjulian;
546222417Sjulian
547222417Sjulian\ Takes a single integer on the stack and updates the timeout display. The
548222417Sjulian\ integer must be between 0 and 9 (we will only update a single digit in the
549222417Sjulian\ source message).
550222417Sjulian\ 
551222417Sjulian: menu-timeout-update ( N -- )
552222417Sjulian
553222417Sjulian	dup 9 > if ( N N 9 -- N )
554222417Sjulian		drop ( N -- )
555222417Sjulian		9 ( maximum: -- N )
556222417Sjulian	then
557222417Sjulian
558222417Sjulian	dup 0 < if ( N N 0 -- N )
559222417Sjulian		drop ( N -- )
560222417Sjulian		0 ( minimum: -- N )
561222417Sjulian	then
562222417Sjulian
563222417Sjulian	48 + ( convert single-digit numeral to ASCII: N 48 -- N )
564222417Sjulian
565222417Sjulian	s" Autoboot in N seconds. [Space] to pause" ( N -- N Addr C )
566222417Sjulian
567222417Sjulian	2 pick 48 - 0> if ( N Addr C N 48 -- N Addr C )
568222417Sjulian
569222417Sjulian		\ Modify 'N' (Addr+12) above to reflect time-left
570222417Sjulian
571222417Sjulian		-rot	( N Addr C -- C N Addr )
572222417Sjulian		tuck	( C N Addr -- C Addr N Addr )
573222417Sjulian		12 +	( C Addr N Addr -- C Addr N Addr2 )
574222417Sjulian		c!	( C Addr N Addr2 -- C Addr )
575222417Sjulian		swap	( C Addr -- Addr C )
576222417Sjulian
577222417Sjulian		menu_timeout_x @
578222417Sjulian		menu_timeout_y @
579222417Sjulian		at-xy ( position cursor: Addr C N N -- Addr C )
580222417Sjulian
581222417Sjulian		type ( print message: Addr C -- )
582222417Sjulian
583222417Sjulian	else ( N Addr C N -- N Addr C )
584222417Sjulian
585222417Sjulian		menu_timeout_x @
586222417Sjulian		menu_timeout_y @
587222417Sjulian		at-xy ( position cursor: N Addr C N N -- N Addr C )
588222417Sjulian
589222417Sjulian		spaces ( erase message: N Addr C -- N Addr )
590222417Sjulian		2drop ( N Addr -- )
591222417Sjulian
592222417Sjulian	then
593222417Sjulian
594222417Sjulian	0 25 at-xy ( position cursor back at bottom-left )
595222417Sjulian;
596222417Sjulian
597222417Sjulian\ This function blocks program flow (loops forever) until a key is pressed.
598222417Sjulian\ The key that was pressed is added to the top of the stack in the form of its
599222417Sjulian\ decimal ASCII representation. This function is called by the menu-display
600222417Sjulian\ function. You need not call it directly.
601222417Sjulian\ 
602222417Sjulian: getkey ( -- ascii_keycode )
603222417Sjulian
604222417Sjulian	begin \ loop forever
605222417Sjulian
606222417Sjulian		menu_timeout_enabled @ 1 = if
607222417Sjulian			( -- )
608222417Sjulian			seconds ( get current time: -- N )
609222417Sjulian			dup menu_time @ <> if ( has time elapsed?: N N N -- N )
610222417Sjulian
611222417Sjulian				\ At least 1 second has elapsed since last loop
612222417Sjulian				\ so we will decrement our "timeout" (really a
613222417Sjulian				\ counter, insuring that we do not proceed too
614222417Sjulian				\ fast) and update our timeout display.
615222417Sjulian
616222417Sjulian				menu_time ! ( update time record: N -- )
617222417Sjulian				menu_timeout @ ( "time" remaining: -- N )
618222417Sjulian				dup 0> if ( greater than 0?: N N 0 -- N )
619222417Sjulian					1- ( decrement counter: N -- N )
620222417Sjulian					dup menu_timeout !
621222417Sjulian						( re-assign: N N Addr -- N )
622222417Sjulian				then
623222417Sjulian				( -- N )
624222417Sjulian
625222417Sjulian				dup 0= swap 0< or if ( N <= 0?: N N -- )
626222417Sjulian					\ halt the timer
627222417Sjulian					0 menu_timeout ! ( 0 Addr -- )
628222417Sjulian					0 menu_timeout_enabled ! ( 0 Addr -- )
629222417Sjulian				then
630222417Sjulian
631222417Sjulian				\ update the timer display ( N -- )
632222417Sjulian				menu_timeout @ menu-timeout-update
633222417Sjulian
634222417Sjulian				menu_timeout @ 0= if
635222417Sjulian					\ We've reached the end of the timeout
636222417Sjulian					\ (user did not cancel by pressing ANY
637222417Sjulian					\ key)
638222417Sjulian
639222417Sjulian					s" menu_timeout_command" getenv dup
640222417Sjulian					-1 = if
641222417Sjulian						drop \ clean-up
642222417Sjulian					else
643222417Sjulian						evaluate
644222417Sjulian					then
645222417Sjulian				then
646222417Sjulian
647222417Sjulian			else ( -- N )
648222417Sjulian				\ No [detectable] time has elapsed (in seconds)
649222417Sjulian				drop ( N -- )
650222417Sjulian			then
651222417Sjulian			( -- )
652222417Sjulian		then
653222417Sjulian
654222417Sjulian		key? if \ Was a key pressed? (see loader(8))
655222417Sjulian
656222417Sjulian			\ An actual key was pressed (if the timeout is running,
657222417Sjulian			\ kill it regardless of which key was pressed)
658222417Sjulian			menu_timeout @ 0<> if
659222417Sjulian				0 menu_timeout !
660222417Sjulian				0 menu_timeout_enabled !
661222417Sjulian
662222417Sjulian				\ clear screen of timeout message
663222417Sjulian				0 menu-timeout-update
664222417Sjulian			then
665222417Sjulian
666222417Sjulian			\ get the key that was pressed and exit (if we
667222417Sjulian			\ get a non-zero ASCII code)
668222417Sjulian			key dup 0<> if
669222417Sjulian				exit
670222417Sjulian			else
671222417Sjulian				drop
672222417Sjulian			then
673222417Sjulian		then
674222417Sjulian		50 ms \ sleep for 50 milliseconds (see loader(8))
675222417Sjulian
676222417Sjulian	again
677222417Sjulian;
678222417Sjulian
679222417Sjulian: menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1.
680222417Sjulian
681222417Sjulian	\ Clear the screen area associated with the interactive menu
682222417Sjulian	menuX @ menuY @
683222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
684222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
685222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
686222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
687222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
688222417Sjulian	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces
689222417Sjulian	2drop
690222417Sjulian
691222417Sjulian	\ Reset the starting index and position for the menu
692222417Sjulian	menu_start 1- menuidx !
693222417Sjulian	0 menurow !
694222417Sjulian;
695222417Sjulian
696222417Sjulian\ Erase and redraw the menu. Useful if you change a caption and want to
697222417Sjulian\ update the menu to reflect the new value.
698222417Sjulian\ 
699222417Sjulian: menu-redraw ( -- )
700222417Sjulian	menu-erase
701222417Sjulian	menu-create
702222417Sjulian;
703222417Sjulian
704222417Sjulian\ This function initializes the menu. Call this from your `loader.rc' file
705222417Sjulian\ before calling any other menu-related functions.
706222417Sjulian\ 
707222417Sjulian: menu-init ( -- )
708222417Sjulian	menu_start
709222417Sjulian	1- menuidx !    \ Initialize the starting index for the menu
710222417Sjulian	0 menurow !     \ Initialize the starting position for the menu
711222417Sjulian	42 13 2 9 box   \ Draw frame (w,h,x,y)
712222417Sjulian	0 25 at-xy      \ Move cursor to the bottom for output
713222417Sjulian;
714222417Sjulian
715222417Sjulian\ Main function. Call this from your `loader.rc' file.
716222417Sjulian\ 
717222417Sjulian: menu-display ( -- )
718222417Sjulian
719222417Sjulian	0 menu_timeout_enabled ! \ start with automatic timeout disabled
720222417Sjulian
721222417Sjulian	\ check indication that automatic execution after delay is requested
722222417Sjulian	s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
723222417Sjulian		drop ( just testing existence right now: Addr -- )
724222417Sjulian
725222417Sjulian		\ initialize state variables
726222417Sjulian		seconds menu_time ! ( store the time we started )
727222417Sjulian		1 menu_timeout_enabled ! ( enable automatic timeout )
728222417Sjulian
729222417Sjulian		\ read custom time-duration (if set)
730222417Sjulian		s" autoboot_delay" getenv dup -1 = if
731222417Sjulian			drop \ no custom duration (remove dup'd bunk -1)
732222417Sjulian			menu_timeout_default \ use default setting
733222417Sjulian		else
734222417Sjulian			2dup ?number 0= if ( if not a number )
735222417Sjulian				\ disable timeout if "NO", else use default
736222417Sjulian				s" NO" compare-insensitive 0= if
737222417Sjulian					0 menu_timeout_enabled !
738222417Sjulian					0 ( assigned to menu_timeout below )
739222417Sjulian				else
740222417Sjulian					menu_timeout_default
741222417Sjulian				then
742222417Sjulian			else
743222417Sjulian				-rot 2drop
744222417Sjulian
745222417Sjulian				\ disable timeout if less than zero
746222417Sjulian				dup 0< if
747222417Sjulian					drop
748222417Sjulian					0 menu_timeout_enabled !
749222417Sjulian					0 ( assigned to menu_timeout below )
750222417Sjulian				then
751222417Sjulian			then
752222417Sjulian		then
753222417Sjulian		menu_timeout ! ( store value on stack from above )
754222417Sjulian
755222417Sjulian		menu_timeout_enabled @ 1 = if
756222417Sjulian			\ read custom column position (if set)
757222417Sjulian			s" loader_menu_timeout_x" getenv dup -1 = if
758222417Sjulian				drop \ no custom column position
759222417Sjulian				menu_timeout_default_x \ use default setting
760222417Sjulian			else
761222417Sjulian				\ make sure custom position is a number
762222417Sjulian				?number 0= if
763222417Sjulian					menu_timeout_default_x \ or use default
764222417Sjulian				then
765222417Sjulian			then
766222417Sjulian			menu_timeout_x ! ( store value on stack from above )
767222417Sjulian        
768222417Sjulian			\ read custom row position (if set)
769222417Sjulian			s" loader_menu_timeout_y" getenv dup -1 = if
770222417Sjulian				drop \ no custom row position
771222417Sjulian				menu_timeout_default_y \ use default setting
772222417Sjulian			else
773222417Sjulian				\ make sure custom position is a number
774222417Sjulian				?number 0= if
775222417Sjulian					menu_timeout_default_y \ or use default
776222417Sjulian				then
777222417Sjulian			then
778222417Sjulian			menu_timeout_y ! ( store value on stack from above )
779222417Sjulian		then
780222417Sjulian	then
781222417Sjulian
782222417Sjulian	menu-create
783222417Sjulian
784222417Sjulian	begin \ Loop forever
785222417Sjulian
786222417Sjulian		0 25 at-xy \ Move cursor to the bottom for output
787222417Sjulian		getkey     \ Block here, waiting for a key to be pressed
788222417Sjulian
789222417Sjulian		dup -1 = if
790222417Sjulian			drop exit \ Caught abort (abnormal return)
791222417Sjulian		then
792222417Sjulian
793222417Sjulian		\ Boot if the user pressed Enter/Ctrl-M (13) or
794222417Sjulian		\ Ctrl-Enter/Ctrl-J (10)
795222417Sjulian		dup over 13 = swap 10 = or if
796222417Sjulian			drop ( no longer needed )
797222417Sjulian			s" boot" evaluate
798222417Sjulian			exit ( pedantic; never reached )
799222417Sjulian		then
800222417Sjulian
801222417Sjulian		\ Evaluate the decimal ASCII value against known menu item
802222417Sjulian		\ key associations and act accordingly
803222417Sjulian
804222417Sjulian		49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
805222417Sjulian		begin
806222417Sjulian			s" menukeyN @"
807222417Sjulian
808222417Sjulian			\ replace 'N' with current iteration
809222417Sjulian			-rot 2dup 7 + c! rot
810222417Sjulian
811222417Sjulian			evaluate rot tuck = if
812222417Sjulian
813222417Sjulian				\ Adjust for missing ACPI menuitem on non-i386
814222417Sjulian				arch-i386? true <> menuacpi @ 0<> and if
815222417Sjulian					menuacpi @ over 2dup < -rot = or
816222417Sjulian					over 58 < and if
817222417Sjulian					( key >= menuacpi && key < 58: N -- N )
818222417Sjulian						1+
819222417Sjulian					then
820222417Sjulian				then
821222417Sjulian
822222417Sjulian				\ base env name for the value (x is a number)
823222417Sjulian				s" menu_command[x]"
824222417Sjulian
825222417Sjulian				\ Copy ASCII number to string at offset 13
826222417Sjulian				-rot 2dup 13 + c! rot
827222417Sjulian
828222417Sjulian				\ Test for the environment variable
829222417Sjulian				getenv dup -1 <> if
830222417Sjulian					\ Execute the stored procedure
831222417Sjulian					evaluate
832222417Sjulian
833222417Sjulian					\ We expect there to be a non-zero
834222417Sjulian					\  value left on the stack after
835222417Sjulian					\ executing the stored procedure.
836222417Sjulian					\ If so, continue to run, else exit.
837222417Sjulian
838222417Sjulian					0= if
839222417Sjulian						drop \ key pressed
840222417Sjulian						drop \ loop iterator
841222417Sjulian						exit
842222417Sjulian					else
843222417Sjulian						swap \ need iterator on top
844222417Sjulian					then
845222417Sjulian				then
846222417Sjulian
847222417Sjulian				\ Re-adjust for missing ACPI menuitem
848222417Sjulian				arch-i386? true <> menuacpi @ 0<> and if
849222417Sjulian					swap
850222417Sjulian					menuacpi @ 1+ over 2dup < -rot = or
851222417Sjulian					over 59 < and if
852222417Sjulian						1-
853222417Sjulian					then
854222417Sjulian					swap
855222417Sjulian				then
856222417Sjulian			else
857222417Sjulian				swap \ need iterator on top
858222417Sjulian			then
859222417Sjulian
860222417Sjulian			\ 
861222417Sjulian			\ Check for menu keycode shortcut(s)
862222417Sjulian			\ 
863222417Sjulian			s" menu_keycode[x]"
864222417Sjulian			-rot 2dup 13 + c! rot
865222417Sjulian			getenv dup -1 = if
866222417Sjulian				drop
867222417Sjulian			else
868222417Sjulian				?number 0<> if
869222417Sjulian					rot tuck = if
870222417Sjulian						swap
871222417Sjulian						s" menu_command[x]"
872222417Sjulian						-rot 2dup 13 + c! rot
873222417Sjulian						getenv dup -1 <> if
874222417Sjulian							evaluate
875222417Sjulian							0= if
876222417Sjulian								2drop
877222417Sjulian								exit
878222417Sjulian							then
879222417Sjulian						else
880222417Sjulian							drop
881222417Sjulian						then
882222417Sjulian					else
883222417Sjulian						swap
884222417Sjulian					then
885222417Sjulian				then
886222417Sjulian			then
887222417Sjulian
888222417Sjulian			1+ dup 56 > \ increment iterator
889222417Sjulian			            \ continue if less than 57
890222417Sjulian		until
891222417Sjulian		drop \ loop iterator
892222417Sjulian
893222417Sjulian		menureboot @ = if 0 reboot then
894222417Sjulian
895222417Sjulian	again	\ Non-operational key was pressed; repeat
896222417Sjulian;
897222417Sjulian
898222417Sjulian\ This function unsets all the possible environment variables associated with
899222417Sjulian\ creating the interactive menu. Call this when you want to clear the menu
900222417Sjulian\ area in preparation for another menu.
901222417Sjulian\ 
902222417Sjulian: menu-clear ( -- )
903222417Sjulian
904222417Sjulian	49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
905222417Sjulian	begin
906222417Sjulian		\ basename for caption variable
907222417Sjulian		loader_color? if
908222417Sjulian			s" ansi_caption[x]"
909222417Sjulian		else
910222417Sjulian			s" menu_caption[x]"
911222417Sjulian		then
912222417Sjulian		-rot 2dup 13 + c! rot	\ replace 'x' with current iteration
913222417Sjulian		unsetenv		\ not erroneous to unset unknown var
914222417Sjulian
915222417Sjulian		s" 0 menukeyN !"	\ basename for key association var
916222417Sjulian		-rot 2dup 9 + c! rot	\ replace 'N' with current iteration
917222417Sjulian		evaluate		\ assign zero (0) to key assoc. var
918222417Sjulian
919222417Sjulian		1+ dup 56 >	\ increment, continue if less than 57
920222417Sjulian	until
921222417Sjulian	drop \ iterator
922222417Sjulian
923222417Sjulian	\ clear the "Reboot" menu option flag
924222417Sjulian	s" menu_reboot" unsetenv
925222417Sjulian	0 menureboot !
926222417Sjulian
927222417Sjulian	\ clear the ACPI menu option flag
928222417Sjulian	s" menu_acpi" unsetenv
929222417Sjulian	0 menuacpi !
930222417Sjulian
931222417Sjulian	\ clear the "Options" menu separator flag
932222417Sjulian	s" menu_options" unsetenv
933222417Sjulian	0 menuoptions !
934222417Sjulian
935222417Sjulian	menu-erase
936222417Sjulian;
937222417Sjulian
938222417Sjulian\ Assign configuration values
939222417Sjulianbullet menubllt !
940222417Sjulian10 menuY !
941222417Sjulian5 menuX !
942222417Sjulian
943222417Sjulian\ Initialize our boolean state variables
944222417Sjulian0 toggle_state1 !
945222417Sjulian0 toggle_state2 !
946222417Sjulian0 toggle_state3 !
947222417Sjulian0 toggle_state4 !
948222417Sjulian0 toggle_state5 !
949222417Sjulian0 toggle_state6 !
950222417Sjulian0 toggle_state7 !
951222417Sjulian0 toggle_state8 !
952222417Sjulian
953222417Sjulian\ Initialize our array state variables
954222417Sjulian0 cycle_state1 !
955222417Sjulian0 cycle_state2 !
956222417Sjulian0 cycle_state3 !
957222417Sjulian0 cycle_state4 !
958222417Sjulian0 cycle_state5 !
959222417Sjulian0 cycle_state6 !
960222417Sjulian0 cycle_state7 !
961222417Sjulian0 cycle_state8 !
962222417Sjulian
963222417Sjulian\ Initialize string containers
964222417Sjulian0 init_text1 c!
965222417Sjulian0 init_text2 c!
966222417Sjulian0 init_text3 c!
967222417Sjulian0 init_text4 c!
968222417Sjulian0 init_text5 c!
969222417Sjulian0 init_text6 c!
970222417Sjulian0 init_text7 c!
971222417Sjulian0 init_text8 c!
972