PageRenderTime 65ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/stand/forth/menu.4th

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