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 UiDrawCenteredText(IN ULONG Left
,
103 ULONG TextLength
, BoxWidth
, BoxHeight
, LineBreakCount
, Index
, LastIndex
;
104 ULONG RealLeft
, RealTop
, X
, Y
;
107 /* Query text length */
108 TextLength
= strlen(TextString
);
110 /* Count the new lines and the box width */
114 for (Index
=0; Index
< TextLength
; Index
++)
116 /* Scan for new lines */
117 if (TextString
[Index
] == '\n')
119 /* Remember the new line */
125 /* Check for new larger box width */
126 if ((Index
- LastIndex
) > BoxWidth
)
129 BoxWidth
= (Index
- LastIndex
);
134 /* Base the box height on the number of lines */
135 BoxHeight
= LineBreakCount
+ 1;
137 /* Create the centered coordinates */
138 RealLeft
= (((Right
- Left
) - BoxWidth
) / 2) + Left
;
139 RealTop
= (((Bottom
- Top
) - BoxHeight
) / 2) + Top
;
141 /* Now go for a second scan */
143 for (Index
=0; Index
< TextLength
; Index
++)
145 /* Look for new lines again */
146 if (TextString
[Index
] == '\n')
148 /* Update where the text should start */
154 /* We've got a line of text to print, do it */
155 X
= RealLeft
+ LastIndex
;
158 Temp
[0] = TextString
[Index
];
160 UiDrawText(X
, Y
, Temp
, Attr
);
166 UiDrawStatusText(IN PCSTR StatusText
)
172 UiInfoBox(IN PCSTR MessageText
)
174 TuiPrintf(MessageText
);
178 UiMessageBox(IN PCSTR MessageText
)
180 TuiPrintf(MessageText
);
184 UiMessageBoxCritical(IN PCSTR MessageText
)
186 TuiPrintf(MessageText
);
190 UiDrawProgressBarCenter(IN ULONG Position
,
192 IN PCHAR ProgressText
)
194 ULONG Left
, Top
, Right
, Bottom
, Width
, Height
;
196 /* Build the coordinates and sizes */
198 Width
= UiScreenWidth
;
200 Right
= (Left
+ Width
) - 1;
201 Top
= UiScreenHeight
- Height
- 4;
202 Bottom
= Top
+ Height
+ 1;
204 /* Draw the progress bar */
205 UiDrawProgressBar(Left
, Top
, Right
, Bottom
, Position
, Range
, ProgressText
);
209 UiDrawProgressBar(IN ULONG Left
,
215 IN PCHAR ProgressText
)
217 ULONG i
, ProgressBarWidth
;
219 /* Calculate the width of the bar proper */
220 ProgressBarWidth
= (Right
- Left
) - 3;
222 /* First make sure the progress bar text fits */
223 UiTruncateStringEllipsis(ProgressText
, ProgressBarWidth
- 4);
224 if (Position
> Range
) Position
= Range
;
226 /* Draw the "Loading..." text */
227 UiDrawCenteredText(Left
+ 2, Top
+ 1, Right
- 2, Top
+ 1, ProgressText
, ATTR(7, 0));
229 /* Draw the percent complete */
230 for (i
= 0; i
< (Position
* ProgressBarWidth
) / Range
; i
++)
232 /* Use the fill character */
233 UiDrawText(Left
+ 2 + i
, Top
+ 2, "\xDB", ATTR(UiTextColor
, UiMenuBgColor
));
238 UiShowMessageBoxesInSection(IN PCSTR SectionName
)
244 UiTruncateStringEllipsis(IN PCHAR StringText
,
247 /* If it's too large, just add some ellipsis past the maximum */
248 if (strlen(StringText
) > MaxChars
) strcpy(&StringText
[MaxChars
- 3], "...");
253 UiDrawMenuBox(IN PUI_MENU_INFO MenuInfo
)
255 CHAR MenuLineText
[80], TempString
[80];
258 /* If there is a timeout draw the time remaining */
259 if (MenuInfo
->MenuTimeRemaining
>= 0)
261 /* Copy the integral time text string, and remove the last 2 chars */
262 strcpy(TempString
, UiTimeText
);
263 i
= strlen(TempString
);
264 TempString
[i
- 2] = 0;
266 /* Display the first part of the string and the remaining time */
267 strcpy(MenuLineText
, TempString
);
268 _itoa(MenuInfo
->MenuTimeRemaining
, TempString
, 10);
269 strcat(MenuLineText
, TempString
);
271 /* Add the last 2 chars */
272 strcat(MenuLineText
, &UiTimeText
[i
- 2]);
274 /* Display under the menu directly */
276 MenuInfo
->Bottom
+ 4,
278 ATTR(UiMenuFgColor
, UiMenuBgColor
));
282 /* Erase the timeout string with spaces, and 0-terminate for sure */
283 for (i
=0; i
<sizeof(MenuLineText
)-1; i
++)
285 MenuLineText
[i
] = ' ';
287 MenuLineText
[sizeof(MenuLineText
)-1] = 0;
289 /* Draw this "empty" string to erase */
291 MenuInfo
->Bottom
+ 4,
293 ATTR(UiMenuFgColor
, UiMenuBgColor
));
297 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
299 /* Check if it's a separator */
300 if (!(_stricmp(MenuInfo
->MenuItemList
[i
], "SEPARATOR")))
302 /* Draw the separator line */
303 UiDrawText(MenuInfo
->Left
,
304 MenuInfo
->Top
+ i
+ 1,
306 ATTR(UiMenuFgColor
, UiMenuBgColor
));
307 UiDrawText(MenuInfo
->Right
,
308 MenuInfo
->Top
+ i
+ 1,
310 ATTR(UiMenuFgColor
, UiMenuBgColor
));
317 UiDrawMenuItem(IN PUI_MENU_INFO MenuInfo
,
318 IN ULONG MenuItemNumber
)
320 CHAR MenuLineText
[80];
321 UCHAR Attribute
= ATTR(UiTextColor
, UiMenuBgColor
);
323 /* Simply left-align it */
324 MenuLineText
[0] = '\0';
325 strcat(MenuLineText
, " ");
327 /* Now append the text string */
328 strcat(MenuLineText
, MenuInfo
->MenuItemList
[MenuItemNumber
]);
330 /* If it is a separator */
331 if (!(_stricmp(MenuInfo
->MenuItemList
[MenuItemNumber
], "SEPARATOR")))
333 /* Make it a separator line and use menu colors */
334 memset(MenuLineText
, 0, 80);
335 memset(MenuLineText
, 0xC4, (MenuInfo
->Right
- MenuInfo
->Left
- 1));
336 Attribute
= ATTR(UiMenuFgColor
, UiMenuBgColor
);
338 else if (MenuItemNumber
== MenuInfo
->SelectedMenuItem
)
340 /* If this is the selected item, use the selected colors */
341 Attribute
= ATTR(UiSelectedTextColor
, UiSelectedTextBgColor
);
345 UiDrawText(MenuInfo
->Left
+ 1,
346 MenuInfo
->Top
+ 1 + MenuItemNumber
,
352 UiDrawMenu(IN PUI_MENU_INFO MenuInfo
)
356 /* No GUI status bar text, just minimal text. first to tell the user to choose */
360 ATTR(UiMenuFgColor
, UiMenuBgColor
));
362 /* Now tell him how to choose */
364 MenuInfo
->Bottom
+ 1,
365 "Use \x18 and \x19 to move the highlight to your choice.",
366 ATTR(UiMenuFgColor
, UiMenuBgColor
));
368 MenuInfo
->Bottom
+ 2,
369 "Press ENTER to choose.",
370 ATTR(UiMenuFgColor
, UiMenuBgColor
));
372 /* And offer F8 options */
375 "For troubleshooting and advanced startup options for "
376 "ReactOS, press F8.",
377 ATTR(UiMenuFgColor
, UiMenuBgColor
));
379 /* Draw the menu box */
380 UiDrawMenuBox(MenuInfo
);
382 /* Draw each line of the menu */
383 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
385 UiDrawMenuItem(MenuInfo
, i
);
391 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo
,
392 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
394 ULONG KeyEvent
= 0, Selected
, Count
;
396 /* Check for a keypress */
399 /* Check if the timeout is not already complete */
400 if (MenuInfo
->MenuTimeRemaining
!= -1)
403 // Cancel it and remove it
405 MenuInfo
->MenuTimeRemaining
= -1;
406 UiDrawMenuBox(MenuInfo
);
410 KeyEvent
= MachConsGetCh();
412 /* Is it extended? Then get the extended key */
413 if (!KeyEvent
) KeyEvent
= MachConsGetCh();
415 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
416 if ((KeyPressFilter
) && (KeyPressFilter(KeyEvent
)))
418 /* It processed the key character, so redraw and exit */
419 UiDrawMenu(MenuInfo
);
423 /* Process the key */
424 if ((KeyEvent
== KEY_UP
) || (KeyEvent
== KEY_DOWN
))
426 /* Get the current selected item and count */
427 Selected
= MenuInfo
->SelectedMenuItem
;
428 Count
= MenuInfo
->MenuItemCount
- 1;
430 /* Check if this was a key up and there's a selected menu item */
431 if ((KeyEvent
== KEY_UP
) && (Selected
))
433 /* Update the menu (Deselect previous item) */
434 MenuInfo
->SelectedMenuItem
--;
435 UiDrawMenuItem(MenuInfo
, Selected
);
438 /* Skip past any separators */
440 !(_stricmp(MenuInfo
->MenuItemList
[Selected
], "SEPARATOR")))
442 MenuInfo
->SelectedMenuItem
--;
445 else if ((KeyEvent
== KEY_DOWN
) && (Selected
< Count
))
447 /* Update the menu (deselect previous item) */
448 MenuInfo
->SelectedMenuItem
++;
449 UiDrawMenuItem(MenuInfo
, Selected
);
452 /* Skip past any separators */
453 if ((Selected
< Count
) &&
454 !(_stricmp(MenuInfo
->MenuItemList
[Selected
], "SEPARATOR")))
456 MenuInfo
->SelectedMenuItem
++;
460 /* Select new item and update video buffer */
461 UiDrawMenuItem(MenuInfo
, MenuInfo
->SelectedMenuItem
);
465 /* Return the pressed key */
471 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo
)
473 ULONG i
, Width
= 0, Height
, Length
;
475 /* Height is the menu item count plus 2 (top border & bottom border) */
476 Height
= MenuInfo
->MenuItemCount
+ 2;
477 Height
-= 1; // Height is zero-based
479 /* Loop every item */
480 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
482 /* Get the string length and make it become the new width if necessary */
483 Length
= strlen(MenuInfo
->MenuItemList
[i
]);
484 if (Length
> Width
) Width
= Length
;
487 /* Allow room for left & right borders, plus 8 spaces on each side */
490 /* Put the menu in the default left-corner position */
494 /* The other margins are the same */
495 MenuInfo
->Right
= (MenuInfo
->Left
) + Width
;
496 MenuInfo
->Bottom
= (MenuInfo
->Top
) + Height
;
500 UiDisplayMenu(IN PCSTR MenuTitle
,
501 IN PCSTR MenuItemList
[],
502 IN ULONG MenuItemCount
,
503 IN ULONG DefaultMenuItem
,
505 OUT PULONG SelectedMenuItem
,
506 IN BOOLEAN CanEscape
,
507 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
509 UI_MENU_INFO MenuInformation
;
510 ULONG LastClockSecond
;
511 ULONG CurrentClockSecond
;
514 /* Check if there's no timeout */
517 /* Return the default selected item */
518 if (SelectedMenuItem
) *SelectedMenuItem
= DefaultMenuItem
;
522 /* Setup the MENU_INFO structure */
523 MenuInformation
.MenuTitle
= MenuTitle
;
524 MenuInformation
.MenuItemList
= MenuItemList
;
525 MenuInformation
.MenuItemCount
= MenuItemCount
;
526 MenuInformation
.MenuTimeRemaining
= MenuTimeOut
;
527 MenuInformation
.SelectedMenuItem
= DefaultMenuItem
;
529 /* Calculate the size of the menu box */
530 UiCalcMenuBoxSize(&MenuInformation
);
533 UiDrawMenu(&MenuInformation
);
535 /* Get the current second of time */
536 LastClockSecond
= ArcGetTime()->Second
;
541 /* Process key presses */
542 KeyPress
= UiProcessMenuKeyboardEvent(&MenuInformation
,
545 /* Check for ENTER or ESC */
546 if (KeyPress
== KEY_ENTER
) break;
547 if (CanEscape
&& KeyPress
== KEY_ESC
) return FALSE
;
549 /* Check if there is a countdown */
550 if (MenuInformation
.MenuTimeRemaining
)
552 /* Get the updated time, seconds only */
553 CurrentClockSecond
= ArcGetTime()->Second
;
555 /* Check if more then a second has now elapsed */
556 if (CurrentClockSecond
!= LastClockSecond
)
558 /* Update the time information */
559 LastClockSecond
= CurrentClockSecond
;
560 MenuInformation
.MenuTimeRemaining
--;
562 /* Update the menu */
563 UiDrawMenuBox(&MenuInformation
);
568 /* A time out occurred, exit this loop and return default OS */
573 /* Return the selected item */
574 if (SelectedMenuItem
) *SelectedMenuItem
= MenuInformation
.SelectedMenuItem
;