Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1740 lines
41 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. spmenu.c
  5. Abstract:
  6. Text setup menu support.
  7. Author:
  8. Ted Miller (tedm) 8-September-1993
  9. Revision History:
  10. --*/
  11. #include "efinvram.h"
  12. WCHAR line[512];
  13. HANDLE InputHandle;
  14. HANDLE OutputHandle;
  15. HANDLE OriginalOutputHandle = NULL;
  16. CONSOLE_SCREEN_BUFFER_INFO OriginalConsoleInfo;
  17. CONSOLE_CURSOR_INFO OriginalCursorInfo;
  18. DWORD OriginalInputMode;
  19. WORD NormalAttribute;
  20. WORD HighlightAttribute;
  21. DWORD NumberOfColumns;
  22. DWORD NumberOfRows;
  23. DWORD NumberOfMenuLines;
  24. #define MENUITEM_NORMAL 0x00000000
  25. #define MENUITEM_STATIC 0x00000001
  26. typedef struct _MENU_ITEM {
  27. PWSTR Text;
  28. ULONG Flags;
  29. ULONG LeftX;
  30. ULONG_PTR UserData;
  31. SIZE_T OriginalLength;
  32. } MENU_ITEM, *PMENU_ITEM;
  33. typedef struct _MENU {
  34. PMENU_ITEM Items;
  35. ULONG ItemCount;
  36. ULONG TopY;
  37. ULONG Height;
  38. ULONG LeftX;
  39. ULONG Width;
  40. ULONG TopDisplayedIndex;
  41. BOOLEAN MoreUp,MoreDown;
  42. } MENU, *PMENU;
  43. BOOL
  44. AddItems (
  45. PVOID Menu,
  46. PWSTR StaticText,
  47. PLIST_ENTRY ListHead
  48. );
  49. VOID
  50. DisplayMainMenuKeys (
  51. VOID
  52. );
  53. VOID
  54. DisplayEditMenuKeys (
  55. VOID
  56. );
  57. VOID
  58. EditBootEntry (
  59. IN PMY_BOOT_ENTRY BootEntry,
  60. IN OUT PBOOL ChangesMade
  61. );
  62. VOID
  63. EditTimeout (
  64. IN OUT PBOOL ChangesMade
  65. );
  66. BOOL
  67. PromptToSaveChanges (
  68. VOID
  69. );
  70. PVOID
  71. SpMnCreate(
  72. IN ULONG LeftX,
  73. IN ULONG TopY,
  74. IN ULONG Width,
  75. IN ULONG Height
  76. );
  77. VOID
  78. SpMnDestroy(
  79. IN PVOID Menu
  80. );
  81. BOOLEAN
  82. SpMnAddItem(
  83. IN PVOID Menu,
  84. IN PWSTR Text,
  85. IN ULONG LeftX,
  86. IN ULONG Width,
  87. IN BOOLEAN Selectable,
  88. IN ULONG_PTR UserData
  89. );
  90. PWSTR
  91. SpMnGetText(
  92. IN PVOID Menu,
  93. IN ULONG_PTR UserData
  94. );
  95. PWSTR
  96. SpMnGetTextDup(
  97. IN PVOID Menu,
  98. IN ULONG_PTR UserData
  99. );
  100. VOID
  101. SpMnDisplay(
  102. IN PVOID Menu,
  103. IN ULONG_PTR UserDataOfHighlightedItem,
  104. IN PULONG ValidKeys,
  105. OUT PULONG KeyPressed,
  106. OUT PULONG_PTR UserDataOfSelectedItem
  107. );
  108. VOID
  109. SpMnClearArea (
  110. IN ULONG Top,
  111. IN ULONG Bottom
  112. );
  113. ULONG
  114. SpWaitValidKey(
  115. IN PULONG ValidKeys1,
  116. IN PULONG ValidKeys2 OPTIONAL
  117. );
  118. VOID
  119. WriteConsoleLine (
  120. ULONG Row,
  121. ULONG Column,
  122. PWSTR Text,
  123. BOOL Highlight
  124. );
  125. VOID
  126. pSpMnDrawMenu(
  127. IN PMENU pMenu,
  128. IN ULONG SelectedIndex,
  129. IN BOOLEAN IndicateMore,
  130. IN PWSTR MoreUpText,
  131. IN PWSTR MoreDownText
  132. );
  133. PVOID
  134. SpMnCreate(
  135. IN ULONG LeftX,
  136. IN ULONG TopY,
  137. IN ULONG Width,
  138. IN ULONG Height
  139. )
  140. /*++
  141. Routine Description:
  142. Create a new menu by allocating space for a new menu structure
  143. and initializing its fields.
  144. Arguments:
  145. LeftX - supplies the 0-based X coordinate of the leftmost column
  146. of the menu.
  147. TopY - supplies the 0-based Y coordinate of the topmost line
  148. of the menu.
  149. Width - supplies the maximum displayed width for lines in the menu.
  150. Height - supplies the maximum displayed height of the menu.
  151. The menu will scroll if it is too long to fit in the
  152. allotted space.
  153. Return Value:
  154. Menu handle (expressed as a pvoid) of NULL if memory couldn't
  155. be allocated.
  156. --*/
  157. {
  158. PMENU p;
  159. p = MemAlloc( sizeof(MENU) );
  160. RtlZeroMemory( p, sizeof(MENU) );
  161. p->Items = MemAlloc( 0 );
  162. p->LeftX = LeftX;
  163. p->TopY = TopY;
  164. p->Width = Width;
  165. p->Height = Height;
  166. return p;
  167. }
  168. VOID
  169. SpMnDestroy(
  170. IN PVOID Menu
  171. )
  172. /*++
  173. Routine Description:
  174. Destroy a menu, releasing all memory associated with it.
  175. Arguments:
  176. Menu - supplies handle to menu to destroy.
  177. Return Value:
  178. None.
  179. --*/
  180. {
  181. PMENU pMenu = Menu;
  182. ULONG u;
  183. for(u=0; u<pMenu->ItemCount; u++) {
  184. if(pMenu->Items[u].Text) {
  185. MemFree(pMenu->Items[u].Text);
  186. }
  187. }
  188. MemFree(pMenu->Items);
  189. MemFree(pMenu);
  190. }
  191. BOOLEAN
  192. SpMnAddItem(
  193. IN PVOID Menu,
  194. IN PWSTR Text,
  195. IN ULONG LeftX,
  196. IN ULONG Width,
  197. IN BOOLEAN Selectable,
  198. IN ULONG_PTR UserData
  199. )
  200. /*++
  201. Routine Description:
  202. Add an item to a menu.
  203. Arguments:
  204. Menu - supplies handle to menu to which the item is to be added.
  205. Text - supplies text that comprises the menu selection. This routine
  206. will make a copy of the text.
  207. LeftX - supplies 0-based x coordinate of leftmost character of the text
  208. when it is displayed.
  209. Width - supplies width in characters of the field for this selection.
  210. If this is larger than the number of characters in the text, then
  211. the text is padded to the right with blanks when highlighted.
  212. Selectable - if FALSE, then this text is static -- ie, not selectable.
  213. UserData - supplies a ulong's worth of caller-specific data to be associated
  214. with this menu item.
  215. Return Value:
  216. TRUE if the menu item was added successfully; FALSE if insufficient memory.
  217. --*/
  218. {
  219. PMENU pMenu = Menu;
  220. PMENU_ITEM p;
  221. SIZE_T TextLen;
  222. SIZE_T PaddedLen;
  223. PWSTR String;
  224. ULONG u;
  225. SIZE_T ColumnLen;
  226. SIZE_T FillLen;
  227. //
  228. // Build a string that is padded to the right with blanks to make
  229. // it the right width.
  230. //
  231. TextLen = wcslen(Text);
  232. PaddedLen = max(TextLen,Width);
  233. ColumnLen = TextLen;
  234. FillLen = (PaddedLen <= ColumnLen) ? 0 : PaddedLen - ColumnLen;
  235. String = MemAlloc((PaddedLen+1)*sizeof(WCHAR));
  236. if(!String) {
  237. return(FALSE);
  238. }
  239. wcsncpy(String,Text,TextLen);
  240. for(u=0; u<FillLen; u++) {
  241. String[TextLen+u] = L' ';
  242. }
  243. String[TextLen+u] = 0;
  244. //
  245. // Make space for the item.
  246. //
  247. if((p = MemRealloc(pMenu->Items,(pMenu->ItemCount+1) * sizeof(MENU_ITEM))) == NULL) {
  248. MemFree(String);
  249. return(FALSE);
  250. }
  251. pMenu->Items = p;
  252. //
  253. // Calculate the address of the new menu item and
  254. // indicate that there is now an additional item in the menu.
  255. //
  256. p = &pMenu->Items[pMenu->ItemCount++];
  257. //
  258. // Set the fields of the menu.
  259. //
  260. p->LeftX = LeftX;
  261. p->UserData = UserData;
  262. p->Flags = Selectable ? MENUITEM_NORMAL : MENUITEM_STATIC;
  263. p->Text = String;
  264. p->OriginalLength = TextLen;
  265. return(TRUE);
  266. }
  267. PWSTR
  268. SpMnGetText(
  269. IN PVOID Menu,
  270. IN ULONG_PTR UserData
  271. )
  272. {
  273. PMENU pMenu = Menu;
  274. ULONG i;
  275. for(i=0; i<pMenu->ItemCount; i++) {
  276. if(pMenu->Items[i].UserData == UserData) {
  277. return(pMenu->Items[i].Text);
  278. }
  279. }
  280. return(NULL);
  281. }
  282. PWSTR
  283. SpMnGetTextDup(
  284. IN PVOID Menu,
  285. IN ULONG_PTR UserData
  286. )
  287. {
  288. PMENU pMenu = Menu;
  289. ULONG i;
  290. PWSTR p;
  291. for(i=0; i<pMenu->ItemCount; i++) {
  292. if(pMenu->Items[i].UserData == UserData) {
  293. //
  294. // Make a duplicate; leave off trailing pad spaces.
  295. //
  296. p = MemAlloc((pMenu->Items[i].OriginalLength+1)*sizeof(WCHAR));
  297. wcsncpy(p,pMenu->Items[i].Text,pMenu->Items[i].OriginalLength);
  298. p[pMenu->Items[i].OriginalLength] = 0;
  299. return(p);
  300. }
  301. }
  302. return(NULL);
  303. }
  304. VOID
  305. SpMnDisplay(
  306. IN PVOID Menu,
  307. IN ULONG_PTR UserDataOfHighlightedItem,
  308. IN PULONG ValidKeys,
  309. OUT PULONG KeyPressed,
  310. OUT PULONG_PTR UserDataOfSelectedItem
  311. )
  312. /*++
  313. Routine Description:
  314. Display a menu and accept keystrokes.
  315. When the user presses a menu keystroke (up/down arrow keys), this
  316. routine automatically updates the highlight and calls a callback function
  317. to inform the caller that a new item has the highlight.
  318. When the user presses a keystroke in a list provided by the caller,
  319. this routine returns, providing information about the key pressed and
  320. the item that was highlighted when the key was pressed.
  321. Arguments:
  322. Menu - supplies handle to menu to be displayed.
  323. UserDataOfHighlightedItem - supplies user data of the menu item which
  324. is to receive the highlight initially.
  325. ValidKeys - supplies a list of keystrokes that cause this routine to
  326. return to the caller. The list must be terminated with a 0 entry.
  327. KeyPressed - receives the key press that caused this routine to exit.
  328. This will be a valid from the ValidKeys array.
  329. UserDataOfSelectedItem - receives the UserData of the item that had the
  330. highlight when the user pressed a key in ValidKeys.
  331. Return Value:
  332. None.
  333. --*/
  334. {
  335. ULONG ValidMenuKeys[3] = { VK_UP, VK_DOWN, 0 };
  336. ULONG key;
  337. PMENU pMenu = Menu;
  338. ULONG SelectedIndex,OldIndex;
  339. BOOLEAN FoundNewItem;
  340. ULONG NewTopDisplayedIndex;
  341. BOOLEAN MustScroll;
  342. PWSTR MoreUpText,MoreDownText;
  343. CONSOLE_CURSOR_INFO cursorInfo;
  344. cursorInfo = OriginalCursorInfo;
  345. cursorInfo.bVisible = FALSE;
  346. SetConsoleCursorInfo( OutputHandle, &cursorInfo );
  347. //
  348. // Get the text for the text that indicate that there are more
  349. // selections.
  350. //
  351. MoreUpText = L"[More above...]";
  352. MoreDownText = L"[More below...]";
  353. //
  354. // Locate the seleccted item.
  355. //
  356. for(SelectedIndex=0; SelectedIndex<pMenu->ItemCount; SelectedIndex++) {
  357. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)
  358. && (pMenu->Items[SelectedIndex].UserData == UserDataOfHighlightedItem))
  359. {
  360. break;
  361. }
  362. }
  363. ASSERT(SelectedIndex < pMenu->ItemCount);
  364. //
  365. // Make sure the selected item will be visible when we draw the menu.
  366. //
  367. pMenu->TopDisplayedIndex = 0;
  368. while(SelectedIndex >= pMenu->TopDisplayedIndex + pMenu->Height) {
  369. pMenu->TopDisplayedIndex += pMenu->Height;
  370. }
  371. //
  372. // Draw the menu itself.
  373. //
  374. pSpMnDrawMenu(pMenu,SelectedIndex,TRUE,MoreUpText,MoreDownText);
  375. while ( TRUE ) {
  376. //
  377. // Wait for a valid keypress.
  378. //
  379. key = SpWaitValidKey(ValidKeys,ValidMenuKeys);
  380. //
  381. // If the key is a menu keystroke, handle it here.
  382. //
  383. FoundNewItem = FALSE;
  384. MustScroll = FALSE;
  385. NewTopDisplayedIndex = 0;
  386. OldIndex = SelectedIndex;
  387. switch(key) {
  388. case VK_UP:
  389. //
  390. // Locate the previous selectable item.
  391. //
  392. if(SelectedIndex) {
  393. for(SelectedIndex=SelectedIndex-1; (LONG)SelectedIndex>=0; SelectedIndex--) {
  394. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)) {
  395. FoundNewItem = TRUE;
  396. break;
  397. }
  398. }
  399. if(FoundNewItem) {
  400. //
  401. // Figure out whether we have to scroll the menu.
  402. //
  403. if(SelectedIndex < pMenu->TopDisplayedIndex) {
  404. MustScroll = TRUE;
  405. NewTopDisplayedIndex = SelectedIndex;
  406. }
  407. } else {
  408. //
  409. // If the first lines are static text, there might be no
  410. // way to get them back on the screen -- the tests above
  411. // fail in this case. So if the user hits the up arrow
  412. // when he's at the topmost selectable item but there are
  413. // static items above him, we'll simply scroll the menu
  414. // so that item #0 is at the top.
  415. //
  416. FoundNewItem = TRUE;
  417. NewTopDisplayedIndex = 0;
  418. MustScroll = TRUE;
  419. SelectedIndex = OldIndex;
  420. }
  421. }
  422. break;
  423. case VK_DOWN:
  424. //
  425. // Locate the next selectable item.
  426. //
  427. if(SelectedIndex < pMenu->ItemCount) {
  428. for(SelectedIndex=SelectedIndex+1; SelectedIndex < pMenu->ItemCount; SelectedIndex++) {
  429. if(!(pMenu->Items[SelectedIndex].Flags & MENUITEM_STATIC)) {
  430. FoundNewItem = TRUE;
  431. break;
  432. }
  433. }
  434. if(FoundNewItem) {
  435. //
  436. // Figure out whether we have to scroll the menu.
  437. //
  438. if(SelectedIndex >= pMenu->TopDisplayedIndex + pMenu->Height) {
  439. MustScroll = TRUE;
  440. NewTopDisplayedIndex = pMenu->TopDisplayedIndex + SelectedIndex - OldIndex;
  441. }
  442. }
  443. }
  444. break;
  445. default:
  446. //
  447. // User pressed a non-menu key.
  448. //
  449. *KeyPressed = key;
  450. *UserDataOfSelectedItem = pMenu->Items[SelectedIndex].UserData;
  451. SetConsoleCursorInfo( OutputHandle, &OriginalCursorInfo );
  452. return;
  453. }
  454. if(FoundNewItem) {
  455. //
  456. // Unhighlight the currently selected item.
  457. //
  458. WriteConsoleLine(
  459. pMenu->TopY + OldIndex - pMenu->TopDisplayedIndex,
  460. pMenu->Items[OldIndex].LeftX,
  461. pMenu->Items[OldIndex].Text,
  462. FALSE
  463. );
  464. //
  465. // Highlight the newly selected item. This may involve
  466. // scrolling the menu.
  467. //
  468. if(MustScroll) {
  469. //
  470. // Redraw the menu so the newly highlighted line is in view.
  471. //
  472. pMenu->TopDisplayedIndex = NewTopDisplayedIndex;
  473. pSpMnDrawMenu(pMenu,SelectedIndex,TRUE,MoreUpText,MoreDownText);
  474. }
  475. //
  476. // Highlight the newly selected item.
  477. //
  478. WriteConsoleLine(
  479. pMenu->TopY + SelectedIndex - pMenu->TopDisplayedIndex,
  480. pMenu->Items[SelectedIndex].LeftX,
  481. pMenu->Items[SelectedIndex].Text,
  482. TRUE
  483. );
  484. } else {
  485. SelectedIndex = OldIndex;
  486. }
  487. }
  488. }
  489. VOID
  490. pSpMnDrawMenu(
  491. IN PMENU pMenu,
  492. IN ULONG SelectedIndex,
  493. IN BOOLEAN IndicateMore,
  494. IN PWSTR MoreUpText,
  495. IN PWSTR MoreDownText
  496. )
  497. {
  498. ULONG item;
  499. BOOLEAN MoreUp,MoreDown,MoreStatusChanged;
  500. //
  501. // Blank out the on-screen menu display.
  502. //
  503. for ( item = pMenu->TopY; item < (pMenu->TopY + pMenu->Height); item++ ) {
  504. WriteConsoleLine( item, 0, NULL, FALSE );
  505. }
  506. MoreUp = (BOOLEAN)(pMenu->TopDisplayedIndex > 0);
  507. MoreDown = (BOOLEAN)(pMenu->TopDisplayedIndex + pMenu->Height < pMenu->ItemCount);
  508. MoreStatusChanged = (BOOLEAN)( IndicateMore
  509. && ( (pMenu->MoreUp != MoreUp)
  510. || (pMenu->MoreDown != MoreDown)
  511. )
  512. );
  513. //
  514. // Draw each item that is currently on-screen.
  515. //
  516. ASSERT(pMenu->TopDisplayedIndex < pMenu->ItemCount);
  517. for(item = pMenu->TopDisplayedIndex;
  518. item < min(pMenu->TopDisplayedIndex+pMenu->Height,pMenu->ItemCount);
  519. item++)
  520. {
  521. WriteConsoleLine(
  522. pMenu->TopY + item - pMenu->TopDisplayedIndex,
  523. pMenu->Items[item].LeftX,
  524. pMenu->Items[item].Text,
  525. (BOOLEAN)(item == SelectedIndex)
  526. );
  527. }
  528. //
  529. // If there are more selections above or below us,
  530. // indicate so by placing a small bit of text on the frame.
  531. // Note that the arrow chars can sometimes be DBCS.
  532. //
  533. if(MoreStatusChanged) {
  534. if(MoreUp) {
  535. WriteConsoleLine(
  536. pMenu->TopY - 1,
  537. pMenu->LeftX + 4,
  538. MoreUpText,
  539. FALSE
  540. );
  541. } else {
  542. WriteConsoleLine(
  543. pMenu->TopY - 1,
  544. 0,
  545. NULL,
  546. FALSE
  547. );
  548. }
  549. if(MoreDown) {
  550. WriteConsoleLine(
  551. pMenu->TopY + pMenu->Height,
  552. pMenu->LeftX + 4,
  553. MoreDownText,
  554. FALSE
  555. );
  556. } else {
  557. WriteConsoleLine(
  558. pMenu->TopY + pMenu->Height,
  559. 0,
  560. NULL,
  561. FALSE
  562. );
  563. }
  564. pMenu->MoreUp = MoreUp;
  565. pMenu->MoreDown = MoreDown;
  566. }
  567. }
  568. VOID
  569. InitializeMenuSystem (
  570. VOID
  571. )
  572. {
  573. DWORD error;
  574. COORD windowSize;
  575. CONSOLE_CURSOR_INFO cursorInfo;
  576. cursorInfo = OriginalCursorInfo;
  577. cursorInfo.bVisible = FALSE;
  578. InputHandle = GetStdHandle( STD_INPUT_HANDLE );
  579. if ( InputHandle == NULL ) {
  580. error = GetLastError( );
  581. FatalError( error, L"Unable to get stdin handle: %d\n", error );
  582. }
  583. if ( !GetConsoleMode( InputHandle, &OriginalInputMode ) ) {
  584. error = GetLastError( );
  585. FatalError( error, L"Unable to get stdin mode: %d\n", error );
  586. }
  587. OriginalOutputHandle = GetStdHandle( STD_OUTPUT_HANDLE );
  588. if ( OriginalOutputHandle == NULL ) {
  589. error = GetLastError( );
  590. FatalError( error, L"Unable to get stdout handle: %d\n", error );
  591. }
  592. OutputHandle = CreateConsoleScreenBuffer(
  593. GENERIC_READ | GENERIC_WRITE,
  594. FILE_SHARE_WRITE | FILE_SHARE_READ,
  595. NULL,
  596. CONSOLE_TEXTMODE_BUFFER,
  597. NULL
  598. );
  599. if ( OutputHandle == NULL ) {
  600. error = GetLastError( );
  601. FatalError( error, L"Unable to create console screen buffer: %d\n", error );
  602. }
  603. if ( !GetConsoleScreenBufferInfo( OriginalOutputHandle, &OriginalConsoleInfo ) ) {
  604. error = GetLastError( );
  605. FatalError( error, L"Unable to get console screen buffer info: %d\n", error );
  606. }
  607. if ( !GetConsoleCursorInfo( OriginalOutputHandle, &OriginalCursorInfo ) ) {
  608. error = GetLastError( );
  609. FatalError( error, L"Unable to get console screen buffer info: %d\n", error );
  610. }
  611. NormalAttribute = 0x1F;
  612. HighlightAttribute = 0x71;
  613. NumberOfColumns = OriginalConsoleInfo.srWindow.Right - OriginalConsoleInfo.srWindow.Left + 1;
  614. NumberOfRows = OriginalConsoleInfo.srWindow.Bottom - OriginalConsoleInfo.srWindow.Top + 1;
  615. NumberOfMenuLines = NumberOfRows - (3 + 1 + 1 + 1 + 8);
  616. if ( NumberOfMenuLines > 12 ) {
  617. NumberOfMenuLines = 12;
  618. }
  619. if ( NumberOfRows < 20 ) {
  620. FatalError(
  621. ERROR_INVALID_PARAMETER,
  622. L"Please run this program in a console window with 20 or more lines\n"
  623. );
  624. }
  625. if ( NumberOfColumns < 80 ) {
  626. FatalError(
  627. ERROR_INVALID_PARAMETER,
  628. L"Please run this program in a console window with 80 or more columns\n"
  629. );
  630. }
  631. windowSize.X = (SHORT)NumberOfColumns;
  632. windowSize.Y = (SHORT)NumberOfRows;
  633. if ( !SetConsoleScreenBufferSize( OutputHandle, windowSize ) ) {
  634. error = GetLastError( );
  635. FatalError( error, L"Unable to set console screen buffer size: %d\n", error );
  636. }
  637. if ( !SetConsoleActiveScreenBuffer( OutputHandle ) ) {
  638. error = GetLastError( );
  639. FatalError( error, L"Unable to set active screen buffer: %d\n", error );
  640. }
  641. if ( !SetConsoleMode( InputHandle, 0 ) ) {
  642. error = GetLastError( );
  643. FatalError( error, L"Unable to set console mode: %d\n", error );
  644. }
  645. return;
  646. } // InitializeMenuSystem
  647. VOID
  648. FatalError (
  649. DWORD Error,
  650. PWSTR Format,
  651. ...
  652. )
  653. {
  654. va_list marker;
  655. if ( OutputHandle != NULL ) {
  656. SetConsoleCursorInfo( OutputHandle, &OriginalCursorInfo );
  657. CloseHandle( OutputHandle );
  658. if ( OriginalOutputHandle != NULL ) {
  659. SetConsoleActiveScreenBuffer( OriginalOutputHandle );
  660. SetConsoleMode( InputHandle, OriginalInputMode );
  661. }
  662. }
  663. va_start( marker, Format );
  664. vwprintf( Format, marker );
  665. va_end( marker );
  666. if ( Error == NO_ERROR ) {
  667. Error = ERROR_GEN_FAILURE;
  668. }
  669. exit( Error );
  670. } // FatalError
  671. VOID
  672. WriteConsoleLine (
  673. ULONG Row,
  674. ULONG Column,
  675. PWSTR Text,
  676. BOOL Highlight
  677. )
  678. {
  679. BOOL ok;
  680. DWORD error;
  681. COORD writeCoord;
  682. DWORD numberWritten;
  683. DWORD textLength;
  684. writeCoord.X = 0;
  685. writeCoord.Y = (SHORT)Row;
  686. ok = FillConsoleOutputCharacter(
  687. OutputHandle,
  688. ' ',
  689. NumberOfColumns,
  690. writeCoord,
  691. &numberWritten
  692. );
  693. if ( !ok ) {
  694. error = GetLastError( );
  695. FatalError( error, L"Error filling console line: %d\n", error );
  696. }
  697. ok = FillConsoleOutputAttribute(
  698. OutputHandle,
  699. OriginalConsoleInfo.wAttributes,
  700. NumberOfColumns,
  701. writeCoord,
  702. &numberWritten
  703. );
  704. if ( !ok ) {
  705. error = GetLastError( );
  706. FatalError( error, L"Error filling console attributes: %d\n", error );
  707. }
  708. if ( (Text == NULL) || ((textLength = (DWORD)wcslen( Text )) == 0) ) {
  709. return;
  710. }
  711. writeCoord.X = (SHORT)Column;
  712. ok = WriteConsoleOutputCharacter(
  713. OutputHandle,
  714. Text,
  715. textLength,
  716. writeCoord,
  717. &numberWritten
  718. );
  719. if ( !ok ) {
  720. error = GetLastError( );
  721. FatalError( error, L"Error writing console line: %d\n", error );
  722. }
  723. if (Highlight) {
  724. WORD attr = ((OriginalConsoleInfo.wAttributes & 0xf0) >> 4) +
  725. ((OriginalConsoleInfo.wAttributes & 0x0f) << 4);
  726. ok = FillConsoleOutputAttribute(
  727. OutputHandle,
  728. attr,
  729. textLength,
  730. writeCoord,
  731. &numberWritten
  732. );
  733. if ( !ok ) {
  734. error = GetLastError( );
  735. FatalError( error, L"Error writing console attributes: %d\n", error );
  736. }
  737. }
  738. return;
  739. } // WriteConsoleLine
  740. ULONG
  741. SpWaitValidKey(
  742. IN PULONG ValidKeys1,
  743. IN PULONG ValidKeys2 OPTIONAL
  744. )
  745. /*++
  746. Routine Description:
  747. Wait for a key to be pressed that appears in a list of valid keys.
  748. Arguments:
  749. ValidKeys1 - supplies list of valid keystrokes. The list must be
  750. terminated with a 0 entry.
  751. ValidKeys2 - if specified, supplies an additional list of valid keystrokes.
  752. Return Value:
  753. The key that was pressed (see above).
  754. --*/
  755. {
  756. ULONG c;
  757. ULONG i;
  758. INPUT_RECORD InputRecord;
  759. ULONG NumberOfInputRecords;
  760. FlushConsoleInputBuffer( InputHandle );
  761. while ( TRUE ) {
  762. WaitForSingleObject( InputHandle, INFINITE );
  763. if ( ReadConsoleInput( InputHandle, &InputRecord, 1, &NumberOfInputRecords ) &&
  764. InputRecord.EventType == KEY_EVENT &&
  765. InputRecord.Event.KeyEvent.bKeyDown ) {
  766. c = InputRecord.Event.KeyEvent.wVirtualKeyCode;
  767. //
  768. // Check for normal key.
  769. //
  770. for(i=0; ValidKeys1[i]; i++) {
  771. if(c == ValidKeys1[i]) {
  772. return(c);
  773. }
  774. }
  775. //
  776. // Check secondary list.
  777. //
  778. if(ValidKeys2) {
  779. for(i=0; ValidKeys2[i]; i++) {
  780. if(c == ValidKeys2[i]) {
  781. return(c);
  782. }
  783. }
  784. }
  785. }
  786. }
  787. }
  788. VOID
  789. SpMnClearArea (
  790. IN ULONG Top,
  791. IN ULONG Bottom
  792. )
  793. {
  794. ULONG row;
  795. for ( row = Top; row <= Bottom; row++ ) {
  796. WriteConsoleLine( row, 0, NULL, FALSE );
  797. }
  798. return;
  799. } // SpMnClearArea
  800. VOID
  801. MainMenu (
  802. VOID
  803. )
  804. {
  805. BOOL b;
  806. PLIST_ENTRY listEntry;
  807. PLIST_ENTRY listEntry2;
  808. PMY_BOOT_ENTRY bootEntry;
  809. PMY_BOOT_ENTRY currentBootEntry;
  810. ULONG validKeys[] = {
  811. VK_ESCAPE,
  812. VK_RETURN,
  813. VK_PRIOR,
  814. VK_NEXT,
  815. VK_HOME,
  816. VK_END,
  817. VK_DELETE,
  818. 'Q',
  819. 'U',
  820. 'D',
  821. 'T',
  822. 'B',
  823. 'X',
  824. 'A',
  825. 'O',
  826. 'E',
  827. 'M',
  828. 'S',
  829. 0
  830. };
  831. PVOID menu;
  832. ULONG pressedKey;
  833. BOOL changesMade = FALSE;
  834. WriteConsoleLine( 1, 1, L"Windows Whistler EFI NVRAM Editor", FALSE );
  835. listEntry = BootEntries.Flink;
  836. if ( listEntry != &BootEntries ) {
  837. currentBootEntry = CONTAINING_RECORD( listEntry, MY_BOOT_ENTRY, ListEntry );
  838. } else {
  839. currentBootEntry = NULL;
  840. }
  841. while ( TRUE ) {
  842. menu = SpMnCreate( 4, 4, NumberOfColumns - 4, NumberOfMenuLines );
  843. b = AddItems(
  844. menu,
  845. NULL,
  846. &BootEntries
  847. );
  848. b |= AddItems(
  849. menu,
  850. L"The following boot entries are marked active, but are not in the boot order list:",
  851. &ActiveUnorderedBootEntries
  852. );
  853. b |= AddItems(
  854. menu,
  855. L"The following boot entries are marked inactive, and are not in the boot order list:",
  856. &InactiveUnorderedBootEntries
  857. );
  858. if ( !b ) {
  859. swprintf( line, L"No boot entries to display" );
  860. SpMnAddItem(
  861. menu,
  862. line,
  863. 4,
  864. (ULONG)wcslen( line ),
  865. TRUE,
  866. (ULONG_PTR)NULL
  867. );
  868. currentBootEntry = NULL;
  869. }
  870. DisplayMainMenuKeys( );
  871. SpMnDisplay(
  872. menu,
  873. (ULONG_PTR)currentBootEntry,
  874. validKeys,
  875. &pressedKey,
  876. (ULONG_PTR *)&bootEntry
  877. );
  878. SpMnDestroy( menu );
  879. if ( (pressedKey == 'Q') || (pressedKey == VK_ESCAPE) ) {
  880. if ( changesMade ) {
  881. if ( PromptToSaveChanges( ) ) {
  882. ClearMenuArea( );
  883. SaveChanges( NULL );
  884. }
  885. }
  886. break;
  887. }
  888. if ( bootEntry != NULL ) {
  889. currentBootEntry = bootEntry;
  890. if ( bootEntry->ListHead == &BootEntries ) {
  891. if ( (pressedKey == 'U') || (pressedKey == VK_PRIOR) ) {
  892. listEntry2 = bootEntry->ListEntry.Blink;
  893. if ( listEntry2 != &BootEntries ) {
  894. RemoveEntryList( &bootEntry->ListEntry );
  895. InsertTailList( listEntry2, &bootEntry->ListEntry );
  896. changesMade = TRUE;
  897. }
  898. continue;
  899. } else if ( (pressedKey == 'D') || (pressedKey == VK_NEXT) ) {
  900. listEntry2 = bootEntry->ListEntry.Flink;
  901. if ( listEntry2 != &BootEntries ) {
  902. RemoveEntryList( &bootEntry->ListEntry );
  903. InsertHeadList( listEntry2, &bootEntry->ListEntry );
  904. changesMade = TRUE;
  905. }
  906. continue;
  907. } else if ( (pressedKey == 'T') || (pressedKey == VK_HOME) ) {
  908. listEntry2 = bootEntry->ListEntry.Blink;
  909. if ( listEntry2 != &BootEntries ) {
  910. RemoveEntryList( &bootEntry->ListEntry );
  911. InsertHeadList( &BootEntries, &bootEntry->ListEntry );
  912. changesMade = TRUE;
  913. }
  914. continue;
  915. } else if ( (pressedKey == 'B') || (pressedKey == VK_END) ) {
  916. listEntry2 = bootEntry->ListEntry.Flink;
  917. if ( listEntry2 != &BootEntries ) {
  918. RemoveEntryList( &bootEntry->ListEntry );
  919. InsertTailList( &BootEntries, &bootEntry->ListEntry );
  920. changesMade = TRUE;
  921. }
  922. continue;
  923. }
  924. }
  925. if ( (pressedKey == 'X') || (pressedKey == VK_DELETE) ) {
  926. MBE_SET_DELETED( bootEntry );
  927. listEntry2 = bootEntry->ListEntry.Flink;
  928. currentBootEntry = CONTAINING_RECORD( listEntry2, MY_BOOT_ENTRY, ListEntry );
  929. if ( listEntry2 == bootEntry->ListHead ) {
  930. listEntry2 = bootEntry->ListEntry.Blink;
  931. currentBootEntry = CONTAINING_RECORD( listEntry2, MY_BOOT_ENTRY, ListEntry );
  932. if ( listEntry2 == bootEntry->ListHead ) {
  933. currentBootEntry = NULL;
  934. }
  935. }
  936. RemoveEntryList( &bootEntry->ListEntry );
  937. InsertTailList( &DeletedBootEntries, &bootEntry->ListEntry );
  938. bootEntry->ListHead = &DeletedBootEntries;
  939. changesMade = TRUE;
  940. } else if ( (pressedKey == 'E') || (pressedKey == VK_RETURN) ) {
  941. EditBootEntry( bootEntry, &changesMade );
  942. } else if ( pressedKey == 'M' ) {
  943. EditTimeout( &changesMade );
  944. } else if ( pressedKey == 'S' ) {
  945. if ( changesMade ) {
  946. ClearMenuArea( );
  947. currentBootEntry = SaveChanges( currentBootEntry );
  948. changesMade = FALSE;
  949. }
  950. } else if ( pressedKey == 'A' ) {
  951. if ( MBE_IS_ACTIVE( bootEntry ) ) {
  952. MBE_CLEAR_ACTIVE( bootEntry );
  953. MBE_SET_MODIFIED( bootEntry );
  954. if ( bootEntry->ListHead == &ActiveUnorderedBootEntries ) {
  955. RemoveEntryList( &bootEntry->ListEntry );
  956. InsertTailList( &InactiveUnorderedBootEntries, &bootEntry->ListEntry );
  957. bootEntry->ListHead = &InactiveUnorderedBootEntries;
  958. }
  959. } else {
  960. MBE_SET_ACTIVE( bootEntry );
  961. MBE_SET_MODIFIED( bootEntry );
  962. if ( bootEntry->ListHead == &InactiveUnorderedBootEntries ) {
  963. RemoveEntryList( &bootEntry->ListEntry );
  964. InsertTailList( &ActiveUnorderedBootEntries, &bootEntry->ListEntry );
  965. bootEntry->ListHead = &ActiveUnorderedBootEntries;
  966. }
  967. }
  968. changesMade = TRUE;
  969. } else if ( pressedKey == 'O' ) {
  970. RemoveEntryList( &bootEntry->ListEntry );
  971. if ( bootEntry->ListHead == &BootEntries ) {
  972. if ( MBE_IS_ACTIVE( bootEntry ) ) {
  973. InsertTailList( &ActiveUnorderedBootEntries, &bootEntry->ListEntry );
  974. bootEntry->ListHead = &ActiveUnorderedBootEntries;
  975. } else {
  976. InsertTailList( &InactiveUnorderedBootEntries, &bootEntry->ListEntry );
  977. bootEntry->ListHead = &InactiveUnorderedBootEntries;
  978. }
  979. } else {
  980. InsertTailList( &BootEntries, &bootEntry->ListEntry );
  981. bootEntry->ListHead = &BootEntries;
  982. }
  983. changesMade = TRUE;
  984. }
  985. }
  986. }
  987. } // MainMenu
  988. BOOL
  989. AddItems (
  990. PVOID Menu,
  991. PWSTR StaticText,
  992. PLIST_ENTRY ListHead
  993. )
  994. {
  995. PLIST_ENTRY listEntry;
  996. PMY_BOOT_ENTRY bootEntry;
  997. if ( ListHead->Flink != ListHead ) {
  998. if ( ARGUMENT_PRESENT(StaticText) ) {
  999. SpMnAddItem(
  1000. Menu,
  1001. L"",
  1002. 4,
  1003. 0,
  1004. FALSE,
  1005. (ULONG_PTR)NULL
  1006. );
  1007. SpMnAddItem(
  1008. Menu,
  1009. StaticText,
  1010. 4,
  1011. (ULONG)wcslen( StaticText ),
  1012. FALSE,
  1013. (ULONG_PTR)NULL
  1014. );
  1015. }
  1016. for ( listEntry = ListHead->Flink;
  1017. listEntry != ListHead;
  1018. listEntry = listEntry->Flink ) {
  1019. PWSTR osDirectoryNtName = NULL;
  1020. bootEntry = CONTAINING_RECORD( listEntry, MY_BOOT_ENTRY, ListEntry );
  1021. if ( MBE_IS_NT( bootEntry ) ) {
  1022. osDirectoryNtName = GetNtNameForFilePath( bootEntry->OsFilePath );
  1023. }
  1024. if ( osDirectoryNtName != NULL) {
  1025. swprintf(
  1026. line,
  1027. L"%-40ws %ws",
  1028. bootEntry->FriendlyName,
  1029. osDirectoryNtName
  1030. );
  1031. } else {
  1032. swprintf(
  1033. line,
  1034. L"%ws",
  1035. bootEntry->FriendlyName
  1036. );
  1037. }
  1038. SpMnAddItem(
  1039. Menu,
  1040. line,
  1041. 6,
  1042. (ULONG)wcslen( line ),
  1043. TRUE,
  1044. (ULONG_PTR)bootEntry
  1045. );
  1046. if ( osDirectoryNtName != NULL ) {
  1047. MemFree( osDirectoryNtName );
  1048. }
  1049. }
  1050. return TRUE;
  1051. }
  1052. return FALSE;
  1053. } // AddItems
  1054. VOID
  1055. EditFriendlyName (
  1056. IN OUT PMY_BOOT_ENTRY BootEntry,
  1057. IN OUT PBOOL ChangesMade
  1058. )
  1059. {
  1060. COORD position;
  1061. DWORD numberRead;
  1062. SpMnClearArea( 3, 3 + NumberOfMenuLines + 1 );
  1063. swprintf( line, L"Current friendly name: %ws", BootEntry->FriendlyName );
  1064. WriteConsoleLine( 4, 4, line, FALSE );
  1065. swprintf( line, L" New friendly name: " );
  1066. WriteConsoleLine( 6, 4, line, FALSE );
  1067. position.X = (USHORT)(4 + wcslen( line ));
  1068. position.Y = 6;
  1069. SetConsoleCursorPosition( OutputHandle, position );
  1070. SetConsoleMode( InputHandle, OriginalInputMode );
  1071. ReadConsole( InputHandle, line, 511, &numberRead, NULL );
  1072. SetConsoleMode( InputHandle, 0 );
  1073. if ( numberRead >= 2 ) {
  1074. numberRead -= 2;
  1075. }
  1076. if ( numberRead != 0 ) {
  1077. line[numberRead] = 0;
  1078. if ( wcscmp( BootEntry->FriendlyName, line ) != 0 ) {
  1079. FREE_IF_SEPARATE_ALLOCATION( BootEntry, FriendlyName );
  1080. BootEntry->FriendlyNameLength = (numberRead + 1) * sizeof(WCHAR);
  1081. BootEntry->FriendlyName = MemAlloc( BootEntry->FriendlyNameLength );
  1082. wcscpy( BootEntry->FriendlyName, line );
  1083. MBE_SET_MODIFIED( BootEntry );
  1084. *ChangesMade = TRUE;
  1085. }
  1086. }
  1087. return;
  1088. } // EditFriendlyName
  1089. VOID
  1090. EditLoadOptions (
  1091. IN OUT PMY_BOOT_ENTRY BootEntry,
  1092. IN OUT PBOOL ChangesMade
  1093. )
  1094. {
  1095. COORD position;
  1096. DWORD numberRead;
  1097. SpMnClearArea( 3, 3 + NumberOfMenuLines + 1 );
  1098. swprintf( line, L"Current load options: %ws", BootEntry->OsLoadOptions );
  1099. WriteConsoleLine( 4, 4, line, FALSE );
  1100. swprintf( line, L" New load options: " );
  1101. WriteConsoleLine( 6, 4, line, FALSE );
  1102. position.X = (USHORT)(4 + wcslen( line ));
  1103. position.Y = 6;
  1104. SetConsoleCursorPosition( OutputHandle, position );
  1105. SetConsoleMode( InputHandle, OriginalInputMode );
  1106. ReadConsole( InputHandle, line, 511, &numberRead, NULL );
  1107. SetConsoleMode( InputHandle, 0 );
  1108. if ( numberRead >= 2 ) {
  1109. numberRead -= 2;
  1110. }
  1111. if ( numberRead != 0 ) {
  1112. line[numberRead] = 0;
  1113. if ( wcscmp( BootEntry->OsLoadOptions, line ) != 0 ) {
  1114. FREE_IF_SEPARATE_ALLOCATION( BootEntry, OsLoadOptions );
  1115. BootEntry->OsLoadOptionsLength = (numberRead + 1) * sizeof(WCHAR);
  1116. BootEntry->OsLoadOptions = MemAlloc( BootEntry->OsLoadOptionsLength );
  1117. wcscpy( BootEntry->OsLoadOptions, line );
  1118. MBE_SET_MODIFIED( BootEntry );
  1119. *ChangesMade = TRUE;
  1120. }
  1121. }
  1122. return;
  1123. } // EditLoadOptions
  1124. VOID
  1125. EditBootEntry (
  1126. IN PMY_BOOT_ENTRY BootEntry,
  1127. IN OUT PBOOL ChangesMade
  1128. )
  1129. {
  1130. ULONG numValidKeys;
  1131. ULONG validKeys[20];
  1132. PVOID menu;
  1133. ULONG pressedKey;
  1134. ULONG_PTR itemToEdit;
  1135. while ( TRUE ) {
  1136. menu = SpMnCreate( 4, 4, NumberOfColumns - 4, NumberOfMenuLines );
  1137. numValidKeys = 0;
  1138. validKeys[numValidKeys++] = VK_ESCAPE;
  1139. validKeys[numValidKeys++] = 'Q';
  1140. validKeys[numValidKeys++] = VK_RETURN;
  1141. validKeys[numValidKeys++] = 'E';
  1142. swprintf(
  1143. line,
  1144. L"Friendly name: %ws",
  1145. BootEntry->FriendlyName
  1146. );
  1147. SpMnAddItem(
  1148. menu,
  1149. line,
  1150. 4,
  1151. (ULONG)wcslen( line ),
  1152. TRUE,
  1153. 1
  1154. );
  1155. validKeys[numValidKeys++] = 'F';
  1156. if ( MBE_IS_NT( BootEntry ) ) {
  1157. swprintf(
  1158. line,
  1159. L"Load options: %ws",
  1160. BootEntry->OsLoadOptions
  1161. );
  1162. SpMnAddItem(
  1163. menu,
  1164. line,
  1165. 4,
  1166. (ULONG)wcslen( line ),
  1167. TRUE,
  1168. 2
  1169. );
  1170. validKeys[numValidKeys++] = 'L';
  1171. }
  1172. validKeys[numValidKeys] = 0;
  1173. DisplayEditMenuKeys( );
  1174. SpMnDisplay(
  1175. menu,
  1176. 1,
  1177. validKeys,
  1178. &pressedKey,
  1179. &itemToEdit
  1180. );
  1181. SpMnDestroy( menu );
  1182. if ( (pressedKey == 'Q') || (pressedKey == VK_ESCAPE) ) {
  1183. break;
  1184. }
  1185. if ( (itemToEdit == 1) || (pressedKey == 'F') ) {
  1186. EditFriendlyName( BootEntry, ChangesMade );
  1187. } else if ( (itemToEdit == 2) || (pressedKey == 'L') ) {
  1188. EditLoadOptions( BootEntry, ChangesMade );
  1189. }
  1190. }
  1191. return;
  1192. } // EditBootEntry
  1193. VOID
  1194. EditTimeout (
  1195. IN OUT PBOOL ChangesMade
  1196. )
  1197. {
  1198. COORD position;
  1199. DWORD numberRead;
  1200. ULONG timeout;
  1201. ULONG i;
  1202. PWSTR p;
  1203. SpMnClearArea( 3, 3 + NumberOfMenuLines + 1 );
  1204. swprintf( line, L"Current timeout: %d", BootOptions->Timeout );
  1205. WriteConsoleLine( 4, 4, line, FALSE );
  1206. again:
  1207. swprintf( line, L" New timeout: " );
  1208. WriteConsoleLine( 6, 4, line, FALSE );
  1209. position.X = (USHORT)(4 + wcslen( line ));
  1210. position.Y = 6;
  1211. SetConsoleCursorPosition( OutputHandle, position );
  1212. SetConsoleMode( InputHandle, OriginalInputMode );
  1213. ReadConsole( InputHandle, line, 511, &numberRead, NULL );
  1214. SetConsoleMode( InputHandle, 0 );
  1215. if ( numberRead >= 2 ) {
  1216. numberRead -= 2;
  1217. }
  1218. if ( numberRead != 0 ) {
  1219. line[numberRead] = 0;
  1220. timeout = 0;
  1221. p = line;
  1222. while ( *p != 0 ) {
  1223. if ( (*p < L'0') || (*p > L'9') ) {
  1224. swprintf( line, L"Invalid characters in number" );
  1225. WriteConsoleLine( 8, 4, line, TRUE );
  1226. goto again;
  1227. }
  1228. i = (timeout * 10) + (*p - L'0');
  1229. if ( i < timeout ) {
  1230. swprintf( line, L"Overflow in number %d %d", i, timeout );
  1231. WriteConsoleLine( 8, 4, line, TRUE );
  1232. goto again;
  1233. }
  1234. timeout = i;
  1235. p++;
  1236. }
  1237. if ( timeout != BootOptions->Timeout ) {
  1238. BootOptions->Timeout = timeout;
  1239. *ChangesMade = TRUE;
  1240. }
  1241. }
  1242. return;
  1243. } // EditTimeout
  1244. BOOL
  1245. PromptToSaveChanges (
  1246. VOID
  1247. )
  1248. {
  1249. COORD position;
  1250. ULONG keys[] = { 'Y', 'N', 0 };
  1251. ULONG key;
  1252. SpMnClearArea( 3, 3 + NumberOfMenuLines + 1 );
  1253. swprintf( line, L"Save changes?" );
  1254. WriteConsoleLine( 4, 4, line, TRUE );
  1255. position.X = (USHORT)(4 + wcslen( line ));
  1256. position.Y = 4;
  1257. SetConsoleCursorPosition( OutputHandle, position );
  1258. key = SpWaitValidKey( keys, NULL );
  1259. return (BOOL)(key == 'Y');
  1260. } // PromptToSaveChanges
  1261. VOID
  1262. ClearMenuArea (
  1263. VOID
  1264. )
  1265. {
  1266. SpMnClearArea( 3, 3 + NumberOfMenuLines + 1 );
  1267. }
  1268. VOID
  1269. SetStatusLine (
  1270. PWSTR Status
  1271. )
  1272. {
  1273. COORD position;
  1274. WriteConsoleLine( 4, 4, Status, TRUE );
  1275. position.X = (USHORT)(4 + wcslen( Status ));
  1276. position.Y = 4;
  1277. SetConsoleCursorPosition( OutputHandle, position );
  1278. }
  1279. VOID
  1280. SetStatusLineAndWait (
  1281. PWSTR Status
  1282. )
  1283. {
  1284. ULONG keys[] = { VK_ESCAPE, VK_RETURN, VK_SPACE, 0 };
  1285. SetStatusLine( Status );
  1286. SpWaitValidKey( keys, NULL );
  1287. }
  1288. VOID
  1289. SetStatusLine2 (
  1290. PWSTR Status
  1291. )
  1292. {
  1293. COORD position;
  1294. WriteConsoleLine( 6, 4, Status, TRUE );
  1295. position.X = (USHORT)(4 + wcslen( Status ));
  1296. position.Y = 6;
  1297. SetConsoleCursorPosition( OutputHandle, position );
  1298. }
  1299. VOID
  1300. SetStatusLine2AndWait (
  1301. PWSTR Status
  1302. )
  1303. {
  1304. ULONG keys[] = { VK_ESCAPE, VK_RETURN, VK_SPACE, 0 };
  1305. SetStatusLine2( Status );
  1306. SpWaitValidKey( keys, NULL );
  1307. }
  1308. VOID
  1309. DisplayMainMenuKeys (
  1310. VOID
  1311. )
  1312. {
  1313. ULONG startLine = 3 + 1 + NumberOfMenuLines + 1 + 1;
  1314. SpMnClearArea( startLine, NumberOfRows - 1 );
  1315. WriteConsoleLine(
  1316. startLine,
  1317. 1,
  1318. L"PGUP/U = Move up | HOME/T = Move to top | DELETE/X = Delete",
  1319. FALSE
  1320. );
  1321. WriteConsoleLine(
  1322. startLine + 1,
  1323. 1,
  1324. L"PGDN/D = Move down | END/B = Move to bottom | RETURN/E = Edit",
  1325. FALSE
  1326. );
  1327. WriteConsoleLine(
  1328. startLine + 3,
  1329. 1,
  1330. L" A = [De]activate | O = Remove from/add to boot order",
  1331. FALSE
  1332. );
  1333. WriteConsoleLine(
  1334. startLine + 5,
  1335. 1,
  1336. L" M = Set timeout",
  1337. FALSE
  1338. );
  1339. WriteConsoleLine(
  1340. startLine + 7,
  1341. 1,
  1342. L" ESC/Q = Quit | S = Save changes",
  1343. FALSE
  1344. );
  1345. return;
  1346. } // DisplayMainMenuKeys
  1347. VOID
  1348. DisplayEditMenuKeys (
  1349. VOID
  1350. )
  1351. {
  1352. ULONG startLine = 3 + 1 + NumberOfMenuLines + 1 + 1 + 1;
  1353. SpMnClearArea( startLine, NumberOfRows - 1 );
  1354. WriteConsoleLine(
  1355. startLine + 1,
  1356. 1,
  1357. L" ESC/Q = Quit | | RETURN/E = Edit",
  1358. FALSE
  1359. );
  1360. return;
  1361. } // DisplayEditMenuKeys