xref: /titanic_51/usr/src/boot/sys/boot/forth/menu.4th (revision 8b96bc0de81576a5859f89b3df31cc9645daee3f)
1\ Copyright (c) 2003 Scott Long <scottl@FreeBSD.org>
2\ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
3\ Copyright (c) 2006-2015 Devin Teske <dteske@FreeBSD.org>
4\ All rights reserved.
5\
6\ Redistribution and use in source and binary forms, with or without
7\ modification, are permitted provided that the following conditions
8\ are met:
9\ 1. Redistributions of source code must retain the above copyright
10\    notice, this list of conditions and the following disclaimer.
11\ 2. Redistributions in binary form must reproduce the above copyright
12\    notice, this list of conditions and the following disclaimer in the
13\    documentation and/or other materials provided with the distribution.
14\
15\ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16\ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17\ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18\ ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19\ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20\ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21\ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22\ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23\ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24\ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25\ SUCH DAMAGE.
26\
27\ $FreeBSD$
28
29marker task-menu.4th
30
31\ Frame drawing
32include /boot/forth/frames.4th
33
34vocabulary menu-infrastructure
35vocabulary menu-namespace
36vocabulary menu-command-helpers
37
38only forth also menu-infrastructure definitions
39
40f_double        \ Set frames to double (see frames.4th). Replace with
41                \ f_single if you want single frames.
4246 constant dot \ ASCII definition of a period (in decimal)
43
44 5 constant menu_default_x         \ default column position of timeout
4510 constant menu_default_y         \ default row position of timeout msg
46 4 constant menu_timeout_default_x \ default column position of timeout
4723 constant menu_timeout_default_y \ default row position of timeout msg
4810 constant menu_timeout_default   \ default timeout (in seconds)
49
50\ Customize the following values with care
51
52  1 constant menu_start \ Numerical prefix of first menu item
53dot constant bullet     \ Menu bullet (appears after numerical prefix)
54  5 constant menu_x     \ Row position of the menu (from the top)
55 10 constant menu_y     \ Column position of the menu (from left side)
56
57\ Menu Appearance
58variable menuidx   \ Menu item stack for number prefixes
59variable menurow   \ Menu item stack for positioning
60variable menubllt  \ Menu item bullet
61
62\ Menu Positioning
63variable menuX     \ Menu X offset (columns)
64variable menuY     \ Menu Y offset (rows)
65
66\ Menu-item elements
67variable menurebootadded
68
69\ Parsing of kernels into menu-items
70variable kernidx
71variable kernlen
72variable kernmenuidx
73
74\ Menu timer [count-down] variables
75variable menu_timeout_enabled \ timeout state (internal use only)
76variable menu_time            \ variable for tracking the passage of time
77variable menu_timeout         \ determined configurable delay duration
78variable menu_timeout_x       \ column position of timeout message
79variable menu_timeout_y       \ row position of timeout message
80
81\ Containers for parsing kernels into menu-items
82create kerncapbuf 64 allot
83create kerndefault 64 allot
84create kernelsbuf 256 allot
85
86only forth also menu-namespace definitions
87
88\ Menu-item key association/detection
89variable menukey1
90variable menukey2
91variable menukey3
92variable menukey4
93variable menukey5
94variable menukey6
95variable menukey7
96variable menukey8
97variable menureboot
98variable menuacpi
99variable menuosconsole
100variable menuoptions
101variable menukernel
102
103\ Menu initialization status variables
104variable init_state1
105variable init_state2
106variable init_state3
107variable init_state4
108variable init_state5
109variable init_state6
110variable init_state7
111variable init_state8
112
113\ Boolean option status variables
114variable toggle_state1
115variable toggle_state2
116variable toggle_state3
117variable toggle_state4
118variable toggle_state5
119variable toggle_state6
120variable toggle_state7
121variable toggle_state8
122
123\ Array option status variables
124variable cycle_state1
125variable cycle_state2
126variable cycle_state3
127variable cycle_state4
128variable cycle_state5
129variable cycle_state6
130variable cycle_state7
131variable cycle_state8
132
133\ Containers for storing the initial caption text
134create init_text1 64 allot
135create init_text2 64 allot
136create init_text3 64 allot
137create init_text4 64 allot
138create init_text5 64 allot
139create init_text6 64 allot
140create init_text7 64 allot
141create init_text8 64 allot
142
143only forth definitions
144
145: arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
146	s" arch-i386" environment? dup if
147		drop
148	then
149;
150
151: acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
152	s" hint.acpi.0.rsdp" getenv
153	dup -1 = if
154		drop false exit
155	then
156	2drop
157	true
158;
159
160: acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
161	s" hint.acpi.0.disabled" getenv
162	dup -1 <> if
163		s" 0" compare 0<> if
164			false exit
165		then
166	else
167		drop
168	then
169	true
170;
171
172: +c! ( N C-ADDR/U K -- C-ADDR/U )
173	3 pick 3 pick	( n c-addr/u k -- n c-addr/u k n c-addr )
174	rot + c!	( n c-addr/u k n c-addr -- n c-addr/u )
175	rot drop	( n c-addr/u -- c-addr/u )
176;
177
178only forth also menu-namespace definitions
179
180\ Forth variables
181: namespace     ( C-ADDR/U N -- ) also menu-namespace +c! evaluate previous ;
182: menukeyN      ( N -- ADDR )   s" menukeyN"       7 namespace ;
183: init_stateN   ( N -- ADDR )   s" init_stateN"   10 namespace ;
184: toggle_stateN ( N -- ADDR )   s" toggle_stateN" 12 namespace ;
185: cycle_stateN  ( N -- ADDR )   s" cycle_stateN"  11 namespace ;
186: init_textN    ( N -- C-ADDR ) s" init_textN"     9 namespace ;
187
188\ Environment variables
189: kernel[x]          ( N -- C-ADDR/U )   s" kernel[x]"           7 +c! ;
190: menu_init[x]       ( N -- C-ADDR/U )   s" menu_init[x]"       10 +c! ;
191: menu_command[x]    ( N -- C-ADDR/U )   s" menu_command[x]"    13 +c! ;
192: menu_caption[x]    ( N -- C-ADDR/U )   s" menu_caption[x]"    13 +c! ;
193: ansi_caption[x]    ( N -- C-ADDR/U )   s" ansi_caption[x]"    13 +c! ;
194: menu_keycode[x]    ( N -- C-ADDR/U )   s" menu_keycode[x]"    13 +c! ;
195: toggled_text[x]    ( N -- C-ADDR/U )   s" toggled_text[x]"    13 +c! ;
196: toggled_ansi[x]    ( N -- C-ADDR/U )   s" toggled_ansi[x]"    13 +c! ;
197: menu_caption[x][y] ( N M -- C-ADDR/U ) s" menu_caption[x][y]" 16 +c! 13 +c! ;
198: ansi_caption[x][y] ( N M -- C-ADDR/U ) s" ansi_caption[x][y]" 16 +c! 13 +c! ;
199
200also menu-infrastructure definitions
201
202\ This function prints a menu item at menuX (row) and menuY (column), returns
203\ the incremental decimal ASCII value associated with the menu item, and
204\ increments the cursor position to the next row for the creation of the next
205\ menu item. This function is called by the menu-create function. You need not
206\ call it directly.
207\
208: printmenuitem ( menu_item_str -- ascii_keycode )
209
210	loader_color? if [char] ^ escc! then
211
212	menurow dup @ 1+ swap ! ( increment menurow )
213	menuidx dup @ 1+ swap ! ( increment menuidx )
214
215	\ Calculate the menuitem row position
216	menurow @ menuY @ +
217
218	\ Position the cursor at the menuitem position
219	dup menuX @ swap at-xy
220
221	\ Print the value of menuidx
222	loader_color? dup ( -- bool bool )
223	if b then
224	menuidx @ .
225	if me then
226
227	\ Move the cursor forward 1 column
228	dup menuX @ 1+ swap at-xy
229
230	menubllt @ emit	\ Print the menu bullet using the emit function
231
232	\ Move the cursor to the 3rd column from the current position
233	\ to allow for a space between the numerical prefix and the
234	\ text caption
235	menuX @ 3 + swap at-xy
236
237	\ Print the menu caption (we expect a string to be on the stack
238	\ prior to invoking this function)
239	type
240
241	\ Here we will add the ASCII decimal of the numerical prefix
242	\ to the stack (decimal ASCII for `1' is 49) as a "return value"
243	menuidx @ 48 +
244;
245
246: delim? ( C -- BOOL )
247	dup  32 =		( c -- c bool )		\ [sp] space
248	over  9 = or		( c bool -- c bool )	\ [ht] horizontal tab
249	over 10 = or		( c bool -- c bool )	\ [nl] newline
250	over 13 = or		( c bool -- c bool )	\ [cr] carriage return
251	over [char] , =	or	( c bool -- c bool )	\ comma
252	swap drop		( c bool -- bool )	\ return boolean
253;
254
255\ This function parses $kernels into variables that are used by the menu to
256\ display which kernel to boot when the [overloaded] `boot' word is interpreted.
257\ Used internally by menu-create, you need not (nor should you) call this
258\ directly.
259\
260: parse-kernels ( N -- ) \ kernidx
261	kernidx ! ( n -- )	\ store provided `x' value
262	[char] 0 kernmenuidx !	\ initialize `y' value for menu_caption[x][y]
263
264	\ Attempt to get a list of kernels, fall back to sensible default
265	s" kernels" getenv dup -1 = if
266		drop ( cruft )
267		s" kernel kernel.old"
268	then ( -- c-addr/u )
269
270	\ Check to see if the user has altered $kernel by comparing it against
271	\ $kernel[N] where N is kernel_state (the actively displayed kernel).
272	s" kernel_state" evaluate @ 48 + s" kernel[N]" 7 +c! getenv
273	dup -1 <> if
274		s" kernel" getenv dup -1 = if
275			drop ( cruft ) s" "
276		then
277		2swap 2over compare 0= if
278			2drop FALSE ( skip below conditional )
279		else \ User has changed $kernel
280			TRUE ( slurp in new value )
281		then
282	else \ We haven't yet parsed $kernels into $kernel[N]
283		drop ( getenv cruft )
284		s" kernel" getenv dup -1 = if
285			drop ( cruft ) s" "
286		then
287		TRUE ( slurp in initial value )
288	then ( c-addr/u -- c-addr/u c-addr/u,-1 | 0 )
289	if \ slurp new value into kerndefault
290		kerndefault 1+ 0 2swap strcat swap 1- c!
291	then
292
293	\ Clear out existing parsed-kernels
294	kernidx @ [char] 0
295	begin
296		dup kernel[x] unsetenv
297		2dup menu_caption[x][y] unsetenv
298		2dup ansi_caption[x][y] unsetenv
299		1+ dup [char] 8 >
300	until
301	2drop
302
303	\ Step through the string until we find the end
304	begin
305		0 kernlen ! \ initialize length of value
306
307		\ Skip leading whitespace and/or comma delimiters
308		begin
309			dup 0<> if
310				over c@ delim? ( c-addr/u -- c-addr/u bool )
311			else
312				false ( c-addr/u -- c-addr/u bool )
313			then
314		while
315			1- swap 1+ swap ( c-addr/u -- c-addr'/u' )
316		repeat
317		( c-addr/u -- c-addr'/u' )
318
319		dup 0= if \ end of string while eating whitespace
320			2drop ( c-addr/u -- )
321			kernmenuidx @ [char] 0 <> if \ found at least one
322				exit \ all done
323			then
324
325			\ No entries in $kernels; use $kernel instead
326			s" kernel" getenv dup -1 = if
327				drop ( cruft ) s" "
328			then ( -- c-addr/u )
329			dup kernlen ! \ store entire value length as kernlen
330		else
331			\ We're still within $kernels parsing toward the end;
332			\ find delimiter/end to determine kernlen
333			2dup ( c-addr/u -- c-addr/u c-addr/u )
334			begin dup 0<> while
335				over c@ delim? if
336					drop 0 ( break ) \ found delimiter
337				else
338					kernlen @ 1+ kernlen ! \ incrememnt
339					1- swap 1+ swap \ c-addr++ u--
340				then
341			repeat
342			2drop ( c-addr/u c-addr'/u' -- c-addr/u )
343
344			\ If this is the first entry, compare it to $kernel
345			\ If different, then insert $kernel beforehand
346			kernmenuidx @ [char] 0 = if
347				over kernlen @ kerndefault count compare if
348					kernelsbuf 0 kerndefault count strcat
349					s" ," strcat 2swap strcat
350					kerndefault count swap drop kernlen !
351				then
352			then
353		then
354		( c-addr/u -- c-addr'/u' )
355
356		\ At this point, we should have something on the stack to store
357		\ as the next kernel menu option; start assembling variables
358
359		over kernlen @ ( c-addr/u -- c-addr/u c-addr/u2 )
360
361		\ Assign first to kernel[x]
362		2dup kernmenuidx @ kernel[x] setenv
363
364		\ Assign second to menu_caption[x][y]
365		kerncapbuf 0 s" [K]ernel: " strcat
366		2over strcat
367		kernidx @ kernmenuidx @ menu_caption[x][y]
368		setenv
369
370		\ Assign third to ansi_caption[x][y]
371		kerncapbuf 0 s" @[1mK@[37mernel: " [char] @ escc! strcat
372		kernmenuidx @ [char] 0 = if
373			s" default/@[32m"
374		else
375			s" @[34;1m"
376		then
377		[char] @ escc! strcat
378		2over strcat
379		s" @[37m" [char] @ escc! strcat
380		kernidx @ kernmenuidx @ ansi_caption[x][y]
381		setenv
382
383		2drop ( c-addr/u c-addr/u2 -- c-addr/u )
384
385		kernmenuidx @ 1+ dup kernmenuidx ! [char] 8 > if
386			2drop ( c-addr/u -- ) exit
387		then
388
389		kernlen @ - swap kernlen @ + swap ( c-addr/u -- c-addr'/u' )
390	again
391;
392
393\ This function goes through the kernels that were discovered by the
394\ parse-kernels function [above], adding " (# of #)" text to the end of each
395\ caption.
396\
397: tag-kernels ( -- )
398	kernidx @ ( -- x ) dup 0= if exit then
399	[char] 0 s"  (Y of Z)" ( x -- x y c-addr/u )
400	kernmenuidx @ -rot 7 +c! \ Replace 'Z' with number of kernels parsed
401	begin
402		2 pick 1+ -rot 2 +c! \ Replace 'Y' with current ASCII num
403
404		2over menu_caption[x][y] getenv dup -1 <> if
405			2dup + 1- c@ [char] ) = if
406				2drop \ Already tagged
407			else
408				kerncapbuf 0 2swap strcat
409				2over strcat
410				5 pick 5 pick menu_caption[x][y] setenv
411			then
412		else
413			drop ( getenv cruft )
414		then
415
416		2over ansi_caption[x][y] getenv dup -1 <> if
417			2dup + 1- c@ [char] ) = if
418				2drop \ Already tagged
419			else
420				kerncapbuf 0 2swap strcat
421				2over strcat
422				5 pick 5 pick ansi_caption[x][y] setenv
423			then
424		else
425			drop ( getenv cruft )
426		then
427
428		rot 1+ dup [char] 8 > if
429			-rot 2drop TRUE ( break )
430		else
431			-rot FALSE
432		then
433	until
434	2drop ( x y -- )
435;
436
437\ Illumos kernel acpi-user-options has following values:
438\ default:	0 - system will enable acpi based on bios date
439\ on:		1 - acpi is set on
440\ off:		2 - acpi is set off
441\ madt:		4 - use only MADT
442\ legacy:	8 - use legacy mode
443
444: acpi-captions ( N -- )
445  \ first entry
446  dup s" [A]CPI.... default" rot 48 menu_caption[x][y] setenv
447  dup s" ^[1mA^[mCPI.... ^[32;7mdefault^[m" rot 48 ansi_caption[x][y] setenv
448
449  dup s" [A]CPI........ On" rot 49 menu_caption[x][y] setenv
450  dup s" ^[1mA^[mCPI........ ^[34;1mOn^[m" rot 49 ansi_caption[x][y] setenv
451
452  dup s" [A]CPI........ Off" rot 50 menu_caption[x][y] setenv
453  dup s" ^[1mA^[mCPI........ ^[34;1mOff^[m" rot 50 ansi_caption[x][y] setenv
454
455  dup s" [A]CPI....... MADT" rot 51 menu_caption[x][y] setenv
456  dup s" ^[1mA^[mCPI....... ^[34;1mMADT^[m" rot 51 ansi_caption[x][y] setenv
457
458  dup s" [A]CPI..... Legacy" rot 52 menu_caption[x][y] setenv
459  s" ^[1mA^[mCPI..... ^[34;1mLegacy^[m" rot 52 ansi_caption[x][y] setenv
460;
461
462\ Illumos console has following values:
463\ text, ttya, ttyb, ttyc, ttyd
464
465: osconsole-captions ( N -- )
466  \ first entry
467  dup s" Os[C]onsole.. text" rot 48 menu_caption[x][y] setenv
468  dup s" Os^[1mC^[monsole.. ^[32;7mtext^[m" rot 48 ansi_caption[x][y] setenv
469
470  dup s" Os[C]onsole.. ttya" rot 49 menu_caption[x][y] setenv
471  dup s" Os^[1mC^[monsole.. ^[34;1mttya^[m" rot 49 ansi_caption[x][y] setenv
472
473  dup s" Os[C]onsole.. ttyb" rot 50 menu_caption[x][y] setenv
474  dup s" Os^[1mC^[monsole.. ^[34;1mttyb^[m" rot 50 ansi_caption[x][y] setenv
475
476  dup s" Os[C]onsole.. ttyc" rot 51 menu_caption[x][y] setenv
477  dup s" Os^[1mC^[monsole.. ^[34;1mttyc^[m" rot 51 ansi_caption[x][y] setenv
478
479  dup s" Os[C]onsole.. ttyd" rot 52 menu_caption[x][y] setenv
480  s" Os^[1mC^[monsole.. ^[34;1mttyd^[m" rot 52 ansi_caption[x][y] setenv
481;
482
483\ This function creates the list of menu items. This function is called by the
484\ menu-display function. You need not call it directly.
485\
486: menu-create ( -- )
487
488	\ Print the frame caption at (x,y)
489	s" loader_menu_title" getenv dup -1 = if
490		drop s"  Welcome on board "
491	then
492	TRUE ( use default alignment )
493	s" loader_menu_title_align" getenv dup -1 <> if
494		2dup s" left" compare-insensitive 0= if ( 1 )
495			2drop ( c-addr/u ) drop ( bool )
496			menuX @ menuY @ 1-
497			FALSE ( don't use default alignment )
498		else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 )
499			2drop ( c-addr/u ) drop ( bool )
500			menuX @ 42 + 4 - over - menuY @ 1-
501			FALSE ( don't use default alignment )
502		else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then
503	else
504		drop ( getenv cruft )
505	then
506	if ( use default center alignement? )
507		menuX @ 19 + over 2 / - menuY @ 1-
508	then
509	at-xy type
510
511	\ If $menu_init is set, evaluate it (allowing for whole menus to be
512	\ constructed dynamically -- as this function could conceivably set
513	\ the remaining environment variables to construct the menu entirely).
514	\
515	s" menu_init" getenv dup -1 <> if
516		evaluate
517	else
518		drop
519	then
520
521	\ Print our menu options with respective key/variable associations.
522	\ `printmenuitem' ends by adding the decimal ASCII value for the
523	\ numerical prefix to the stack. We store the value left on the stack
524	\ to the key binding variable for later testing against a character
525	\ captured by the `getkey' function.
526
527	\ Note that any menu item beyond 9 will have a numerical prefix on the
528	\ screen consisting of the first digit (ie. 1 for the tenth menu item)
529	\ and the key required to activate that menu item will be the decimal
530	\ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
531	\ which is misleading and not desirable.
532	\
533	\ Thus, we do not allow more than 8 configurable items on the menu
534	\ (with "Reboot" as the optional ninth and highest numbered item).
535
536	\
537	\ Initialize the OsConsole option status.
538	\
539	0 menuosconsole !
540	s" menu_osconsole" getenv -1 <> if
541		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
542			dup menuosconsole !
543			dup osconsole-captions
544
545			s" init_osconsole" evaluate
546
547			\ Get the current cycle state (entry to use)
548			s" osconsole_state" evaluate @ 48 + ( n -- n y )
549
550			\ Set the current non-ANSI caption
551			2dup swap dup ( n y -- n y y n n )
552			s" set menu_caption[x]=$menu_caption[x][y]"
553			17 +c! 34 +c! 37 +c! evaluate
554			( n y y n n c-addr/u -- n y  )
555
556			\ Set the current ANSI caption
557			2dup swap dup ( n y -- n y y n n )
558			s" set ansi_caption[x]=$ansi_caption[x][y]"
559			17 +c! 34 +c! 37 +c! evaluate
560			( n y y n n c-addr/u -- n y )
561
562			\ Initialize cycle state from stored value
563			48 - ( n y -- n k )
564			s" init_cyclestate" evaluate ( n k -- n )
565
566			\ Set $os_console
567			s" activate_osconsole" evaluate ( n -- n )
568		then
569		drop
570	then
571
572	\
573	\ Initialize the ACPI option status.
574	\
575	0 menuacpi !
576	s" menu_acpi" getenv -1 <> if
577		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
578			dup menuacpi !
579			dup acpi-captions
580
581			s" init_acpi" evaluate
582
583			\ Get the current cycle state (entry to use)
584			s" acpi_state" evaluate @ 48 + ( n -- n y )
585
586			\ Set the current non-ANSI caption
587			2dup swap dup ( n y -- n y y n n )
588			s" set menu_caption[x]=$menu_caption[x][y]"
589			17 +c! 34 +c! 37 +c! evaluate
590			( n y y n n c-addr/u -- n y  )
591
592			\ Set the current ANSI caption
593			2dup swap dup ( n y -- n y y n n )
594			s" set ansi_caption[x]=$ansi_caption[x][y]"
595			17 +c! 34 +c! 37 +c! evaluate
596			( n y y n n c-addr/u -- n y )
597
598			\ Initialize cycle state from stored value
599			48 - ( n y -- n k )
600			s" init_cyclestate" evaluate ( n k -- n )
601
602			\ Set $acpi-user-options
603			s" activate_acpi" evaluate ( n -- n )
604		then
605		drop
606	then
607
608	\
609	\ Initialize kernel captions after parsing $kernels
610	\
611	0 menukernel !
612	s" menu_kernel" getenv -1 <> if
613		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
614			dup menukernel !
615			dup parse-kernels tag-kernels
616
617			\ Get the current cycle state (entry to use)
618			s" kernel_state" evaluate @ 48 + ( n -- n y )
619
620			\ If state is invalid, reset
621			dup kernmenuidx @ 1- > if
622				drop [char] 0 ( n y -- n 48 )
623				0 s" kernel_state" evaluate !
624				over s" init_kernel" evaluate drop
625			then
626
627			\ Set the current non-ANSI caption
628			2dup swap dup ( n y -- n y y n n )
629			s" set menu_caption[x]=$menu_caption[x][y]"
630			17 +c! 34 +c! 37 +c! evaluate
631			( n y y n n c-addr/u -- n y  )
632
633			\ Set the current ANSI caption
634			2dup swap dup ( n y -- n y y n n )
635			s" set ansi_caption[x]=$ansi_caption[x][y]"
636			17 +c! 34 +c! 37 +c! evaluate
637			( n y y n n c-addr/u -- n y )
638
639			\ Initialize cycle state from stored value
640			48 - ( n y -- n k )
641			s" init_cyclestate" evaluate ( n k -- n )
642
643			\ Set $kernel to $kernel[y]
644			s" activate_kernel" evaluate ( n -- n )
645		then
646		drop
647	then
648
649	\
650	\ Initialize the menu_options visual separator.
651	\
652	0 menuoptions !
653	s" menu_options" getenv -1 <> if
654		c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
655			menuoptions !
656		else
657			drop
658		then
659	then
660
661	\ Initialize "Reboot" menu state variable (prevents double-entry)
662	false menurebootadded !
663
664	menu_start
665	1- menuidx !    \ Initialize the starting index for the menu
666	0 menurow !     \ Initialize the starting position for the menu
667
668	49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
669	begin
670		\ If the "Options:" separator, print it.
671		dup menuoptions @ = if
672			\ Optionally add a reboot option to the menu
673			s" menu_reboot" getenv -1 <> if
674				drop
675				s" Reboot" printmenuitem menureboot !
676				true menurebootadded !
677			then
678
679			menuX @
680			menurow @ 2 + menurow !
681			menurow @ menuY @ +
682			at-xy
683			s" menu_optionstext" getenv dup -1 <> if
684				type
685			else
686				drop ." Options:"
687			then
688		then
689
690		\ make sure we have not already initialized this item
691		dup init_stateN dup @ 0= if
692			1 swap !
693
694			\ If this menuitem has an initializer, run it
695			dup menu_init[x]
696			getenv dup -1 <> if
697				evaluate
698			else
699				drop
700			then
701		else
702			drop
703		then
704
705		dup
706		loader_color? if
707			ansi_caption[x]
708		else
709			menu_caption[x]
710		then
711
712		dup -1 <> if
713			\ test for environment variable
714			getenv dup -1 <> if
715				printmenuitem ( c-addr/u -- n )
716				dup menukeyN !
717			else
718				drop
719			then
720		else
721			drop
722		then
723
724		1+ dup 56 > \ add 1 to iterator, continue if less than 57
725	until
726	drop \ iterator
727
728	\ Optionally add a reboot option to the menu
729	menurebootadded @ true <> if
730		s" menu_reboot" getenv -1 <> if
731			drop       \ no need for the value
732			s" Reboot" \ menu caption (required by printmenuitem)
733
734			printmenuitem
735			menureboot !
736		else
737			0 menureboot !
738		then
739	then
740;
741
742\ Takes a single integer on the stack and updates the timeout display. The
743\ integer must be between 0 and 9 (we will only update a single digit in the
744\ source message).
745\
746: menu-timeout-update ( N -- )
747
748	\ Enforce minimum/maximum
749	dup 9 > if drop 9 then
750	dup 0 < if drop 0 then
751
752	s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
753
754	2 pick 0> if
755		rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII
756		12 +c!        ( n' c-addr/u -- c-addr/u )   \ replace 'N' above
757
758		menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
759		type ( c-addr/u -- ) \ print message
760	else
761		menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
762		spaces ( n c-addr/u -- n c-addr ) \ erase message
763		2drop ( n c-addr -- )
764	then
765
766	0 25 at-xy ( position cursor back at bottom-left )
767;
768
769\ This function blocks program flow (loops forever) until a key is pressed.
770\ The key that was pressed is added to the top of the stack in the form of its
771\ decimal ASCII representation. This function is called by the menu-display
772\ function. You need not call it directly.
773\ note, the esc sequences will be dropped, this needs to be changed if
774\ menu is built based on arrow keys.
775\
776: getkey ( -- ascii_keycode )
777
778	begin \ loop forever
779
780		menu_timeout_enabled @ 1 = if
781			( -- )
782			seconds ( get current time: -- N )
783			dup menu_time @ <> if ( has time elapsed?: N N N -- N )
784
785				\ At least 1 second has elapsed since last loop
786				\ so we will decrement our "timeout" (really a
787				\ counter, insuring that we do not proceed too
788				\ fast) and update our timeout display.
789
790				menu_time ! ( update time record: N -- )
791				menu_timeout @ ( "time" remaining: -- N )
792				dup 0> if ( greater than 0?: N N 0 -- N )
793					1- ( decrement counter: N -- N )
794					dup menu_timeout !
795						( re-assign: N N Addr -- N )
796				then
797				( -- N )
798
799				dup 0= swap 0< or if ( N <= 0?: N N -- )
800					\ halt the timer
801					0 menu_timeout ! ( 0 Addr -- )
802					0 menu_timeout_enabled ! ( 0 Addr -- )
803				then
804
805				\ update the timer display ( N -- )
806				menu_timeout @ menu-timeout-update
807
808				menu_timeout @ 0= if
809					\ We've reached the end of the timeout
810					\ (user did not cancel by pressing ANY
811					\ key)
812
813					s" menu_timeout_command"  getenv dup
814					-1 = if
815						drop \ clean-up
816					else
817						evaluate
818					then
819				then
820
821			else ( -- N )
822				\ No [detectable] time has elapsed (in seconds)
823				drop ( N -- )
824			then
825			( -- )
826		then
827
828		key? if \ Was a key pressed? (see loader(8))
829
830			\ An actual key was pressed (if the timeout is running,
831			\ kill it regardless of which key was pressed)
832			menu_timeout @ 0<> if
833				0 menu_timeout !
834				0 menu_timeout_enabled !
835
836				\ clear screen of timeout message
837				0 menu-timeout-update
838			then
839
840			\ get the key that was pressed and exit (if we
841			\ get a non-zero ASCII code)
842			key dup 0<> if
843				dup 0x1b = if
844					key? if ( is it sequence? )
845						drop
846						begin
847							key?
848						while
849							key drop
850						repeat
851					else
852						exit
853					then
854				else
855					exit
856				then
857			else
858				drop
859			then
860		then
861		50 ms \ sleep for 50 milliseconds (see loader(8))
862
863	again
864;
865
866: menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1.
867
868	\ Clear the screen area associated with the interactive menu
869	menuX @ menuY @
870	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
871	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
872	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
873	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
874	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces 1+
875	2dup at-xy 38 spaces 1+		2dup at-xy 38 spaces
876	2drop
877
878	\ Reset the starting index and position for the menu
879	menu_start 1- menuidx !
880	0 menurow !
881;
882
883only forth
884also menu-infrastructure
885also menu-namespace
886also menu-command-helpers definitions
887
888: toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
889
890	\ ASCII numeral equal to user-selected menu item must be on the stack.
891	\ We do not modify the stack, so the ASCII numeral is left on top.
892
893	dup init_textN c@ 0= if
894		\ NOTE: no need to check toggle_stateN since the first time we
895		\ are called, we will populate init_textN. Further, we don't
896		\ need to test whether menu_caption[x] (ansi_caption[x] when
897		\ loader_color?=1) is available since we would not have been
898		\ called if the caption was NULL.
899
900		\ base name of environment variable
901		dup ( n -- n n ) \ key pressed
902		loader_color? if
903			ansi_caption[x]
904		else
905			menu_caption[x]
906		then
907		getenv dup -1 <> if
908
909			2 pick ( n c-addr/u -- n c-addr/u n )
910			init_textN ( n c-addr/u n -- n c-addr/u c-addr )
911
912			\ now we have the buffer c-addr on top
913			\ ( followed by c-addr/u of current caption )
914
915			\ Copy the current caption into our buffer
916			2dup c! -rot \ store strlen at first byte
917			begin
918				rot 1+    \ bring alt addr to top and increment
919				-rot -rot \ bring buffer addr to top
920				2dup c@ swap c! \ copy current character
921				1+     \ increment buffer addr
922				rot 1- \ bring buffer len to top and decrement
923				dup 0= \ exit loop if buffer len is zero
924			until
925			2drop \ buffer len/addr
926			drop  \ alt addr
927
928		else
929			drop
930		then
931	then
932
933	\ Now we are certain to have init_textN populated with the initial
934	\ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
935	\ We can now use init_textN as the untoggled caption and
936	\ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
937	\ toggled caption and store the appropriate value into menu_caption[x]
938	\ (again, ansi_caption[x] with loader_color enabled). Last, we'll
939	\ negate the toggled state so that we reverse the flow on subsequent
940	\ calls.
941
942	dup toggle_stateN @ 0= if
943		\ state is OFF, toggle to ON
944
945		dup ( n -- n n ) \ key pressed
946		loader_color? if
947			toggled_ansi[x]
948		else
949			toggled_text[x]
950		then
951		getenv dup -1 <> if
952			\ Assign toggled text to menu caption
953			2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
954			loader_color? if
955				ansi_caption[x]
956			else
957				menu_caption[x]
958			then
959			setenv
960		else
961			\ No toggled text, keep the same caption
962			drop ( n -1 -- n ) \ getenv cruft
963		then
964
965		true \ new value of toggle state var (to be stored later)
966	else
967		\ state is ON, toggle to OFF
968
969		dup init_textN count ( n -- n c-addr/u )
970
971		\ Assign init_textN text to menu caption
972		2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
973		loader_color? if
974			ansi_caption[x]
975		else
976			menu_caption[x]
977		then
978		setenv
979
980		false \ new value of toggle state var (to be stored below)
981	then
982
983	\ now we'll store the new toggle state (on top of stack)
984	over toggle_stateN !
985;
986
987: cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
988
989	\ ASCII numeral equal to user-selected menu item must be on the stack.
990	\ We do not modify the stack, so the ASCII numeral is left on top.
991
992	dup cycle_stateN dup @ 1+ \ get value and increment
993
994	\ Before assigning the (incremented) value back to the pointer,
995	\ let's test for the existence of this particular array element.
996	\ If the element exists, we'll store index value and move on.
997	\ Otherwise, we'll loop around to zero and store that.
998
999	dup 48 + ( n addr k -- n addr k k' )
1000	         \ duplicate array index and convert to ASCII numeral
1001
1002	3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y)
1003	loader_color? if
1004		ansi_caption[x][y]
1005	else
1006		menu_caption[x][y]
1007	then
1008	( n addr k n k' -- n addr k c-addr/u )
1009
1010	\ Now test for the existence of our incremented array index in the
1011	\ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
1012	\ enabled) as set in loader.rc(5), et. al.
1013
1014	getenv dup -1 = if
1015		\ No caption set for this array index. Loop back to zero.
1016
1017		drop ( n addr k -1 -- n addr k ) \ getenv cruft
1018		drop 0 ( n addr k -- n addr 0 )  \ new value to store later
1019
1020		2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y)
1021		loader_color? if
1022			ansi_caption[x][y]
1023		else
1024			menu_caption[x][y]
1025		then
1026		( n addr 0 n 48 -- n addr 0 c-addr/u )
1027		getenv dup -1 = if
1028			\ Highly unlikely to occur, but to ensure things move
1029			\ along smoothly, allocate a temporary NULL string
1030			drop ( cruft ) s" "
1031		then
1032	then
1033
1034	\ At this point, we should have the following on the stack (in order,
1035	\ from bottom to top):
1036	\
1037	\    n        - Ascii numeral representing the menu choice (inherited)
1038	\    addr     - address of our internal cycle_stateN variable
1039	\    k        - zero-based number we intend to store to the above
1040	\    c-addr/u - string value we intend to store to menu_caption[x]
1041	\               (or ansi_caption[x] with loader_color enabled)
1042	\
1043	\ Let's perform what we need to with the above.
1044
1045	\ Assign array value text to menu caption
1046	4 pick ( n addr k c-addr/u -- n addr k c-addr/u n )
1047	loader_color? if
1048		ansi_caption[x]
1049	else
1050		menu_caption[x]
1051	then
1052	setenv
1053
1054	swap ! ( n addr k -- n ) \ update array state variable
1055;
1056
1057only forth definitions also menu-infrastructure
1058
1059\ Erase and redraw the menu. Useful if you change a caption and want to
1060\ update the menu to reflect the new value.
1061\
1062: menu-redraw ( -- )
1063	menu-erase
1064	menu-create
1065;
1066
1067\ This function initializes the menu. Call this from your `loader.rc' file
1068\ before calling any other menu-related functions.
1069\
1070: menu-init ( -- )
1071	menu_start
1072	1- menuidx !    \ Initialize the starting index for the menu
1073	0 menurow !     \ Initialize the starting position for the menu
1074
1075	\ Assign configuration values
1076	s" loader_menu_y" getenv dup -1 = if
1077		drop \ no custom row position
1078		menu_default_y
1079	else
1080		\ make sure custom position is a number
1081		?number 0= if
1082			menu_default_y \ or use default
1083		then
1084	then
1085	menuY !
1086	s" loader_menu_x" getenv dup -1 = if
1087		drop \ no custom column position
1088		menu_default_x
1089	else
1090		\ make sure custom position is a number
1091		?number 0= if
1092			menu_default_x \ or use default
1093		then
1094	then
1095	menuX !
1096
1097	\ Interpret a custom frame type for the menu
1098	TRUE ( draw a box? default yes, but might be altered below )
1099	s" loader_menu_frame" getenv dup -1 = if ( 1 )
1100		drop \ no custom frame type
1101	else ( 1 )  2dup s" single" compare-insensitive 0= if ( 2 )
1102		f_single ( see frames.4th )
1103	else ( 2 )  2dup s" double" compare-insensitive 0= if ( 3 )
1104		f_double ( see frames.4th )
1105	else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
1106		drop FALSE \ don't draw a box
1107	( 4 ) then ( 3 ) then ( 2 )  then ( 1 ) then
1108	if
1109		42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
1110	then
1111
1112	0 25 at-xy \ Move cursor to the bottom for output
1113;
1114
1115also menu-namespace
1116
1117\ Main function. Call this from your `loader.rc' file.
1118\
1119: menu-display ( -- )
1120
1121	0 menu_timeout_enabled ! \ start with automatic timeout disabled
1122
1123	\ check indication that automatic execution after delay is requested
1124	s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
1125		drop ( just testing existence right now: Addr -- )
1126
1127		\ initialize state variables
1128		seconds menu_time ! ( store the time we started )
1129		1 menu_timeout_enabled ! ( enable automatic timeout )
1130
1131		\ read custom time-duration (if set)
1132		s" autoboot_delay" getenv dup -1 = if
1133			drop \ no custom duration (remove dup'd bunk -1)
1134			menu_timeout_default \ use default setting
1135		else
1136			2dup ?number 0= if ( if not a number )
1137				\ disable timeout if "NO", else use default
1138				s" NO" compare-insensitive 0= if
1139					0 menu_timeout_enabled !
1140					0 ( assigned to menu_timeout below )
1141				else
1142					menu_timeout_default
1143				then
1144			else
1145				-rot 2drop
1146
1147				\ boot immediately if less than zero
1148				dup 0< if
1149					drop
1150					menu-create
1151					0 25 at-xy
1152					0 boot
1153				then
1154			then
1155		then
1156		menu_timeout ! ( store value on stack from above )
1157
1158		menu_timeout_enabled @ 1 = if
1159			\ read custom column position (if set)
1160			s" loader_menu_timeout_x" getenv dup -1 = if
1161				drop \ no custom column position
1162				menu_timeout_default_x \ use default setting
1163			else
1164				\ make sure custom position is a number
1165				?number 0= if
1166					menu_timeout_default_x \ or use default
1167				then
1168			then
1169			menu_timeout_x ! ( store value on stack from above )
1170
1171			\ read custom row position (if set)
1172			s" loader_menu_timeout_y" getenv dup -1 = if
1173				drop \ no custom row position
1174				menu_timeout_default_y \ use default setting
1175			else
1176				\ make sure custom position is a number
1177				?number 0= if
1178					menu_timeout_default_y \ or use default
1179				then
1180			then
1181			menu_timeout_y ! ( store value on stack from above )
1182		then
1183	then
1184
1185	menu-create
1186
1187	begin \ Loop forever
1188
1189		0 25 at-xy \ Move cursor to the bottom for output
1190		getkey     \ Block here, waiting for a key to be pressed
1191
1192		dup -1 = if
1193			drop exit \ Caught abort (abnormal return)
1194		then
1195
1196		\ Boot if the user pressed Enter/Ctrl-M (13) or
1197		\ Ctrl-Enter/Ctrl-J (10)
1198		dup over 13 = swap 10 = or if
1199			drop ( no longer needed )
1200			s" boot" evaluate
1201			exit ( pedantic; never reached )
1202		then
1203
1204		dup menureboot @ = if 0 reboot then
1205
1206		\ Evaluate the decimal ASCII value against known menu item
1207		\ key associations and act accordingly
1208
1209		49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
1210		begin
1211			dup menukeyN @
1212			rot tuck = if
1213
1214				\ Adjust for missing ACPI menuitem on non-i386
1215\ 				arch-i386? true <> menuacpi @ 0<> and if
1216\ 					menuacpi @ over 2dup < -rot = or
1217\ 					over 58 < and if
1218\ 					( key >= menuacpi && key < 58: N -- N )
1219\ 						1+
1220\ 					then
1221\ 				then
1222
1223				\ Test for the environment variable
1224				dup menu_command[x]
1225				getenv dup -1 <> if
1226					\ Execute the stored procedure
1227					evaluate
1228
1229					\ We expect there to be a non-zero
1230					\  value left on the stack after
1231					\ executing the stored procedure.
1232					\ If so, continue to run, else exit.
1233
1234					0= if
1235						drop \ key pressed
1236						drop \ loop iterator
1237						exit
1238					else
1239						swap \ need iterator on top
1240					then
1241				then
1242
1243				\ Re-adjust for missing ACPI menuitem
1244\				arch-i386? true <> menuacpi @ 0<> and if
1245\					swap
1246\					menuacpi @ 1+ over 2dup < -rot = or
1247\					over 59 < and if
1248\						1-
1249\					then
1250\					swap
1251\				then
1252			else
1253				swap \ need iterator on top
1254			then
1255
1256			\
1257			\ Check for menu keycode shortcut(s)
1258			\
1259			dup menu_keycode[x]
1260			getenv dup -1 = if
1261				drop
1262			else
1263				?number 0<> if
1264					rot tuck = if
1265						swap
1266						dup menu_command[x]
1267						getenv dup -1 <> if
1268							evaluate
1269							0= if
1270								2drop
1271								exit
1272							then
1273						else
1274							drop
1275						then
1276					else
1277						swap
1278					then
1279				then
1280			then
1281
1282			1+ dup 56 > \ increment iterator
1283			            \ continue if less than 57
1284		until
1285		drop \ loop iterator
1286		drop \ key pressed
1287
1288	again	\ Non-operational key was pressed; repeat
1289;
1290
1291\ This function unsets all the possible environment variables associated with
1292\ creating the interactive menu.
1293\
1294: menu-unset ( -- )
1295
1296	49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
1297	begin
1298		dup menu_init[x]    unsetenv	\ menu initializer
1299		dup menu_command[x] unsetenv	\ menu command
1300		dup menu_caption[x] unsetenv	\ menu caption
1301		dup ansi_caption[x] unsetenv	\ ANSI caption
1302		dup menu_keycode[x] unsetenv	\ menu keycode
1303		dup toggled_text[x] unsetenv	\ toggle_menuitem caption
1304		dup toggled_ansi[x] unsetenv	\ toggle_menuitem ANSI caption
1305
1306		48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9')
1307		begin
1308			\ cycle_menuitem caption and ANSI caption
1309			2dup menu_caption[x][y] unsetenv
1310			2dup ansi_caption[x][y] unsetenv
1311			1+ dup 57 >
1312		until
1313		drop \ inner iterator
1314
1315		0 over menukeyN      !	\ used by menu-create, menu-display
1316		0 over init_stateN   !	\ used by menu-create
1317		0 over toggle_stateN !	\ used by toggle_menuitem
1318		0 over init_textN   c!	\ used by toggle_menuitem
1319		0 over cycle_stateN  !	\ used by cycle_menuitem
1320
1321		1+ dup 56 >	\ increment, continue if less than 57
1322	until
1323	drop \ iterator
1324
1325	s" menu_timeout_command" unsetenv	\ menu timeout command
1326	s" menu_reboot"          unsetenv	\ Reboot menu option flag
1327	s" menu_acpi"            unsetenv	\ ACPI menu option flag
1328	s" menu_osconsole"       unsetenv	\ osconsole menu option flag
1329	s" menu_kernel"          unsetenv	\ Kernel menu option flag
1330	s" menu_options"         unsetenv	\ Options separator flag
1331	s" menu_optionstext"     unsetenv	\ separator display text
1332	s" menu_init"            unsetenv	\ menu initializer
1333
1334	0 menureboot !
1335	0 menuacpi !
1336	0 menuosconsole !
1337	0 menuoptions !
1338;
1339
1340only forth definitions also menu-infrastructure
1341
1342\ This function both unsets menu variables and visually erases the menu area
1343\ in-preparation for another menu.
1344\
1345: menu-clear ( -- )
1346	menu-unset
1347	menu-erase
1348;
1349
1350bullet menubllt !
1351
1352also menu-namespace
1353
1354\ Initialize our menu initialization state variables
13550 init_state1 !
13560 init_state2 !
13570 init_state3 !
13580 init_state4 !
13590 init_state5 !
13600 init_state6 !
13610 init_state7 !
13620 init_state8 !
1363
1364\ Initialize our boolean state variables
13650 toggle_state1 !
13660 toggle_state2 !
13670 toggle_state3 !
13680 toggle_state4 !
13690 toggle_state5 !
13700 toggle_state6 !
13710 toggle_state7 !
13720 toggle_state8 !
1373
1374\ Initialize our array state variables
13750 cycle_state1 !
13760 cycle_state2 !
13770 cycle_state3 !
13780 cycle_state4 !
13790 cycle_state5 !
13800 cycle_state6 !
13810 cycle_state7 !
13820 cycle_state8 !
1383
1384\ Initialize string containers
13850 init_text1 c!
13860 init_text2 c!
13870 init_text3 c!
13880 init_text4 c!
13890 init_text5 c!
13900 init_text6 c!
13910 init_text7 c!
13920 init_text8 c!
1393
1394only forth definitions
1395