PageRenderTime 68ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/stand/forth/menu.4th

https://github.com/freebsd/freebsd
Forth | 1328 lines | 1127 code | 190 blank | 11 comment | 87 complexity | b87d07ee4729baa3d4d8abb983da09c9 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. swap 1- swap
  427. at-xy dup 0= if
  428. 2drop ( empty loader_menu_title )
  429. else
  430. space type space
  431. then
  432. \ If $menu_init is set, evaluate it (allowing for whole menus to be
  433. \ constructed dynamically -- as this function could conceivably set
  434. \ the remaining environment variables to construct the menu entirely).
  435. \
  436. s" menu_init" getenv dup -1 <> if
  437. evaluate
  438. else
  439. drop
  440. then
  441. \ Print our menu options with respective key/variable associations.
  442. \ `printmenuitem' ends by adding the decimal ASCII value for the
  443. \ numerical prefix to the stack. We store the value left on the stack
  444. \ to the key binding variable for later testing against a character
  445. \ captured by the `getkey' function.
  446. \ Note that any menu item beyond 9 will have a numerical prefix on the
  447. \ screen consisting of the first digit (ie. 1 for the tenth menu item)
  448. \ and the key required to activate that menu item will be the decimal
  449. \ ASCII of 48 plus the menu item (ie. 58 for the tenth item, aka. `:')
  450. \ which is misleading and not desirable.
  451. \
  452. \ Thus, we do not allow more than 8 configurable items on the menu
  453. \ (with "Reboot" as the optional ninth and highest numbered item).
  454. \
  455. \ Initialize the ACPI option status.
  456. \
  457. 0 menuacpi !
  458. s" menu_acpi" getenv -1 <> if
  459. c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
  460. menuacpi !
  461. arch-i386? if acpipresent? if
  462. \
  463. \ Set menu toggle state to active state
  464. \ (required by generic toggle_menuitem)
  465. \
  466. acpienabled? menuacpi @ toggle_stateN !
  467. then then
  468. else
  469. drop
  470. then
  471. then
  472. \
  473. \ Initialize kernel captions after parsing $kernels
  474. \
  475. 0 menukernel !
  476. s" menu_kernel" getenv -1 <> if
  477. c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
  478. dup menukernel !
  479. dup parse-kernels tag-kernels
  480. \ Get the current cycle state (entry to use)
  481. s" kernel_state" evaluate @ 48 + ( n -- n y )
  482. \ If state is invalid, reset
  483. dup kernmenuidx @ 1- > if
  484. drop [char] 0 ( n y -- n 48 )
  485. 0 s" kernel_state" evaluate !
  486. over s" init_kernel" evaluate drop
  487. then
  488. \ Set the current non-ANSI caption
  489. 2dup swap dup ( n y -- n y y n n )
  490. s" set menu_caption[x]=$menu_caption[x][y]"
  491. 17 +c! 34 +c! 37 +c! evaluate
  492. ( n y y n n c-addr/u -- n y )
  493. \ Set the current ANSI caption
  494. 2dup swap dup ( n y -- n y y n n )
  495. s" set ansi_caption[x]=$ansi_caption[x][y]"
  496. 17 +c! 34 +c! 37 +c! evaluate
  497. ( n y y n n c-addr/u -- n y )
  498. \ Initialize cycle state from stored value
  499. 48 - ( n y -- n k )
  500. s" init_cyclestate" evaluate ( n k -- n )
  501. \ Set $kernel to $kernel[y]
  502. s" activate_kernel" evaluate ( n -- n )
  503. then
  504. drop
  505. then
  506. \
  507. \ Initialize the menu_options visual separator.
  508. \
  509. 0 menuoptions !
  510. s" menu_options" getenv -1 <> if
  511. c@ dup 48 > over 57 < and if ( '1' <= c1 <= '8' )
  512. menuoptions !
  513. else
  514. drop
  515. then
  516. then
  517. \ Initialize "Reboot" menu state variable (prevents double-entry)
  518. false menurebootadded !
  519. menu_start
  520. 1- menuidx ! \ Initialize the starting index for the menu
  521. 0 menurow ! \ Initialize the starting position for the menu
  522. 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
  523. begin
  524. \ If the "Options:" separator, print it.
  525. dup menuoptions @ = if
  526. \ Optionally add a reboot option to the menu
  527. s" menu_reboot" getenv -1 <> if
  528. drop
  529. s" Reboot" printmenuitem menureboot !
  530. true menurebootadded !
  531. then
  532. menuX @
  533. menurow @ 2 + menurow !
  534. menurow @ menuY @ +
  535. at-xy
  536. s" menu_optionstext" getenv dup -1 <> if
  537. type
  538. else
  539. drop ." Options:"
  540. then
  541. then
  542. \ If this is the ACPI menu option, act accordingly.
  543. dup menuacpi @ = if
  544. dup acpimenuitem ( n -- n n c-addr/u | n n -1 )
  545. dup -1 <> if
  546. 13 +c! ( n n c-addr/u -- n c-addr/u )
  547. \ replace 'x' with n
  548. else
  549. swap drop ( n n -1 -- n -1 )
  550. over menu_command[x] unsetenv
  551. then
  552. else
  553. \ make sure we have not already initialized this item
  554. dup init_stateN dup @ 0= if
  555. 1 swap !
  556. \ If this menuitem has an initializer, run it
  557. dup menu_init[x]
  558. getenv dup -1 <> if
  559. evaluate
  560. else
  561. drop
  562. then
  563. else
  564. drop
  565. then
  566. dup
  567. loader_color? if
  568. ansi_caption[x]
  569. else
  570. menu_caption[x]
  571. then
  572. then
  573. dup -1 <> if
  574. \ test for environment variable
  575. getenv dup -1 <> if
  576. printmenuitem ( c-addr/u -- n )
  577. dup menukeyN !
  578. else
  579. drop
  580. then
  581. else
  582. drop
  583. then
  584. 1+ dup 56 > \ add 1 to iterator, continue if less than 57
  585. until
  586. drop \ iterator
  587. \ Optionally add a reboot option to the menu
  588. menurebootadded @ true <> if
  589. s" menu_reboot" getenv -1 <> if
  590. drop \ no need for the value
  591. s" Reboot" \ menu caption (required by printmenuitem)
  592. printmenuitem
  593. menureboot !
  594. else
  595. 0 menureboot !
  596. then
  597. then
  598. ;
  599. \ Takes a single integer on the stack and updates the timeout display. The
  600. \ integer must be between 0 and 9 (we will only update a single digit in the
  601. \ source message).
  602. \
  603. : menu-timeout-update ( N -- )
  604. \ Enforce minimum/maximum
  605. dup 9 > if drop 9 then
  606. dup 0 < if drop 0 then
  607. s" Autoboot in N seconds. [Space] to pause" ( n -- n c-addr/u )
  608. 2 pick 0> if
  609. rot 48 + -rot ( n c-addr/u -- n' c-addr/u ) \ convert to ASCII
  610. 12 +c! ( n' c-addr/u -- c-addr/u ) \ replace 'N' above
  611. menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
  612. type ( c-addr/u -- ) \ print message
  613. else
  614. menu_timeout_x @ menu_timeout_y @ at-xy \ position cursor
  615. spaces ( n c-addr/u -- n c-addr ) \ erase message
  616. 2drop ( n c-addr -- )
  617. then
  618. 0 25 at-xy ( position cursor back at bottom-left )
  619. ;
  620. \ This function blocks program flow (loops forever) until a key is pressed.
  621. \ The key that was pressed is added to the top of the stack in the form of its
  622. \ decimal ASCII representation. This function is called by the menu-display
  623. \ function. You need not call it directly.
  624. \
  625. : getkey ( -- ascii_keycode )
  626. begin \ loop forever
  627. menu_timeout_enabled @ 1 = if
  628. ( -- )
  629. seconds ( get current time: -- N )
  630. dup menu_time @ <> if ( has time elapsed?: N N N -- N )
  631. \ At least 1 second has elapsed since last loop
  632. \ so we will decrement our "timeout" (really a
  633. \ counter, insuring that we do not proceed too
  634. \ fast) and update our timeout display.
  635. menu_time ! ( update time record: N -- )
  636. menu_timeout @ ( "time" remaining: -- N )
  637. dup 0> if ( greater than 0?: N N 0 -- N )
  638. 1- ( decrement counter: N -- N )
  639. dup menu_timeout !
  640. ( re-assign: N N Addr -- N )
  641. then
  642. ( -- N )
  643. dup 0= swap 0< or if ( N <= 0?: N N -- )
  644. \ halt the timer
  645. 0 menu_timeout ! ( 0 Addr -- )
  646. 0 menu_timeout_enabled ! ( 0 Addr -- )
  647. then
  648. \ update the timer display ( N -- )
  649. menu_timeout @ menu-timeout-update
  650. menu_timeout @ 0= if
  651. \ We've reached the end of the timeout
  652. \ (user did not cancel by pressing ANY
  653. \ key)
  654. s" menu_timeout_command" getenv dup
  655. -1 = if
  656. drop \ clean-up
  657. else
  658. evaluate
  659. then
  660. then
  661. else ( -- N )
  662. \ No [detectable] time has elapsed (in seconds)
  663. drop ( N -- )
  664. then
  665. ( -- )
  666. then
  667. key? if \ Was a key pressed? (see loader(8))
  668. \ An actual key was pressed (if the timeout is running,
  669. \ kill it regardless of which key was pressed)
  670. menu_timeout @ 0<> if
  671. 0 menu_timeout !
  672. 0 menu_timeout_enabled !
  673. \ clear screen of timeout message
  674. 0 menu-timeout-update
  675. then
  676. \ get the key that was pressed and exit (if we
  677. \ get a non-zero ASCII code)
  678. key dup 0<> if
  679. exit
  680. else
  681. drop
  682. then
  683. then
  684. 50 ms \ sleep for 50 milliseconds (see loader(8))
  685. again
  686. ;
  687. : menu-erase ( -- ) \ Erases menu and resets positioning variable to position 1.
  688. \ Clear the screen area associated with the interactive menu
  689. menuX @ menuY @
  690. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
  691. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
  692. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
  693. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
  694. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces 1+
  695. 2dup at-xy 38 spaces 1+ 2dup at-xy 38 spaces
  696. 2drop
  697. \ Reset the starting index and position for the menu
  698. menu_start 1- menuidx !
  699. 0 menurow !
  700. ;
  701. only forth
  702. also menu-infrastructure
  703. also menu-namespace
  704. also menu-command-helpers definitions
  705. : toggle_menuitem ( N -- N ) \ toggles caption text and internal menuitem state
  706. \ ASCII numeral equal to user-selected menu item must be on the stack.
  707. \ We do not modify the stack, so the ASCII numeral is left on top.
  708. dup init_textN c@ 0= if
  709. \ NOTE: no need to check toggle_stateN since the first time we
  710. \ are called, we will populate init_textN. Further, we don't
  711. \ need to test whether menu_caption[x] (ansi_caption[x] when
  712. \ loader_color?=1) is available since we would not have been
  713. \ called if the caption was NULL.
  714. \ base name of environment variable
  715. dup ( n -- n n ) \ key pressed
  716. loader_color? if
  717. ansi_caption[x]
  718. else
  719. menu_caption[x]
  720. then
  721. getenv dup -1 <> if
  722. 2 pick ( n c-addr/u -- n c-addr/u n )
  723. init_textN ( n c-addr/u n -- n c-addr/u c-addr )
  724. \ now we have the buffer c-addr on top
  725. \ ( followed by c-addr/u of current caption )
  726. \ Copy the current caption into our buffer
  727. 2dup c! -rot \ store strlen at first byte
  728. begin
  729. rot 1+ \ bring alt addr to top and increment
  730. -rot -rot \ bring buffer addr to top
  731. 2dup c@ swap c! \ copy current character
  732. 1+ \ increment buffer addr
  733. rot 1- \ bring buffer len to top and decrement
  734. dup 0= \ exit loop if buffer len is zero
  735. until
  736. 2drop \ buffer len/addr
  737. drop \ alt addr
  738. else
  739. drop
  740. then
  741. then
  742. \ Now we are certain to have init_textN populated with the initial
  743. \ value of menu_caption[x] (ansi_caption[x] with loader_color enabled).
  744. \ We can now use init_textN as the untoggled caption and
  745. \ toggled_text[x] (toggled_ansi[x] with loader_color enabled) as the
  746. \ toggled caption and store the appropriate value into menu_caption[x]
  747. \ (again, ansi_caption[x] with loader_color enabled). Last, we'll
  748. \ negate the toggled state so that we reverse the flow on subsequent
  749. \ calls.
  750. dup toggle_stateN @ 0= if
  751. \ state is OFF, toggle to ON
  752. dup ( n -- n n ) \ key pressed
  753. loader_color? if
  754. toggled_ansi[x]
  755. else
  756. toggled_text[x]
  757. then
  758. getenv dup -1 <> if
  759. \ Assign toggled text to menu caption
  760. 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
  761. loader_color? if
  762. ansi_caption[x]
  763. else
  764. menu_caption[x]
  765. then
  766. setenv
  767. else
  768. \ No toggled text, keep the same caption
  769. drop ( n -1 -- n ) \ getenv cruft
  770. then
  771. true \ new value of toggle state var (to be stored later)
  772. else
  773. \ state is ON, toggle to OFF
  774. dup init_textN count ( n -- n c-addr/u )
  775. \ Assign init_textN text to menu caption
  776. 2 pick ( n c-addr/u -- n c-addr/u n ) \ key pressed
  777. loader_color? if
  778. ansi_caption[x]
  779. else
  780. menu_caption[x]
  781. then
  782. setenv
  783. false \ new value of toggle state var (to be stored below)
  784. then
  785. \ now we'll store the new toggle state (on top of stack)
  786. over toggle_stateN !
  787. ;
  788. : cycle_menuitem ( N -- N ) \ cycles through array of choices for a menuitem
  789. \ ASCII numeral equal to user-selected menu item must be on the stack.
  790. \ We do not modify the stack, so the ASCII numeral is left on top.
  791. dup cycle_stateN dup @ 1+ \ get value and increment
  792. \ Before assigning the (incremented) value back to the pointer,
  793. \ let's test for the existence of this particular array element.
  794. \ If the element exists, we'll store index value and move on.
  795. \ Otherwise, we'll loop around to zero and store that.
  796. dup 48 + ( n addr k -- n addr k k' )
  797. \ duplicate array index and convert to ASCII numeral
  798. 3 pick swap ( n addr k k' -- n addr k n k' ) \ (n,k') as (x,y)
  799. loader_color? if
  800. ansi_caption[x][y]
  801. else
  802. menu_caption[x][y]
  803. then
  804. ( n addr k n k' -- n addr k c-addr/u )
  805. \ Now test for the existence of our incremented array index in the
  806. \ form of $menu_caption[x][y] ($ansi_caption[x][y] with loader_color
  807. \ enabled) as set in loader.rc(5), et. al.
  808. getenv dup -1 = if
  809. \ No caption set for this array index. Loop back to zero.
  810. drop ( n addr k -1 -- n addr k ) \ getenv cruft
  811. drop 0 ( n addr k -- n addr 0 ) \ new value to store later
  812. 2 pick [char] 0 ( n addr 0 -- n addr 0 n 48 ) \ (n,48) as (x,y)
  813. loader_color? if
  814. ansi_caption[x][y]
  815. else
  816. menu_caption[x][y]
  817. then
  818. ( n addr 0 n 48 -- n addr 0 c-addr/u )
  819. getenv dup -1 = if
  820. \ Highly unlikely to occur, but to ensure things move
  821. \ along smoothly, allocate a temporary NULL string
  822. drop ( cruft ) s" "
  823. then
  824. then
  825. \ At this point, we should have the following on the stack (in order,
  826. \ from bottom to top):
  827. \
  828. \ n - Ascii numeral representing the menu choice (inherited)
  829. \ addr - address of our internal cycle_stateN variable
  830. \ k - zero-based number we intend to store to the above
  831. \ c-addr/u - string value we intend to store to menu_caption[x]
  832. \ (or ansi_caption[x] with loader_color enabled)
  833. \
  834. \ Let's perform what we need to with the above.
  835. \ Assign array value text to menu caption
  836. 4 pick ( n addr k c-addr/u -- n addr k c-addr/u n )
  837. loader_color? if
  838. ansi_caption[x]
  839. else
  840. menu_caption[x]
  841. then
  842. setenv
  843. swap ! ( n addr k -- n ) \ update array state variable
  844. ;
  845. only forth definitions also menu-infrastructure
  846. \ Erase and redraw the menu. Useful if you change a caption and want to
  847. \ update the menu to reflect the new value.
  848. \
  849. : menu-redraw ( -- )
  850. menu-erase
  851. menu-create
  852. ;
  853. : menu-box
  854. f_double ( default frame type )
  855. \ Interpret a custom frame type for the menu
  856. TRUE ( draw a box? default yes, but might be altered below )
  857. s" loader_menu_frame" getenv dup -1 = if ( 1 )
  858. drop \ no custom frame type
  859. else ( 1 ) 2dup s" single" compare-insensitive 0= if ( 2 )
  860. f_single ( see frames.4th )
  861. else ( 2 ) 2dup s" double" compare-insensitive 0= if ( 3 )
  862. f_double ( see frames.4th )
  863. else ( 3 ) s" none" compare-insensitive 0= if ( 4 )
  864. drop FALSE \ don't draw a box
  865. ( 4 ) then ( 3 ) then ( 2 ) then ( 1 ) then
  866. if
  867. 42 13 menuX @ 3 - menuY @ 1- box \ Draw frame (w,h,x,y)
  868. then
  869. ;
  870. \ This function initializes the menu. Call this from your `loader.rc' file
  871. \ before calling any other menu-related functions.
  872. \
  873. : menu-init ( -- )
  874. menu_start
  875. 1- menuidx ! \ Initialize the starting index for the menu
  876. 0 menurow ! \ Initialize the starting position for the menu
  877. \ Assign configuration values
  878. s" loader_menu_y" getenv dup -1 = if
  879. drop \ no custom row position
  880. menu_default_y
  881. else
  882. \ make sure custom position is a number
  883. ?number 0= if
  884. menu_default_y \ or use default
  885. then
  886. then
  887. menuY !
  888. s" loader_menu_x" getenv dup -1 = if
  889. drop \ no custom column position
  890. menu_default_x
  891. else
  892. \ make sure custom position is a number
  893. ?number 0= if
  894. menu_default_x \ or use default
  895. then
  896. then
  897. menuX !
  898. ['] menu-box console-iterate
  899. 0 25 at-xy \ Move cursor to the bottom for output
  900. ;
  901. also menu-namespace
  902. \ Main function. Call this from your `loader.rc' file.
  903. \
  904. : menu-display ( -- )
  905. 0 menu_timeout_enabled ! \ start with automatic timeout disabled
  906. \ check indication that automatic execution after delay is requested
  907. s" menu_timeout_command" getenv -1 <> if ( Addr C -1 -- | Addr )
  908. drop ( just testing existence right now: Addr -- )
  909. \ initialize state variables
  910. seconds menu_time ! ( store the time we started )
  911. 1 menu_timeout_enabled ! ( enable automatic timeout )
  912. \ read custom time-duration (if set)
  913. s" autoboot_delay" getenv dup -1 = if
  914. drop \ no custom duration (remove dup'd bunk -1)
  915. menu_timeout_default \ use default setting
  916. else
  917. 2dup ?number 0= if ( if not a number )
  918. \ disable timeout if "NO", else use default
  919. s" NO" compare-insensitive 0= if
  920. 0 menu_timeout_enabled !
  921. 0 ( assigned to menu_timeout below )
  922. else
  923. menu_timeout_default
  924. then
  925. else
  926. -rot 2drop
  927. \ boot immediately if less than zero
  928. dup 0< if
  929. drop
  930. menu-create
  931. 0 25 at-xy
  932. 0 boot
  933. then
  934. then
  935. then
  936. menu_timeout ! ( store value on stack from above )
  937. menu_timeout_enabled @ 1 = if
  938. \ read custom column position (if set)
  939. s" loader_menu_timeout_x" getenv dup -1 = if
  940. drop \ no custom column position
  941. menu_timeout_default_x \ use default setting
  942. else
  943. \ make sure custom position is a number
  944. ?number 0= if
  945. menu_timeout_default_x \ or use default
  946. then
  947. then
  948. menu_timeout_x ! ( store value on stack from above )
  949. \ read custom row position (if set)
  950. s" loader_menu_timeout_y" getenv dup -1 = if
  951. drop \ no custom row position
  952. menu_timeout_default_y \ use default setting
  953. else
  954. \ make sure custom position is a number
  955. ?number 0= if
  956. menu_timeout_default_y \ or use default
  957. then
  958. then
  959. menu_timeout_y ! ( store value on stack from above )
  960. then
  961. then
  962. menu-create
  963. begin \ Loop forever
  964. 0 25 at-xy \ Move cursor to the bottom for output
  965. getkey \ Block here, waiting for a key to be pressed
  966. dup -1 = if
  967. drop exit \ Caught abort (abnormal return)
  968. then
  969. \ Boot if the user pressed Enter/Ctrl-M (13) or
  970. \ Ctrl-Enter/Ctrl-J (10)
  971. dup over 13 = swap 10 = or if
  972. drop ( no longer needed )
  973. s" boot" evaluate
  974. exit ( pedantic; never reached )
  975. then
  976. dup menureboot @ = if 0 reboot then
  977. \ Evaluate the decimal ASCII value against known menu item
  978. \ key associations and act accordingly
  979. 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
  980. begin
  981. dup menukeyN @
  982. rot tuck = if
  983. \ Adjust for missing ACPI menuitem on non-i386
  984. arch-i386? true <> menuacpi @ 0<> and if
  985. menuacpi @ over 2dup < -rot = or
  986. over 58 < and if
  987. ( key >= menuacpi && key < 58: N -- N )
  988. 1+
  989. then
  990. then
  991. \ Test for the environment variable
  992. dup menu_command[x]
  993. getenv dup -1 <> if
  994. \ Execute the stored procedure
  995. evaluate
  996. \ We expect there to be a non-zero
  997. \ value left on the stack after
  998. \ executing the stored procedure.
  999. \ If so, continue to run, else exit.
  1000. 0= if
  1001. drop \ key pressed
  1002. drop \ loop iterator
  1003. exit
  1004. else
  1005. swap \ need iterator on top
  1006. then
  1007. then
  1008. \ Re-adjust for missing ACPI menuitem
  1009. arch-i386? true <> menuacpi @ 0<> and if
  1010. swap
  1011. menuacpi @ 1+ over 2dup < -rot = or
  1012. over 59 < and if
  1013. 1-
  1014. then
  1015. swap
  1016. then
  1017. else
  1018. swap \ need iterator on top
  1019. then
  1020. \
  1021. \ Check for menu keycode shortcut(s)
  1022. \
  1023. dup menu_keycode[x]
  1024. getenv dup -1 = if
  1025. drop
  1026. else
  1027. ?number 0<> if
  1028. rot tuck = if
  1029. swap
  1030. dup menu_command[x]
  1031. getenv dup -1 <> if
  1032. evaluate
  1033. 0= if
  1034. 2drop
  1035. exit
  1036. then
  1037. else
  1038. drop
  1039. then
  1040. else
  1041. swap
  1042. then
  1043. then
  1044. then
  1045. 1+ dup 56 > \ increment iterator
  1046. \ continue if less than 57
  1047. until
  1048. drop \ loop iterator
  1049. drop \ key pressed
  1050. again \ Non-operational key was pressed; repeat
  1051. ;
  1052. \ This function unsets all the possible environment variables associated with
  1053. \ creating the interactive menu.
  1054. \
  1055. : menu-unset ( -- )
  1056. 49 \ Iterator start (loop range 49 to 56; ASCII '1' to '8')
  1057. begin
  1058. dup menu_init[x] unsetenv \ menu initializer
  1059. dup menu_command[x] unsetenv \ menu command
  1060. dup menu_caption[x] unsetenv \ menu caption
  1061. dup ansi_caption[x] unsetenv \ ANSI caption
  1062. dup menu_keycode[x] unsetenv \ menu keycode
  1063. dup toggled_text[x] unsetenv \ toggle_menuitem caption
  1064. dup toggled_ansi[x] unsetenv \ toggle_menuitem ANSI caption
  1065. 48 \ Iterator start (inner range 48 to 57; ASCII '0' to '9')
  1066. begin
  1067. \ cycle_menuitem caption and ANSI caption
  1068. 2dup menu_caption[x][y] unsetenv
  1069. 2dup ansi_caption[x][y] unsetenv
  1070. 1+ dup 57 >
  1071. until
  1072. drop \ inner iterator
  1073. 0 over menukeyN ! \ used by menu-create, menu-display
  1074. 0 over init_stateN ! \ used by menu-create
  1075. 0 over toggle_stateN ! \ used by toggle_menuitem
  1076. 0 over init_textN c! \ used by toggle_menuitem
  1077. 0 over cycle_stateN ! \ used by cycle_menuitem
  1078. 1+ dup 56 > \ increment, continue if less than 57
  1079. until
  1080. drop \ iterator
  1081. s" menu_timeout_command" unsetenv \ menu timeout command
  1082. s" menu_reboot" unsetenv \ Reboot menu option flag
  1083. s" menu_acpi" unsetenv \ ACPI menu option flag
  1084. s" menu_kernel" unsetenv \ Kernel menu option flag
  1085. s" menu_options" unsetenv \ Options separator flag
  1086. s" menu_optionstext" unsetenv \ separator display text
  1087. s" menu_init" unsetenv \ menu initializer
  1088. 0 menureboot !
  1089. 0 menuacpi !
  1090. 0 menuoptions !
  1091. ;
  1092. only forth definitions also menu-infrastructure
  1093. \ This function both unsets menu variables and visually erases the menu area
  1094. \ in-preparation for another menu.
  1095. \
  1096. : menu-clear ( -- )
  1097. menu-unset
  1098. menu-erase
  1099. ;
  1100. bullet menubllt !
  1101. also menu-namespace
  1102. \ Initialize our menu initialization state variables
  1103. 0 init_state1 !
  1104. 0 init_state2 !
  1105. 0 init_state3 !
  1106. 0 init_state4 !
  1107. 0 init_state5 !
  1108. 0 init_state6 !
  1109. 0 init_state7 !
  1110. 0 init_state8 !
  1111. \ Initialize our boolean state variables
  1112. 0 toggle_state1 !
  1113. 0 toggle_state2 !
  1114. 0 toggle_state3 !
  1115. 0 toggle_state4 !
  1116. 0 toggle_state5 !
  1117. 0 toggle_state6 !
  1118. 0 toggle_state7 !
  1119. 0 toggle_state8 !
  1120. \ Initialize our array state variables
  1121. 0 cycle_state1 !
  1122. 0 cycle_state2 !
  1123. 0 cycle_state3 !
  1124. 0 cycle_state4 !
  1125. 0 cycle_state5 !
  1126. 0 cycle_state6 !
  1127. 0 cycle_state7 !
  1128. 0 cycle_state8 !
  1129. \ Initialize string containers
  1130. 0 init_text1 c!
  1131. 0 init_text2 c!
  1132. 0 init_text3 c!
  1133. 0 init_text4 c!
  1134. 0 init_text5 c!
  1135. 0 init_text6 c!
  1136. 0 init_text7 c!
  1137. 0 init_text8 c!
  1138. only forth definitions