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
+ 3,
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
+ 3,
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 */
359 "Please select the operating system to start:",
360 ATTR(UiMenuFgColor
, UiMenuBgColor
));
362 /* Now tell him how to choose */
364 MenuInfo
->Bottom
+ 1,
365 "Use the up and down arrow keys to move the highlight to "
367 ATTR(UiMenuFgColor
, UiMenuBgColor
));
369 MenuInfo
->Bottom
+ 2,
370 "Press ENTER to choose.",
371 ATTR(UiMenuFgColor
, UiMenuBgColor
));
373 /* And offer F8 options */
376 "For troubleshooting and advanced startup options for "
377 "ReactOS, press F8.",
378 ATTR(UiMenuFgColor
, UiMenuBgColor
));
380 /* Draw the menu box */
381 UiDrawMenuBox(MenuInfo
);
383 /* Draw each line of the menu */
384 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++) UiDrawMenuItem(MenuInfo
, i
);
389 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo
,
390 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
392 ULONG KeyEvent
= 0, Selected
, Count
;
394 /* Check for a keypress */
397 /* Check if the timeout is not already complete */
398 if (MenuInfo
->MenuTimeRemaining
!= -1)
401 // Cancel it and remove it
403 MenuInfo
->MenuTimeRemaining
= -1;
404 UiDrawMenuBox(MenuInfo
);
408 KeyEvent
= MachConsGetCh();
410 /* Is it extended? Then get the extended key */
411 if (!KeyEvent
) KeyEvent
= MachConsGetCh();
413 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
414 if ((KeyPressFilter
) && (KeyPressFilter(KeyEvent
)))
416 /* It processed the key character, so redraw and exit */
417 UiDrawMenu(MenuInfo
);
421 /* Process the key */
422 if ((KeyEvent
== KEY_UP
) || (KeyEvent
== KEY_DOWN
))
424 /* Get the current selected item and count */
425 Selected
= MenuInfo
->SelectedMenuItem
;
426 Count
= MenuInfo
->MenuItemCount
- 1;
428 /* Check if this was a key up and there's a selected menu item */
429 if ((KeyEvent
== KEY_UP
) && (Selected
))
431 /* Update the menu (Deselect previous item) */
432 MenuInfo
->SelectedMenuItem
--;
433 UiDrawMenuItem(MenuInfo
, Selected
);
436 /* Skip past any separators */
438 !(_stricmp(MenuInfo
->MenuItemList
[Selected
], "SEPARATOR")))
440 MenuInfo
->SelectedMenuItem
--;
443 else if ((KeyEvent
== KEY_DOWN
) && (Selected
< Count
))
445 /* Update the menu (deselect previous item) */
446 MenuInfo
->SelectedMenuItem
++;
447 UiDrawMenuItem(MenuInfo
, Selected
);
450 /* Skip past any separators */
451 if ((Selected
< Count
) &&
452 !(_stricmp(MenuInfo
->MenuItemList
[Selected
], "SEPARATOR")))
454 MenuInfo
->SelectedMenuItem
++;
458 /* Select new item and update video buffer */
459 UiDrawMenuItem(MenuInfo
, MenuInfo
->SelectedMenuItem
);
463 /* Return the pressed key */
469 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo
)
471 ULONG i
, Width
= 0, Height
, Length
;
473 /* Height is the menu item count plus 2 (top border & bottom border) */
474 Height
= MenuInfo
->MenuItemCount
+ 2;
475 Height
-= 1; // Height is zero-based
477 /* Loop every item */
478 for (i
= 0; i
< MenuInfo
->MenuItemCount
; i
++)
480 /* Get the string length and make it become the new width if necessary */
481 Length
= strlen(MenuInfo
->MenuItemList
[i
]);
482 if (Length
> Width
) Width
= Length
;
485 /* Allow room for left & right borders, plus 8 spaces on each side */
488 /* Put the menu in the default left-corner position */
492 /* The other margins are the same */
493 MenuInfo
->Right
= (MenuInfo
->Left
) + Width
;
494 MenuInfo
->Bottom
= (MenuInfo
->Top
) + Height
;
498 UiDisplayMenu(IN PCSTR MenuItemList
[],
499 IN ULONG MenuItemCount
,
500 IN ULONG DefaultMenuItem
,
502 OUT PULONG SelectedMenuItem
,
503 IN BOOLEAN CanEscape
,
504 IN UiMenuKeyPressFilterCallback KeyPressFilter
)
506 UI_MENU_INFO MenuInformation
;
507 ULONG LastClockSecond
;
508 ULONG CurrentClockSecond
;
511 /* Check if there's no timeout */
514 /* Return the default selected item */
515 if (SelectedMenuItem
) *SelectedMenuItem
= DefaultMenuItem
;
519 /* Setup the MENU_INFO structure */
520 MenuInformation
.MenuItemList
= MenuItemList
;
521 MenuInformation
.MenuItemCount
= MenuItemCount
;
522 MenuInformation
.MenuTimeRemaining
= MenuTimeOut
;
523 MenuInformation
.SelectedMenuItem
= DefaultMenuItem
;
525 /* Calculate the size of the menu box */
526 UiCalcMenuBoxSize(&MenuInformation
);
529 UiDrawMenu(&MenuInformation
);
531 /* Get the current second of time */
532 LastClockSecond
= ArcGetTime()->Second
;
537 /* Process key presses */
538 KeyPress
= UiProcessMenuKeyboardEvent(&MenuInformation
,
541 /* Check for ENTER or ESC */
542 if (KeyPress
== KEY_ENTER
) break;
543 if (CanEscape
&& KeyPress
== KEY_ESC
) return FALSE
;
545 /* Check if there is a countdown */
546 if (MenuInformation
.MenuTimeRemaining
)
548 /* Get the updated time, seconds only */
549 CurrentClockSecond
= ArcGetTime()->Second
;
551 /* Check if more then a second has now elapsed */
552 if (CurrentClockSecond
!= LastClockSecond
)
554 /* Update the time information */
555 LastClockSecond
= CurrentClockSecond
;
556 MenuInformation
.MenuTimeRemaining
--;
558 /* Update the menu */
559 UiDrawMenuBox(&MenuInformation
);
564 /* A time out occurred, exit this loop and return default OS */
569 /* Return the selected item */
570 if (SelectedMenuItem
) *SelectedMenuItem
= MenuInformation
.SelectedMenuItem
;