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 (MenuInfo
->MenuItemList
[i
] == NULL
)
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 if (MenuInfo
->MenuItemList
[MenuItemNumber
])
329 strcat(MenuLineText
, MenuInfo
->MenuItemList
[MenuItemNumber
]);
331 /* If it is a separator */
332 if (MenuInfo
->MenuItemList
[MenuItemNumber
] == NULL
)
334 /* Make it a separator line and use menu colors */
335 memset(MenuLineText
, 0, 80);
336 memset(MenuLineText
, 0xC4, (MenuInfo
->Right
- MenuInfo
->Left
- 1));
337 Attribute
= ATTR(UiMenuFgColor
, UiMenuBgColor
);
339 else if (MenuItemNumber
== MenuInfo
->SelectedMenuItem
)
341 /* If this is the selected item, use the selected colors */
342 Attribute
= ATTR(UiSelectedTextColor
, UiSelectedTextBgColor
);
346 UiDrawText(MenuInfo
->Left
+ 1,
347 MenuInfo
->Top
+ 1 + MenuItemNumber
,
353 UiDrawMenu(IN PUI_MENU_INFO MenuInfo
)
357 /* No GUI status bar text, just minimal text. Show the menu header. */
360 MenuInfo
->MenuHeader
,
361 ATTR(UiMenuFgColor
, UiMenuBgColor
));
363 /* Now tell the user how to choose */
365 MenuInfo
->Bottom
+ 1,
366 "Use \x18 and \x19 to move the highlight to your choice.",
367 ATTR(UiMenuFgColor
, UiMenuBgColor
));
369 MenuInfo
->Bottom
+ 2,
370 "Press ENTER to choose.",
371 ATTR(UiMenuFgColor
, UiMenuBgColor
));
373 /* And show the menu footer */
376 MenuInfo
->MenuFooter
,
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
);
388 /* Display the boot options if needed */
389 if (MenuInfo
->ShowBootOptions
)
391 DisplayBootTimeOptions();
397 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo
,
398 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
400 ULONG KeyEvent
= 0, Selected
, Count
;
402 /* Check for a keypress */
405 /* Check if the timeout is not already complete */
406 if (MenuInfo
->MenuTimeRemaining
!= -1)
409 // Cancel it and remove it
411 MenuInfo
->MenuTimeRemaining
= -1;
412 UiDrawMenuBox(MenuInfo
);
416 KeyEvent
= MachConsGetCh();
418 /* Is it extended? Then get the extended key */
419 if (!KeyEvent
) KeyEvent
= MachConsGetCh();
421 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
422 if ((KeyPressFilter
) && (KeyPressFilter(KeyEvent
)))
424 /* It processed the key character, so redraw and exit */
425 UiDrawMenu(MenuInfo
);
429 /* Process the key */
430 if ((KeyEvent
== KEY_UP
) || (KeyEvent
== KEY_DOWN
))
432 /* Get the current selected item and count */
433 Selected
= MenuInfo
->SelectedMenuItem
;
434 Count
= MenuInfo
->MenuItemCount
- 1;
436 /* Check if this was a key up and there's a selected menu item */
437 if ((KeyEvent
== KEY_UP
) && (Selected
))
439 /* Update the menu (Deselect previous item) */
440 MenuInfo
->SelectedMenuItem
--;
441 UiDrawMenuItem(MenuInfo
, Selected
);
444 /* Skip past any separators */
446 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
448 MenuInfo
->SelectedMenuItem
--;
451 else if ((KeyEvent
== KEY_DOWN
) && (Selected
< Count
))
453 /* Update the menu (deselect previous item) */
454 MenuInfo
->SelectedMenuItem
++;
455 UiDrawMenuItem(MenuInfo
, Selected
);
458 /* Skip past any separators */
459 if ((Selected
< Count
) &&
460 (MenuInfo
->MenuItemList
[Selected
] == NULL
))
462 MenuInfo
->SelectedMenuItem
++;
466 /* Select new item and update video buffer */
467 UiDrawMenuItem(MenuInfo
, MenuInfo
->SelectedMenuItem
);
471 /* Return the pressed key */
477 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo
)
479 ULONG i
, Width
= 0, Height
, Length
;
481 /* Height is the menu item count plus 2 (top border & bottom border) */
482 Height
= MenuInfo
->MenuItemCount
+ 2;
483 Height
-= 1; // Height is zero-based
485 /* Loop every item */
486 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
488 /* Get the string length and make it become the new width if necessary */
489 if (MenuInfo
->MenuItemList
[i
])
491 Length
= (ULONG
)strlen(MenuInfo
->MenuItemList
[i
]);
492 if (Length
> Width
) Width
= Length
;
496 /* Allow room for left & right borders, plus 8 spaces on each side */
499 /* Put the menu in the default left-corner position */
503 /* The other margins are the same */
504 MenuInfo
->Right
= (MenuInfo
->Left
) + Width
;
505 MenuInfo
->Bottom
= (MenuInfo
->Top
) + Height
;
509 UiDisplayMenu(IN PCSTR MenuHeader
,
511 IN BOOLEAN ShowBootOptions
,
512 IN PCSTR MenuItemList
[],
513 IN ULONG MenuItemCount
,
514 IN ULONG DefaultMenuItem
,
516 OUT PULONG SelectedMenuItem
,
517 IN BOOLEAN CanEscape
,
518 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
520 UI_MENU_INFO MenuInformation
;
521 ULONG LastClockSecond
;
522 ULONG CurrentClockSecond
;
525 /* Check if there's no timeout */
528 /* Return the default selected item */
529 if (SelectedMenuItem
) *SelectedMenuItem
= DefaultMenuItem
;
533 /* Setup the MENU_INFO structure */
534 MenuInformation
.MenuHeader
= MenuHeader
;
535 MenuInformation
.MenuFooter
= MenuFooter
;
536 MenuInformation
.ShowBootOptions
= ShowBootOptions
;
537 MenuInformation
.MenuItemList
= MenuItemList
;
538 MenuInformation
.MenuItemCount
= MenuItemCount
;
539 MenuInformation
.MenuTimeRemaining
= MenuTimeOut
;
540 MenuInformation
.SelectedMenuItem
= DefaultMenuItem
;
542 /* Calculate the size of the menu box */
543 UiCalcMenuBoxSize(&MenuInformation
);
546 UiDrawMenu(&MenuInformation
);
548 /* Get the current second of time */
549 LastClockSecond
= ArcGetTime()->Second
;
554 /* Process key presses */
555 KeyPress
= UiProcessMenuKeyboardEvent(&MenuInformation
,
558 /* Check for ENTER or ESC */
559 if (KeyPress
== KEY_ENTER
) break;
560 if (CanEscape
&& KeyPress
== KEY_ESC
) return FALSE
;
562 /* Check if there is a countdown */
563 if (MenuInformation
.MenuTimeRemaining
> 0)
565 /* Get the updated time, seconds only */
566 CurrentClockSecond
= ArcGetTime()->Second
;
568 /* Check if more then a second has now elapsed */
569 if (CurrentClockSecond
!= LastClockSecond
)
571 /* Update the time information */
572 LastClockSecond
= CurrentClockSecond
;
573 MenuInformation
.MenuTimeRemaining
--;
575 /* Update the menu */
576 UiDrawMenuBox(&MenuInformation
);
579 else if (MenuInformation
.MenuTimeRemaining
== 0)
581 /* A time out occurred, exit this loop and return default OS */
586 /* Return the selected item */
587 if (SelectedMenuItem
) *SelectedMenuItem
= MenuInformation
.SelectedMenuItem
;