2 * PROJECT: ReactOS Boot Loader
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/freeldr/freeldr/ui/directui.c
5 * PURPOSE: FreeLDR UI Routines
6 * PROGRAMMERS: ReactOS Portable Systems Group
10 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 /* FUNCTIONS ******************************************************************/
21 UCHAR UiMenuFgColor
= COLOR_GRAY
;
22 UCHAR UiMenuBgColor
= COLOR_BLACK
;
23 UCHAR UiTextColor
= COLOR_GRAY
;
24 UCHAR UiSelectedTextColor
= COLOR_BLACK
;
25 UCHAR UiSelectedTextBgColor
= COLOR_GRAY
;
26 CHAR UiTimeText
[260] = "Seconds until highlighted choice will be started automatically: ";
29 TuiPrintf(const char *Format
,
38 Length
= _vsnprintf(Buffer
, sizeof(Buffer
), Format
, ap
);
41 if (Length
== -1) Length
= sizeof(Buffer
);
43 for (i
= 0; i
< Length
; i
++)
45 MachConsPutChar(Buffer
[i
]);
52 UiInitialize(IN BOOLEAN ShowGui
)
57 if (!ShowGui
) return TRUE
;
59 /* Set mode and query size */
60 MachVideoSetDisplayMode(NULL
, TRUE
);
61 MachVideoGetDisplaySize(&UiScreenWidth
, &UiScreenHeight
, &Depth
);
66 UiUnInitialize(IN PCSTR BootText
)
75 /* Clear the screen */
76 MachVideoClearScreen(ATTR(COLOR_WHITE
, COLOR_BLACK
));
80 UiDrawText(IN ULONG X
,
87 /* Draw the text character by character, but don't exceed the width */
88 for (i
= X
, j
= 0; Text
[j
] && i
< UiScreenWidth
; i
++, j
++)
90 /* Write the character */
91 MachVideoPutChar(Text
[j
], Attr
, i
, Y
);
96 UiDrawText2(IN ULONG X
,
104 /* Draw the text character by character, but don't exceed the width */
105 for (i
= X
, j
= 0; Text
[j
] && i
< UiScreenWidth
&& (MaxNumChars
> 0 ? j
< MaxNumChars
: TRUE
); i
++, j
++)
107 /* Write the character */
108 MachVideoPutChar(Text
[j
], Attr
, i
, Y
);
113 UiDrawCenteredText(IN ULONG Left
,
120 ULONG TextLength
, BoxWidth
, BoxHeight
, LineBreakCount
, Index
, LastIndex
;
121 ULONG RealLeft
, RealTop
, X
, Y
;
124 /* Query text length */
125 TextLength
= strlen(TextString
);
127 /* Count the new lines and the box width */
131 for (Index
=0; Index
< TextLength
; Index
++)
133 /* Scan for new lines */
134 if (TextString
[Index
] == '\n')
136 /* Remember the new line */
142 /* Check for new larger box width */
143 if ((Index
- LastIndex
) > BoxWidth
)
146 BoxWidth
= (Index
- LastIndex
);
151 /* Base the box height on the number of lines */
152 BoxHeight
= LineBreakCount
+ 1;
154 /* Create the centered coordinates */
155 RealLeft
= (((Right
- Left
) - BoxWidth
) / 2) + Left
;
156 RealTop
= (((Bottom
- Top
) - BoxHeight
) / 2) + Top
;
158 /* Now go for a second scan */
160 for (Index
=0; Index
< TextLength
; Index
++)
162 /* Look for new lines again */
163 if (TextString
[Index
] == '\n')
165 /* Update where the text should start */
171 /* We've got a line of text to print, do it */
172 X
= RealLeft
+ LastIndex
;
175 Temp
[0] = TextString
[Index
];
177 UiDrawText(X
, Y
, Temp
, Attr
);
183 UiDrawStatusText(IN PCSTR StatusText
)
189 UiInfoBox(IN PCSTR MessageText
)
191 TuiPrintf(MessageText
);
195 UiMessageBox(IN PCSTR MessageText
)
197 TuiPrintf(MessageText
);
201 UiMessageBoxCritical(IN PCSTR MessageText
)
203 TuiPrintf(MessageText
);
207 UiDrawProgressBarCenter(IN ULONG Position
,
209 IN PCHAR ProgressText
)
211 ULONG Left
, Top
, Right
, Bottom
, Width
, Height
;
213 /* Build the coordinates and sizes */
215 Width
= UiScreenWidth
;
217 Right
= (Left
+ Width
) - 1;
218 Top
= UiScreenHeight
- Height
- 4;
219 Bottom
= Top
+ Height
+ 1;
221 /* Draw the progress bar */
222 UiDrawProgressBar(Left
, Top
, Right
, Bottom
, Position
, Range
, ProgressText
);
226 UiDrawProgressBar(IN ULONG Left
,
232 IN PCHAR ProgressText
)
234 ULONG i
, ProgressBarWidth
;
236 /* Calculate the width of the bar proper */
237 ProgressBarWidth
= (Right
- Left
) - 3;
239 /* First make sure the progress bar text fits */
240 UiTruncateStringEllipsis(ProgressText
, ProgressBarWidth
- 4);
241 if (Position
> Range
) Position
= Range
;
243 /* Draw the "Loading..." text */
244 UiDrawCenteredText(Left
+ 2, Top
+ 1, Right
- 2, Top
+ 1, ProgressText
, ATTR(7, 0));
246 /* Draw the percent complete */
247 for (i
= 0; i
< (Position
* ProgressBarWidth
) / Range
; i
++)
249 /* Use the fill character */
250 UiDrawText(Left
+ 2 + i
, Top
+ 2, "\xDB", ATTR(UiTextColor
, UiMenuBgColor
));
255 UiShowMessageBoxesInSection(IN PCSTR SectionName
)
261 UiTruncateStringEllipsis(IN PCHAR StringText
,
264 /* If it's too large, just add some ellipsis past the maximum */
265 if (strlen(StringText
) > MaxChars
) strcpy(&StringText
[MaxChars
- 3], "...");
270 UiDrawMenuBox(IN PUI_MENU_INFO MenuInfo
)
272 CHAR MenuLineText
[80], TempString
[80];
275 /* If there is a timeout draw the time remaining */
276 if (MenuInfo
->MenuTimeRemaining
>= 0)
278 /* Copy the integral time text string, and remove the last 2 chars */
279 strcpy(TempString
, UiTimeText
);
280 i
= strlen(TempString
);
281 TempString
[i
- 2] = 0;
283 /* Display the first part of the string and the remaining time */
284 strcpy(MenuLineText
, TempString
);
285 _itoa(MenuInfo
->MenuTimeRemaining
, TempString
, 10);
286 strcat(MenuLineText
, TempString
);
288 /* Add the last 2 chars */
289 strcat(MenuLineText
, &UiTimeText
[i
- 2]);
291 /* Display under the menu directly */
293 MenuInfo
->Bottom
+ 4,
295 ATTR(UiMenuFgColor
, UiMenuBgColor
));
299 /* Erase the timeout string with spaces, and 0-terminate for sure */
300 for (i
=0; i
<sizeof(MenuLineText
)-1; i
++)
302 MenuLineText
[i
] = ' ';
304 MenuLineText
[sizeof(MenuLineText
)-1] = 0;
306 /* Draw this "empty" string to erase */
308 MenuInfo
->Bottom
+ 4,
310 ATTR(UiMenuFgColor
, UiMenuBgColor
));
314 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
316 /* Check if it's a separator */
317 if (MenuInfo
->MenuItemList
[i
] == NULL
)
319 /* Draw the separator line */
320 UiDrawText(MenuInfo
->Left
,
321 MenuInfo
->Top
+ i
+ 1,
323 ATTR(UiMenuFgColor
, UiMenuBgColor
));
324 UiDrawText(MenuInfo
->Right
,
325 MenuInfo
->Top
+ i
+ 1,
327 ATTR(UiMenuFgColor
, UiMenuBgColor
));
334 UiDrawMenuItem(IN PUI_MENU_INFO MenuInfo
,
335 IN ULONG MenuItemNumber
)
337 CHAR MenuLineText
[80];
338 UCHAR Attribute
= ATTR(UiTextColor
, UiMenuBgColor
);
340 /* Simply left-align it */
341 MenuLineText
[0] = '\0';
342 strcat(MenuLineText
, " ");
344 /* Now append the text string */
345 if (MenuInfo
->MenuItemList
[MenuItemNumber
])
346 strcat(MenuLineText
, MenuInfo
->MenuItemList
[MenuItemNumber
]);
348 /* If it is a separator */
349 if (MenuInfo
->MenuItemList
[MenuItemNumber
] == NULL
)
351 /* Make it a separator line and use menu colors */
352 memset(MenuLineText
, 0, 80);
353 memset(MenuLineText
, 0xC4, (MenuInfo
->Right
- MenuInfo
->Left
- 1));
354 Attribute
= ATTR(UiMenuFgColor
, UiMenuBgColor
);
356 else if (MenuItemNumber
== MenuInfo
->SelectedMenuItem
)
358 /* If this is the selected item, use the selected colors */
359 Attribute
= ATTR(UiSelectedTextColor
, UiSelectedTextBgColor
);
363 UiDrawText(MenuInfo
->Left
+ 1,
364 MenuInfo
->Top
+ 1 + MenuItemNumber
,
370 UiDrawMenu(IN PUI_MENU_INFO MenuInfo
)
374 /* No GUI status bar text, just minimal text. Show the menu header. */
377 MenuInfo
->MenuHeader
,
378 ATTR(UiMenuFgColor
, UiMenuBgColor
));
380 /* Now tell the user how to choose */
382 MenuInfo
->Bottom
+ 1,
383 "Use \x18 and \x19 to move the highlight to your choice.",
384 ATTR(UiMenuFgColor
, UiMenuBgColor
));
386 MenuInfo
->Bottom
+ 2,
387 "Press ENTER to choose.",
388 ATTR(UiMenuFgColor
, UiMenuBgColor
));
390 /* And show the menu footer */
393 MenuInfo
->MenuFooter
,
394 ATTR(UiMenuFgColor
, UiMenuBgColor
));
396 /* Draw the menu box */
397 UiDrawMenuBox(MenuInfo
);
399 /* Draw each line of the menu */
400 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
402 UiDrawMenuItem(MenuInfo
, i
);
405 /* Display the boot options if needed */
406 if (MenuInfo
->ShowBootOptions
)
408 DisplayBootTimeOptions();
414 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo
,
415 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
418 ULONG Selected
, Count
;
420 /* Check for a keypress */
423 /* Check if the timeout is not already complete */
424 if (MenuInfo
->MenuTimeRemaining
!= -1)
426 /* Cancel it and remove it */
427 MenuInfo
->MenuTimeRemaining
= -1;
428 UiDrawMenuBox(MenuInfo
);
432 KeyEvent
= MachConsGetCh();
434 /* Is it extended? Then get the extended key */
435 if (!KeyEvent
) KeyEvent
= MachConsGetCh();
438 * Call the supplied key filter callback function to see
439 * if it is going to handle this keypress.
441 if ((KeyPressFilter
) && (KeyPressFilter(KeyEvent
)))
443 /* It processed the key character, so redraw and exit */
444 UiDrawMenu(MenuInfo
);
448 /* Process the key */
449 if ((KeyEvent
== KEY_UP
) || (KeyEvent
== KEY_DOWN
) ||
450 (KeyEvent
== KEY_HOME
) || (KeyEvent
== KEY_END
))
452 /* Get the current selected item and count */
453 Selected
= MenuInfo
->SelectedMenuItem
;
454 Count
= MenuInfo
->MenuItemCount
- 1;
456 /* Check the key and change the selected menu item */
457 if ((KeyEvent
== KEY_UP
) && (Selected
> 0))
459 /* Deselect previous item and go up */
460 MenuInfo
->SelectedMenuItem
--;
461 UiDrawMenuItem(MenuInfo
, Selected
);
464 // Skip past any separators
465 if ((Selected
> 0) &&
466 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
468 MenuInfo
->SelectedMenuItem
--;
471 else if ( ((KeyEvent
== KEY_UP
) && (Selected
== 0)) ||
472 (KeyEvent
== KEY_END
) )
475 MenuInfo
->SelectedMenuItem
= Count
;
476 UiDrawMenuItem(MenuInfo
, Selected
);
478 else if ((KeyEvent
== KEY_DOWN
) && (Selected
< Count
))
480 /* Deselect previous item and go down */
481 MenuInfo
->SelectedMenuItem
++;
482 UiDrawMenuItem(MenuInfo
, Selected
);
485 // Skip past any separators
486 if ((Selected
< Count
) &&
487 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
489 MenuInfo
->SelectedMenuItem
++;
492 else if ( ((KeyEvent
== KEY_DOWN
) && (Selected
== Count
)) ||
493 (KeyEvent
== KEY_HOME
) )
495 /* Go to the beginning */
496 MenuInfo
->SelectedMenuItem
= 0;
497 UiDrawMenuItem(MenuInfo
, Selected
);
500 /* Select new item and update video buffer */
501 UiDrawMenuItem(MenuInfo
, MenuInfo
->SelectedMenuItem
);
505 /* Return the pressed key */
511 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo
)
513 ULONG i
, Width
= 0, Height
, Length
;
515 /* Height is the menu item count plus 2 (top border & bottom border) */
516 Height
= MenuInfo
->MenuItemCount
+ 2;
517 Height
-= 1; // Height is zero-based
519 /* Loop every item */
520 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
522 /* Get the string length and make it become the new width if necessary */
523 if (MenuInfo
->MenuItemList
[i
])
525 Length
= (ULONG
)strlen(MenuInfo
->MenuItemList
[i
]);
526 if (Length
> Width
) Width
= Length
;
530 /* Allow room for left & right borders, plus 8 spaces on each side */
533 /* Put the menu in the default left-corner position */
537 /* The other margins are the same */
538 MenuInfo
->Right
= (MenuInfo
->Left
) + Width
;
539 MenuInfo
->Bottom
= (MenuInfo
->Top
) + Height
;
543 UiDisplayMenu(IN PCSTR MenuHeader
,
545 IN BOOLEAN ShowBootOptions
,
546 IN PCSTR MenuItemList
[],
547 IN ULONG MenuItemCount
,
548 IN ULONG DefaultMenuItem
,
550 OUT PULONG SelectedMenuItem
,
551 IN BOOLEAN CanEscape
,
552 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
554 UI_MENU_INFO MenuInformation
;
555 ULONG LastClockSecond
;
556 ULONG CurrentClockSecond
;
560 * Before taking any default action if there is no timeout,
561 * check whether the supplied key filter callback function
562 * may handle a specific user keypress. If it does, the
563 * timeout is cancelled.
565 if (!MenuTimeOut
&& KeyPressFilter
&& MachConsKbHit())
568 KeyPress
= MachConsGetCh();
570 /* Is it extended? Then get the extended key */
571 if (!KeyPress
) KeyPress
= MachConsGetCh();
574 * Call the supplied key filter callback function to see
575 * if it is going to handle this keypress.
577 if (KeyPressFilter(KeyPress
))
579 /* It processed the key character, cancel the timeout */
584 /* Check if there's no timeout */
587 /* Return the default selected item */
588 if (SelectedMenuItem
) *SelectedMenuItem
= DefaultMenuItem
;
592 /* Setup the MENU_INFO structure */
593 MenuInformation
.MenuHeader
= MenuHeader
;
594 MenuInformation
.MenuFooter
= MenuFooter
;
595 MenuInformation
.ShowBootOptions
= ShowBootOptions
;
596 MenuInformation
.MenuItemList
= MenuItemList
;
597 MenuInformation
.MenuItemCount
= MenuItemCount
;
598 MenuInformation
.MenuTimeRemaining
= MenuTimeOut
;
599 MenuInformation
.SelectedMenuItem
= DefaultMenuItem
;
601 /* Calculate the size of the menu box */
602 UiCalcMenuBoxSize(&MenuInformation
);
605 UiDrawMenu(&MenuInformation
);
607 /* Get the current second of time */
608 LastClockSecond
= ArcGetTime()->Second
;
613 /* Process key presses */
614 KeyPress
= UiProcessMenuKeyboardEvent(&MenuInformation
,
617 /* Check for ENTER or ESC */
618 if (KeyPress
== KEY_ENTER
) break;
619 if (CanEscape
&& KeyPress
== KEY_ESC
) return FALSE
;
621 /* Check if there is a countdown */
622 if (MenuInformation
.MenuTimeRemaining
> 0)
624 /* Get the updated time, seconds only */
625 CurrentClockSecond
= ArcGetTime()->Second
;
627 /* Check if more then a second has now elapsed */
628 if (CurrentClockSecond
!= LastClockSecond
)
630 /* Update the time information */
631 LastClockSecond
= CurrentClockSecond
;
632 MenuInformation
.MenuTimeRemaining
--;
634 /* Update the menu */
635 UiDrawMenuBox(&MenuInformation
);
638 else if (MenuInformation
.MenuTimeRemaining
== 0)
640 /* A time out occurred, exit this loop and return default OS */
645 /* Return the selected item */
646 if (SelectedMenuItem
) *SelectedMenuItem
= MenuInformation
.SelectedMenuItem
;