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