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