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