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