]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/forth/menu.4th
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / boot / forth / menu.4th
1 \ Copyright (c) 2003 Scott Long <scottl@freebsd.org>
2 \ Copyright (c) 2003 Aleksander Fafula <alex@fafula.com>
3 \ Copyright (c) 2006-2013 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
29 marker task-menu.4th
30
31 \ Frame drawing
32 include /boot/frames.4th
33
34 f_double        \ Set frames to double (see frames.4th). Replace with
35                 \ f_single if you want single frames.
36 46 constant dot \ ASCII definition of a period (in decimal)
37
38  5 constant menu_default_x         \ default column position of timeout
39 10 constant menu_default_y         \ default row position of timeout msg
40  4 constant menu_timeout_default_x \ default column position of timeout
41 23 constant menu_timeout_default_y \ default row position of timeout msg
42 10 constant menu_timeout_default   \ default timeout (in seconds)
43
44 \ Customize the following values with care
45
46   1 constant menu_start \ Numerical prefix of first menu item
47 dot constant bullet     \ Menu bullet (appears after numerical prefix)
48   5 constant menu_x     \ Row position of the menu (from the top)
49  10 constant menu_y     \ Column position of the menu (from left side)
50
51 \ Menu Appearance
52 variable menuidx   \ Menu item stack for number prefixes
53 variable menurow   \ Menu item stack for positioning
54 variable menubllt  \ Menu item bullet
55
56 \ Menu Positioning
57 variable menuX     \ Menu X offset (columns)
58 variable menuY     \ Menu Y offset (rows)
59
60 \ Menu-item key association/detection
61 variable menukey1
62 variable menukey2
63 variable menukey3
64 variable menukey4
65 variable menukey5
66 variable menukey6
67 variable menukey7
68 variable menukey8
69 variable menureboot
70 variable menurebootadded
71 variable menuacpi
72 variable menuoptions
73
74 \ Menu timer [count-down] variables
75 variable menu_timeout_enabled \ timeout state (internal use only)
76 variable menu_time            \ variable for tracking the passage of time
77 variable menu_timeout         \ determined configurable delay duration
78 variable menu_timeout_x       \ column position of timeout message
79 variable menu_timeout_y       \ row position of timeout message
80
81 \ Menu initialization status variables
82 variable init_state1
83 variable init_state2
84 variable init_state3
85 variable init_state4
86 variable init_state5
87 variable init_state6
88 variable init_state7
89 variable init_state8
90
91 \ Boolean option status variables
92 variable toggle_state1
93 variable toggle_state2
94 variable toggle_state3
95 variable toggle_state4
96 variable toggle_state5
97 variable toggle_state6
98 variable toggle_state7
99 variable toggle_state8
100
101 \ Array option status variables
102 variable cycle_state1
103 variable cycle_state2
104 variable cycle_state3
105 variable cycle_state4
106 variable cycle_state5
107 variable cycle_state6
108 variable cycle_state7
109 variable cycle_state8
110
111 \ Containers for storing the initial caption text
112 create init_text1 255 allot
113 create init_text2 255 allot
114 create init_text3 255 allot
115 create init_text4 255 allot
116 create init_text5 255 allot
117 create init_text6 255 allot
118 create init_text7 255 allot
119 create init_text8 255 allot
120
121 : +c! ( N C-ADDR/U K -- C-ADDR/U )
122         3 pick 3 pick   ( n c-addr/u k -- n c-addr/u k n c-addr )
123         rot + c!        ( n c-addr/u k n c-addr -- n c-addr/u )
124         rot drop        ( n c-addr/u -- c-addr/u )
125 ;
126
127 : menukeyN      ( N -- ADDR )   s" menukeyN"       7 +c! evaluate ;
128 : init_stateN   ( N -- ADDR )   s" init_stateN"   10 +c! evaluate ;
129 : toggle_stateN ( N -- ADDR )   s" toggle_stateN" 12 +c! evaluate ;
130 : cycle_stateN  ( N -- ADDR )   s" cycle_stateN"  11 +c! evaluate ;
131 : init_textN    ( N -- C-ADDR ) s" init_textN"     9 +c! evaluate ;
132
133 : str_loader_menu_frame       ( -- C-ADDR/U ) s" loader_menu_frame" ;
134 : str_loader_menu_title       ( -- C-ADDR/U ) s" loader_menu_title" ;
135 : str_loader_menu_title_align ( -- C-ADDR/U ) s" loader_menu_title_align" ;
136 : str_loader_menu_x           ( -- C-ADDR/U ) s" loader_menu_x" ;
137 : str_loader_menu_y           ( -- C-ADDR/U ) s" loader_menu_y" ;
138 : str_loader_menu_timeout_x   ( -- C-ADDR/U ) s" loader_menu_timeout_x" ;
139 : str_loader_menu_timeout_y   ( -- C-ADDR/U ) s" loader_menu_timeout_y" ;
140 : str_menu_init               ( -- C-ADDR/U ) s" menu_init" ;
141 : str_menu_timeout_command    ( -- C-ADDR/U ) s" menu_timeout_command" ;
142 : str_menu_reboot             ( -- C-ADDR/U ) s" menu_reboot" ;
143 : str_menu_acpi               ( -- C-ADDR/U ) s" menu_acpi" ;
144 : str_menu_options            ( -- C-ADDR/U ) s" menu_options" ;
145 : str_menu_optionstext        ( -- C-ADDR/U ) s" menu_optionstext" ;
146
147 : str_menu_init[x]          ( -- C-ADDR/U ) s" menu_init[x]" ;
148 : str_menu_command[x]       ( -- C-ADDR/U ) s" menu_command[x]" ;
149 : str_menu_caption[x]       ( -- C-ADDR/U ) s" menu_caption[x]" ;
150 : str_ansi_caption[x]       ( -- C-ADDR/U ) s" ansi_caption[x]" ;
151 : str_menu_keycode[x]       ( -- C-ADDR/U ) s" menu_keycode[x]" ;
152 : str_toggled_text[x]       ( -- C-ADDR/U ) s" toggled_text[x]" ;
153 : str_toggled_ansi[x]       ( -- C-ADDR/U ) s" toggled_ansi[x]" ;
154 : str_menu_caption[x][y]    ( -- C-ADDR/U ) s" menu_caption[x][y]" ;
155 : str_ansi_caption[x][y]    ( -- C-ADDR/U ) s" ansi_caption[x][y]" ;
156
157 : menu_init[x]       ( N -- C-ADDR/U )   str_menu_init[x]       10 +c! ;
158 : menu_command[x]    ( N -- C-ADDR/U )   str_menu_command[x]    13 +c! ;
159 : menu_caption[x]    ( N -- C-ADDR/U )   str_menu_caption[x]    13 +c! ;
160 : ansi_caption[x]    ( N -- C-ADDR/U )   str_ansi_caption[x]    13 +c! ;
161 : menu_keycode[x]    ( N -- C-ADDR/U )   str_menu_keycode[x]    13 +c! ;
162 : toggled_text[x]    ( N -- C-ADDR/U )   str_toggled_text[x]    13 +c! ;
163 : toggled_ansi[x]    ( N -- C-ADDR/U )   str_toggled_ansi[x]    13 +c! ;
164 : menu_caption[x][y] ( N M -- C-ADDR/U ) str_menu_caption[x][y] 16 +c! 13 +c! ;
165 : ansi_caption[x][y] ( N M -- C-ADDR/U ) str_ansi_caption[x][y] 16 +c! 13 +c! ;
166
167 : arch-i386? ( -- BOOL ) \ Returns TRUE (-1) on i386, FALSE (0) otherwise.
168         s" arch-i386" environment? dup if
169                 drop
170         then
171 ;
172
173 \ This function prints a menu item at menuX (row) and menuY (column), returns
174 \ the incremental decimal ASCII value associated with the menu item, and
175 \ increments the cursor position to the next row for the creation of the next
176 \ menu item. This function is called by the menu-create function. You need not
177 \ call it directly.
178
179 : printmenuitem ( menu_item_str -- ascii_keycode )
180
181         menurow dup @ 1+ swap ! ( increment menurow )
182         menuidx dup @ 1+ swap ! ( increment menuidx )
183
184         \ Calculate the menuitem row position
185         menurow @ menuY @ +
186
187         \ Position the cursor at the menuitem position
188         dup menuX @ swap at-xy
189
190         \ Print the value of menuidx
191         loader_color? if
192                 ." \e[1m" ( \e[22m )
193         then
194         menuidx @ .
195         loader_color? if
196                 ." \e[37m" ( \e[39m )
197         then
198
199         \ Move the cursor forward 1 column
200         dup menuX @ 1+ swap at-xy
201
202         menubllt @ emit \ Print the menu bullet using the emit function
203
204         \ Move the cursor to the 3rd column from the current position
205         \ to allow for a space between the numerical prefix and the
206         \ text caption
207         menuX @ 3 + swap at-xy
208
209         \ Print the menu caption (we expect a string to be on the stack
210         \ prior to invoking this function)
211         type
212
213         \ Here we will add the ASCII decimal of the numerical prefix
214         \ to the stack (decimal ASCII for `1' is 49) as a "return value"
215         menuidx @ 48 +
216 ;
217
218 : toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
219
220         \ ASCII numeral equal to user-selected menu item must be on the stack.
221         \ We do not modify the stack, so the ASCII numeral is left on top.
222
223         dup init_textN c@ 0= if
224                 \ NOTE: no need to check toggle_stateN since the first time we
225                 \ are called, we will populate init_textN. Further, we don't
226                 \ need to test whether menu_caption[x] (ansi_caption[x] when
227                 \ loader_color?=1) is available since we would not have been
228                 \ called if the caption was NULL.
229
230                 \ base name of environment variable
231                 dup ( n -- n n ) \ key pressed
232                 loader_color? if
233                         ansi_caption[x]
234                 else
235                         menu_caption[x]
236                 then    
237                 getenv dup -1 <> if
238
239                         2 pick ( n c-addr/u -- n c-addr/u n )
240                         init_textN ( n c-addr/u n -- n c-addr/u c-addr )
241
242                         \ now we have the buffer c-addr on top
243                         \ ( followed by c-addr/u of current caption )
244
245                         \ Copy the current caption into our buffer
246                         2dup c! -rot \ store strlen at first byte
247                         begin
248                                 rot 1+    \ bring alt addr to top and increment
249                                 -rot -rot \ bring buffer addr to top
250                                 2dup c@ swap c! \ copy current character
251                                 1+     \ increment buffer addr
252                                 rot 1- \ bring buffer len to top and decrement
253                                 dup 0= \ exit loop if buffer len is zero
254                         until
255                         2drop \ buffer len/addr
256                         drop  \ alt addr
257
258                 else
259                         drop
260                 then
261         then
262
263         \ Now we are certain to have init_textN populated with the initial
264         \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
265         \ We can now use init_textN as the untoggled caption and
266         \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
267         \ toggled caption and store the appropriate value into menu_caption[x]
268         \ (again, ansi_caption[x] with loader_color enabled). Last, we'll
269         \ negate the toggled state so that we reverse the flow on subsequent
270         \ calls.
271
272         dup toggle_stateN @ 0= if
273                 \ state is OFF, toggle to ON
274
275                 dup ( n -- n n ) \ key pressed
276                 loader_color? if
277                         toggled_ansi[x]
278                 else
279                         toggled_text[x]
280                 then
281                 getenv dup -1 <> if
282                         \ Assign toggled text to menu caption
283                         2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
284                         loader_color? if
285                                 ansi_caption[x]
286                         else
287                                 menu_caption[x]
288                         then
289                         setenv
290                 else
291                         \ No toggled text, keep the same caption
292                         drop ( n -1 -- n ) \ getenv cruft
293                 then
294
295                 true \ new value of toggle state var (to be stored later)
296         else
297                 \ state is ON, toggle to OFF
298
299                 dup init_textN count ( n -- n c-addr/u )
300
301                 \ Assign init_textN text to menu caption
302                 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
303                 loader_color? if
304                         ansi_caption[x]
305                 else
306                         menu_caption[x]
307                 then
308                 setenv
309
310                 false \ new value of toggle state var (to be stored below)
311         then
312
313         \ now we'll store the new toggle state (on top of stack)
314         over toggle_stateN !
315 ;
316
317 : cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
318
319         \ ASCII numeral equal to user-selected menu item must be on the stack.
320         \ We do not modify the stack, so the ASCII numeral is left on top.
321
322         dup cycle_stateN dup @ 1+ \ get value and increment
323
324         \ Before assigning the (incremented) value back to the pointer,
325         \ let's test for the existence of this particular array element.
326         \ If the element exists, we'll store index value and move on.
327         \ Otherwise, we'll loop around to zero and store that.
328
329         dup 48 + ( n addr k -- n addr k k' )
330                  \ duplicate array index and convert to ASCII numeral
331
332         3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y)
333         loader_color? if
334                 ansi_caption[x][y]
335         else
336                 menu_caption[x][y]
337         then
338         ( n addr k n k' -- n addr k c-addr/u )
339
340         \ Now test for the existence of our incremented array index in the
341         \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
342         \ enabled) as set in loader.rc(5), et. al.
343
344         getenv dup -1 = if
345                 \ No caption set for this array index. Loop back to zero.
346
347                 drop ( n addr k -1 -- n addr k ) \ getenv cruft
348                 drop 0 ( n addr k -- n addr 0 )  \ new value to store later
349
350                 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y)
351                 loader_color? if
352                         ansi_caption[x][y]
353                 else
354                         menu_caption[x][y]
355                 then
356                 ( n addr 0 n 48 -- n addr 0 c-addr/u )
357                 getenv dup -1 = if
358                         \ This is highly unlikely to occur, but to make
359                         \ sure that things move along smoothly, allocate
360                         \ a temporary NULL string
361
362                         drop ( n addr 0 -1 -- n addr 0 ) \ getenv cruft
363                         s" " ( n addr 0 -- n addr 0 c-addr/u )
364                 then
365         then
366
367         \ At this point, we should have the following on the stack (in order,
368         \ from bottom to top):
369         \ 
370         \    n        - Ascii numeral representing the menu choice (inherited)
371         \    addr     - address of our internal cycle_stateN variable
372         \    k        - zero-based number we intend to store to the above
373         \    c-addr/u - string value we intend to store to menu_caption[x]
374         \               (or ansi_caption[x] with loader_color enabled)
375         \ 
376         \ Let's perform what we need to with the above.
377
378         \ Assign array value text to menu caption
379         4 pick ( n addr k c-addr/u -- n addr k c-addr/u n )
380         loader_color? if
381                 ansi_caption[x]
382         else
383                 menu_caption[x]
384         then
385         setenv
386
387         swap ! ( n addr k -- n ) \ update array state variable
388 ;
389
390 : acpipresent? ( -- flag ) \ Returns TRUE if ACPI is present, FALSE otherwise
391         s" hint.acpi.0.rsdp" getenv
392         dup -1 = if
393                 drop false exit
394         then
395         2drop
396         true
397 ;
398
399 : acpienabled? ( -- flag ) \ Returns TRUE if ACPI is enabled, FALSE otherwise
400         s" hint.acpi.0.disabled" getenv
401         dup -1 <> if
402                 s" 0" compare 0<> if
403                         false exit
404                 then
405         else
406                 drop
407         then
408         true
409 ;
410
411 \ This function prints the appropriate menuitem basename to the stack if an
412 \ ACPI option is to be presented to the user, otherwise returns -1. Used
413 \ internally by menu-create, you need not (nor should you) call this directly.
414
415 : acpimenuitem ( -- C-Addr/U | -1 )
416
417         arch-i386? if
418                 acpipresent? if
419                         acpienabled? if
420                                 loader_color? if
421                                         str_toggled_ansi[x]
422                                 else
423                                         str_toggled_text[x]
424                                 then
425                         else
426                                 loader_color? if
427                                         str_ansi_caption[x]
428                                 else
429                                         str_menu_caption[x]
430                                 then
431                         then
432                 else
433                         menuidx dup @ 1+ swap ! ( increment menuidx )
434                         -1
435                 then
436         else
437                 -1
438         then
439 ;
440
441 \ This function creates the list of menu items. This function is called by the
442 \ menu-display function. You need not be call it directly.
443
444 : menu-create ( -- )
445
446         \ Print the frame caption at (x,y)
447         str_loader_menu_title getenv dup -1 = if
448                 drop s" Welcome to FreeBSD"
449         then
450         TRUE ( use default alignment )
451         str_loader_menu_title_align getenv dup -1 <> if
452                 2dup s" left" compare-insensitive 0= if ( 1 )
453                         2drop ( c-addr/u ) drop ( bool )
454                         menuX @ menuY @ 1-
455                         FALSE ( don't use default alignment )
456                 else ( 1 ) 2dup s" right" compare-insensitive 0= if ( 2 )
457                         2drop ( c-addr/u ) drop ( bool )
458                         menuX @ 42 + 4 - over - menuY @ 1-
459                         FALSE ( don't use default alignment )
460                 else ( 2 ) 2drop ( c-addr/u ) then ( 1 ) then
461         else
462                 drop ( getenv cruft )
463         then
464         if ( use default center alignement? )
465                 menuX @ 19 + over 2 / - menuY @ 1-
466         then
467         at-xy type 
468
469         \ If $menu_init is set, evaluate it (allowing for whole menus to be
470         \ constructed dynamically -- as this function could conceivably set
471         \ the remaining environment variables to construct the menu entirely).
472         \ 
473         str_menu_init getenv dup -1 <> if
474                 evaluate
475         else
476                 drop
477         then
478
479         \ Print our menu options with respective key/variable associations.
480         \ `printmenuitem' ends by adding the decimal ASCII value for the
481         \ numerical prefix to the stack. We store the value left on the stack
482         \ to the key binding variable for later testing against a character
483         \ captured by the `getkey' function.
484
485         \ Note that any menu item beyond 9 will have a numerical prefix on the
486         \ screen consisting of the first digit (ie. 1 for the tenth menu item)
487         \ and the key required to activate that menu item will be the decimal
488         \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
489         \ which is misleading and not desirable.
490         \ 
491         \ Thus, we do not allow more than 8 configurable items on the menu
492         \ (with "Reboot" as the optional ninth and highest numbered item).
493
494         \ 
495         \ Initialize the ACPI option status.
496         \ 
497         0 menuacpi !
498         str_menu_acpi getenv -1 <> if
499                 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
500                         menuacpi !
501                         arch-i386? if acpipresent? if
502                                 \ 
503                                 \ Set menu toggle state to active state
504                                 \ (required by generic toggle_menuitem)
505                                 \ 
506                                 acpienabled? menuacpi @ toggle_stateN !
507                         then then
508                 else
509                         drop
510                 then
511         then
512
513         \ 
514         \ Initialize the menu_options visual separator.
515         \ 
516         0 menuoptions !
517         str_menu_options getenv -1 <> if
518                 c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
519                         menuoptions !
520                 else
521                         drop
522                 then
523         then
524
525         \ Initialize "Reboot" menu state variable (prevents double-entry)
526         false menurebootadded !
527
528         menu_start
529         1- menuidx !    \ Initialize the starting index for the menu
530         0 menurow !     \ Initialize the starting position for the menu
531
532         49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
533         begin
534                 \ If the "Options:" separator, print it.
535                 dup menuoptions @ = if
536                         \ Optionally add a reboot option to the menu
537                         str_menu_reboot getenv -1 <> if
538                                 drop
539                                 s" Reboot" printmenuitem menureboot !
540                                 true menurebootadded !
541                         then
542
543                         menuX @
544                         menurow @ 2 + menurow !
545                         menurow @ menuY @ +
546                         at-xy
547                         str_menu_optionstext getenv dup -1 <> if
548                                 type
549                         else
550                                 drop ." Options:"
551                         then
552                 then
553
554                 \ If this is the ACPI menu option, act accordingly.
555                 dup menuacpi @ = if
556                         dup acpimenuitem ( n -- n n c-addr/u | n n -1 )
557                         dup -1 <> if
558                                 13 +c! ( n n c-addr/u -- n c-addr/u )
559                                        \ replace 'x' with n
560                         else
561                                 swap drop ( n n -1 -- n -1 )
562                                 over menu_command[x] unsetenv
563                         then
564                 else
565                         \ make sure we have not already initialized this item
566                         dup init_stateN dup @ 0= if
567                                 1 swap !
568
569                                 \ If this menuitem has an initializer, run it
570                                 dup menu_init[x]
571                                 getenv dup -1 <> if
572                                         evaluate
573                                 else
574                                         drop
575                                 then
576                         else
577                                 drop
578                         then
579
580                         dup
581                         loader_color? if
582                                 ansi_caption[x]
583                         else
584                                 menu_caption[x]
585                         then
586                 then
587
588                 dup -1 <> if
589                         \ test for environment variable
590                         getenv dup -1 <> if
591                                 printmenuitem ( c-addr/u -- n )
592                                 dup menukeyN !
593                         else
594                                 drop
595                         then
596                 else
597                         drop
598                 then
599
600                 1+ dup 56 > \ add 1 to iterator, continue if less than 57
601         until
602         drop \ iterator
603
604         \ Optionally add a reboot option to the menu
605         menurebootadded @ true <> if
606                 str_menu_reboot getenv -1 <> if
607                         drop       \ no need for the value
608                         s" Reboot" \ menu caption (required by printmenuitem)
609
610                         printmenuitem
611                         menureboot !
612                 else
613                         0 menureboot !
614                 then
615         then
616 ;
617
618 \ Takes a single integer on the stack and updates the timeout display. The
619 \ integer must be between 0 and 9 (we will only update a single digit in the
620 \ source message).
621
622 : menu-timeout-update ( N -- )
623
624         \ Enforce minimum/maximum
625         dup 9 > if drop 9 then
626         dup 0 < if drop 0 then
627
628         s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
629
630         2 pick 0> if
631                 rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII
632                 12 +c!        ( n' c-addr/u -- c-addr/u )   \ replace 'N' above
633
634                 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
635                 type ( c-addr/u -- ) \ print message
636         else
637                 menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
638                 spaces ( n c-addr/u -- n c-addr ) \ erase message
639                 2drop ( n c-addr -- )
640         then
641
642         0 25 at-xy ( position cursor back at bottom-left )
643 ;
644
645 \ This function blocks program flow (loops forever) until a key is pressed.
646 \ The key that was pressed is added to the top of the stack in the form of its
647 \ decimal ASCII representation. This function is called by the menu-display
648 \ function. You need not call it directly.
649
650 : getkey ( -- ascii_keycode )
651
652         begin \ loop forever
653
654                 menu_timeout_enabled @ 1 = if
655                         ( -- )
656                         seconds ( get current time: -- N )
657                         dup menu_time @ <> if ( has time elapsed?: N N N -- N )
658
659                                 \ At least 1 second has elapsed since last loop
660                                 \ so we will decrement our "timeout" (really a
661                                 \ counter, insuring that we do not proceed too
662                                 \ fast) and update our timeout display.
663
664                                 menu_time ! ( update time record: N -- )
665                                 menu_timeout @ ( "time" remaining: -- N )
666                                 dup 0> if ( greater than 0?: N N 0 -- N )
667                                         1- ( decrement counter: N -- N )
668                                         dup menu_timeout !
669                                                 ( re-assign: N N Addr -- N )
670                                 then
671                                 ( -- N )
672
673                                 dup 0= swap 0< or if ( N <= 0?: N N -- )
674                                         \ halt the timer
675                                         0 menu_timeout ! ( 0 Addr -- )
676                                         0 menu_timeout_enabled ! ( 0 Addr -- )
677                                 then
678
679                                 \ update the timer display ( N -- )
680                                 menu_timeout @ menu-timeout-update
681
682                                 menu_timeout @ 0= if
683                                         \ We've reached the end of the timeout
684                                         \ (user did not cancel by pressing ANY
685                                         \ key)
686
687                                         str_menu_timeout_command getenv dup
688                                         -1 = if
689                                                 drop \ clean-up
690                                         else
691                                                 evaluate
692                                         then
693                                 then
694
695                         else ( -- N )
696                                 \ No [detectable] time has elapsed (in seconds)
697                                 drop ( N -- )
698                         then
699                         ( -- )
700                 then
701
702                 key? if \ Was a key pressed? (see loader(8))
703
704                         \ An actual key was pressed (if the timeout is running,
705                         \ kill it regardless of which key was pressed)
706                         menu_timeout @ 0<> if
707                                 0 menu_timeout !
708                                 0 menu_timeout_enabled !
709
710                                 \ clear screen of timeout message
711                                 0 menu-timeout-update
712                         then
713
714                         \ get the key that was pressed and exit (if we
715                         \ get a non-zero ASCII code)
716                         key dup 0<> if
717                                 exit
718                         else
719                                 drop
720                         then
721                 then
722                 50 ms \ sleep for 50 milliseconds (see loader(8))
723
724         again
725 ;
726
727 : menu-erase ( -- ) \ Erases menu and resets positioning variable to positon 1.
728
729         \ Clear the screen area associated with the interactive menu
730         menuX @ menuY @
731         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces 1+
732         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces 1+
733         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces 1+
734         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces 1+
735         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces 1+
736         2dup at-xy 38 spaces 1+         2dup at-xy 38 spaces
737         2drop
738
739         \ Reset the starting index and position for the menu
740         menu_start 1- menuidx !
741         0 menurow !
742 ;
743
744 \ Erase and redraw the menu. Useful if you change a caption and want to
745 \ update the menu to reflect the new value.
746
747 : menu-redraw ( -- )
748         menu-erase
749         menu-create
750 ;
751
752 \ This function initializes the menu. Call this from your `loader.rc' file
753 \ before calling any other menu-related functions.
754
755 : menu-init ( -- )
756         menu_start
757         1- menuidx !    \ Initialize the starting index for the menu
758         0 menurow !     \ Initialize the starting position for the menu
759
760         \ Assign configuration values
761         str_loader_menu_y getenv dup -1 = if
762                 drop \ no custom row position
763                 menu_default_y
764         else
765                 \ make sure custom position is a number
766                 ?number 0= if
767                         menu_default_y \ or use default
768                 then
769         then
770         menuY !
771         str_loader_menu_x getenv dup -1 = if
772                 drop \ no custom column position
773                 menu_default_x
774         else
775                 \ make sure custom position is a number
776                 ?number 0= if
777                         menu_default_x \ or use default
778                 then
779         then
780         menuX !
781
782         \ Interpret a custom frame type for the menu
783         TRUE ( draw a box? default yes, but might be altered below )
784         str_loader_menu_frame getenv dup -1 = if ( 1 )
785                 drop \ no custom frame type
786         else ( 1 )  2dup s" single" compare-insensitive 0= if ( 2 )
787                 f_single ( see frames.4th )
788         else ( 2 )  2dup s" double" compare-insensitive 0= if ( 3 )
789                 f_double ( see frames.4th )
790         else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
791                 drop FALSE \ don't draw a box
792         ( 4 ) then ( 3 ) then ( 2 )  then ( 1 ) then
793         if
794                 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
795         then
796
797         0 25 at-xy \ Move cursor to the bottom for output
798 ;
799
800 \ Main function. Call this from your `loader.rc' file.
801
802 : menu-display ( -- )
803
804         0 menu_timeout_enabled ! \ start with automatic timeout disabled
805
806         \ check indication that automatic execution after delay is requested
807         str_menu_timeout_command getenv -1 <> if ( Addr C -1 -- | Addr )
808                 drop ( just testing existence right now: Addr -- )
809
810                 \ initialize state variables
811                 seconds menu_time ! ( store the time we started )
812                 1 menu_timeout_enabled ! ( enable automatic timeout )
813
814                 \ read custom time-duration (if set)
815                 s" autoboot_delay" getenv dup -1 = if
816                         drop \ no custom duration (remove dup'd bunk -1)
817                         menu_timeout_default \ use default setting
818                 else
819                         2dup ?number 0= if ( if not a number )
820                                 \ disable timeout if "NO", else use default
821                                 s" NO" compare-insensitive 0= if
822                                         0 menu_timeout_enabled !
823                                         0 ( assigned to menu_timeout below )
824                                 else
825                                         menu_timeout_default
826                                 then
827                         else
828                                 -rot 2drop
829
830                                 \ boot immediately if less than zero
831                                 dup 0< if
832                                         drop
833                                         menu-create
834                                         0 25 at-xy
835                                         0 boot
836                                 then
837                         then
838                 then
839                 menu_timeout ! ( store value on stack from above )
840
841                 menu_timeout_enabled @ 1 = if
842                         \ read custom column position (if set)
843                         str_loader_menu_timeout_x getenv dup -1 = if
844                                 drop \ no custom column position
845                                 menu_timeout_default_x \ use default setting
846                         else
847                                 \ make sure custom position is a number
848                                 ?number 0= if
849                                         menu_timeout_default_x \ or use default
850                                 then
851                         then
852                         menu_timeout_x ! ( store value on stack from above )
853         
854                         \ read custom row position (if set)
855                         str_loader_menu_timeout_y getenv dup -1 = if
856                                 drop \ no custom row position
857                                 menu_timeout_default_y \ use default setting
858                         else
859                                 \ make sure custom position is a number
860                                 ?number 0= if
861                                         menu_timeout_default_y \ or use default
862                                 then
863                         then
864                         menu_timeout_y ! ( store value on stack from above )
865                 then
866         then
867
868         menu-create
869
870         begin \ Loop forever
871
872                 0 25 at-xy \ Move cursor to the bottom for output
873                 getkey     \ Block here, waiting for a key to be pressed
874
875                 dup -1 = if
876                         drop exit \ Caught abort (abnormal return)
877                 then
878
879                 \ Boot if the user pressed Enter/Ctrl-M (13) or
880                 \ Ctrl-Enter/Ctrl-J (10)
881                 dup over 13 = swap 10 = or if
882                         drop ( no longer needed )
883                         s" boot" evaluate
884                         exit ( pedantic; never reached )
885                 then
886
887                 dup menureboot @ = if 0 reboot then
888
889                 \ Evaluate the decimal ASCII value against known menu item
890                 \ key associations and act accordingly
891
892                 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
893                 begin
894                         dup menukeyN @
895                         rot tuck = if
896
897                                 \ Adjust for missing ACPI menuitem on non-i386
898                                 arch-i386? true <> menuacpi @ 0<> and if
899                                         menuacpi @ over 2dup < -rot = or
900                                         over 58 < and if
901                                         ( key >= menuacpi && key < 58: N -- N )
902                                                 1+
903                                         then
904                                 then
905
906                                 \ Test for the environment variable
907                                 dup menu_command[x]
908                                 getenv dup -1 <> if
909                                         \ Execute the stored procedure
910                                         evaluate
911
912                                         \ We expect there to be a non-zero
913                                         \  value left on the stack after
914                                         \ executing the stored procedure.
915                                         \ If so, continue to run, else exit.
916
917                                         0= if
918                                                 drop \ key pressed
919                                                 drop \ loop iterator
920                                                 exit
921                                         else
922                                                 swap \ need iterator on top
923                                         then
924                                 then
925
926                                 \ Re-adjust for missing ACPI menuitem
927                                 arch-i386? true <> menuacpi @ 0<> and if
928                                         swap
929                                         menuacpi @ 1+ over 2dup < -rot = or
930                                         over 59 < and if
931                                                 1-
932                                         then
933                                         swap
934                                 then
935                         else
936                                 swap \ need iterator on top
937                         then
938
939                         \ 
940                         \ Check for menu keycode shortcut(s)
941                         \ 
942                         dup menu_keycode[x]
943                         getenv dup -1 = if
944                                 drop
945                         else
946                                 ?number 0<> if
947                                         rot tuck = if
948                                                 swap
949                                                 dup menu_command[x]
950                                                 getenv dup -1 <> if
951                                                         evaluate
952                                                         0= if
953                                                                 2drop
954                                                                 exit
955                                                         then
956                                                 else
957                                                         drop
958                                                 then
959                                         else
960                                                 swap
961                                         then
962                                 then
963                         then
964
965                         1+ dup 56 > \ increment iterator
966                                     \ continue if less than 57
967                 until
968                 drop \ loop iterator
969                 drop \ key pressed
970
971         again   \ Non-operational key was pressed; repeat
972 ;
973
974 \ This function unsets all the possible environment variables associated with
975 \ creating the interactive menu.
976
977 : menu-unset ( -- )
978
979         49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
980         begin
981                 dup menu_init[x]    unsetenv    \ menu initializer
982                 dup menu_command[x] unsetenv    \ menu command
983                 dup menu_caption[x] unsetenv    \ menu caption
984                 dup ansi_caption[x] unsetenv    \ ANSI caption
985                 dup menu_keycode[x] unsetenv    \ menu keycode
986                 dup toggled_text[x] unsetenv    \ toggle_menuitem caption
987                 dup toggled_ansi[x] unsetenv    \ toggle_menuitem ANSI caption
988
989                 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9')
990                 begin
991                         \ cycle_menuitem caption and ANSI caption
992                         2dup menu_caption[x][y] unsetenv
993                         2dup ansi_caption[x][y] unsetenv
994                         1+ dup 57 >
995                 until
996                 drop \ inner iterator
997
998                 0 over menukeyN      !  \ used by menu-create, menu-display
999                 0 over init_stateN   !  \ used by menu-create
1000                 0 over toggle_stateN !  \ used by toggle_menuitem
1001                 0 over init_textN   c!  \ used by toggle_menuitem
1002                 0 over cycle_stateN  !  \ used by cycle_menuitem
1003
1004                 1+ dup 56 >     \ increment, continue if less than 57
1005         until
1006         drop \ iterator
1007
1008         str_menu_timeout_command unsetenv       \ menu timeout command
1009         str_menu_reboot          unsetenv       \ Reboot menu option flag
1010         str_menu_acpi            unsetenv       \ ACPI menu option flag
1011         str_menu_options         unsetenv       \ Options separator flag
1012         str_menu_optionstext     unsetenv       \ separator display text
1013         str_menu_init            unsetenv       \ menu initializer
1014
1015         0 menureboot !
1016         0 menuacpi !
1017         0 menuoptions !
1018 ;
1019
1020 \ This function both unsets menu variables and visually erases the menu area
1021 \ in-preparation for another menu.
1022
1023 : menu-clear ( -- )
1024         menu-unset
1025         menu-erase
1026 ;
1027
1028 bullet menubllt !
1029
1030 \ Initialize our menu initialization state variables
1031 0 init_state1 !
1032 0 init_state2 !
1033 0 init_state3 !
1034 0 init_state4 !
1035 0 init_state5 !
1036 0 init_state6 !
1037 0 init_state7 !
1038 0 init_state8 !
1039
1040 \ Initialize our boolean state variables
1041 0 toggle_state1 !
1042 0 toggle_state2 !
1043 0 toggle_state3 !
1044 0 toggle_state4 !
1045 0 toggle_state5 !
1046 0 toggle_state6 !
1047 0 toggle_state7 !
1048 0 toggle_state8 !
1049
1050 \ Initialize our array state variables
1051 0 cycle_state1 !
1052 0 cycle_state2 !
1053 0 cycle_state3 !
1054 0 cycle_state4 !
1055 0 cycle_state5 !
1056 0 cycle_state6 !
1057 0 cycle_state7 !
1058 0 cycle_state8 !
1059
1060 \ Initialize string containers
1061 0 init_text1 c!
1062 0 init_text2 c!
1063 0 init_text3 c!
1064 0 init_text4 c!
1065 0 init_text5 c!
1066 0 init_text6 c!
1067 0 init_text7 c!
1068 0 init_text8 c!