2a9db377bd6da71c06a4607f4898711ca1abdf48
[reactos.git] / boot / freeldr / freeldr / ui / directui.c
1 /*
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
7 */
8 #ifdef _M_ARM
9
10 /* INCLUDES *******************************************************************/
11
12 #include <freeldr.h>
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 /* FUNCTIONS ******************************************************************/
18
19 ULONG UiScreenWidth;
20 ULONG UiScreenHeight;
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: ";
27
28 INT
29 TuiPrintf(const char *Format,
30 ...)
31 {
32 int i;
33 int Length;
34 va_list ap;
35 CHAR Buffer[512];
36
37 va_start(ap, Format);
38 Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
39 va_end(ap);
40
41 if (Length == -1) Length = sizeof(Buffer);
42
43 for (i = 0; i < Length; i++)
44 {
45 MachConsPutChar(Buffer[i]);
46 }
47
48 return Length;
49 }
50
51 BOOLEAN
52 UiInitialize(IN BOOLEAN ShowGui)
53 {
54 ULONG Depth;
55
56 /* Nothing to do */
57 if (!ShowGui) return TRUE;
58
59 /* Set mode and query size */
60 MachVideoSetDisplayMode(NULL, TRUE);
61 MachVideoGetDisplaySize(&UiScreenWidth, &UiScreenHeight, &Depth);
62 return TRUE;
63 }
64
65 VOID
66 UiUnInitialize(IN PCSTR BootText)
67 {
68 /* Nothing to do */
69 return;
70 }
71
72 VOID
73 UiDrawBackdrop(VOID)
74 {
75 /* Clear the screen */
76 MachVideoClearScreen(ATTR(COLOR_WHITE, COLOR_BLACK));
77 }
78
79 VOID
80 UiDrawText(IN ULONG X,
81 IN ULONG Y,
82 IN PCSTR Text,
83 IN UCHAR Attr)
84 {
85 ULONG i, j;
86
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++)
89 {
90 /* Write the character */
91 MachVideoPutChar(Text[j], Attr, i, Y);
92 }
93 }
94
95 VOID
96 UiDrawCenteredText(IN ULONG Left,
97 IN ULONG Top,
98 IN ULONG Right,
99 IN ULONG Bottom,
100 IN PCSTR TextString,
101 IN UCHAR Attr)
102 {
103 ULONG TextLength, BoxWidth, BoxHeight, LineBreakCount, Index, LastIndex;
104 ULONG RealLeft, RealTop, X, Y;
105 CHAR Temp[2];
106
107 /* Query text length */
108 TextLength = strlen(TextString);
109
110 /* Count the new lines and the box width */
111 LineBreakCount = 0;
112 BoxWidth = 0;
113 LastIndex = 0;
114 for (Index=0; Index < TextLength; Index++)
115 {
116 /* Scan for new lines */
117 if (TextString[Index] == '\n')
118 {
119 /* Remember the new line */
120 LastIndex = Index;
121 LineBreakCount++;
122 }
123 else
124 {
125 /* Check for new larger box width */
126 if ((Index - LastIndex) > BoxWidth)
127 {
128 /* Update it */
129 BoxWidth = (Index - LastIndex);
130 }
131 }
132 }
133
134 /* Base the box height on the number of lines */
135 BoxHeight = LineBreakCount + 1;
136
137 /* Create the centered coordinates */
138 RealLeft = (((Right - Left) - BoxWidth) / 2) + Left;
139 RealTop = (((Bottom - Top) - BoxHeight) / 2) + Top;
140
141 /* Now go for a second scan */
142 LastIndex = 0;
143 for (Index=0; Index < TextLength; Index++)
144 {
145 /* Look for new lines again */
146 if (TextString[Index] == '\n')
147 {
148 /* Update where the text should start */
149 RealTop++;
150 LastIndex = 0;
151 }
152 else
153 {
154 /* We've got a line of text to print, do it */
155 X = RealLeft + LastIndex;
156 Y = RealTop;
157 LastIndex++;
158 Temp[0] = TextString[Index];
159 Temp[1] = 0;
160 UiDrawText(X, Y, Temp, Attr);
161 }
162 }
163 }
164
165 VOID
166 UiDrawStatusText(IN PCSTR StatusText)
167 {
168 return;
169 }
170
171 VOID
172 UiInfoBox(IN PCSTR MessageText)
173 {
174 TuiPrintf(MessageText);
175 }
176
177 VOID
178 UiMessageBox(IN PCSTR MessageText)
179 {
180 TuiPrintf(MessageText);
181 }
182
183 VOID
184 UiMessageBoxCritical(IN PCSTR MessageText)
185 {
186 TuiPrintf(MessageText);
187 }
188
189 VOID
190 UiDrawProgressBarCenter(IN ULONG Position,
191 IN ULONG Range,
192 IN PCHAR ProgressText)
193 {
194 ULONG Left, Top, Right, Bottom, Width, Height;
195
196 /* Build the coordinates and sizes */
197 Height = 2;
198 Width = UiScreenWidth;
199 Left = 0;
200 Right = (Left + Width) - 1;
201 Top = UiScreenHeight - Height - 4;
202 Bottom = Top + Height + 1;
203
204 /* Draw the progress bar */
205 UiDrawProgressBar(Left, Top, Right, Bottom, Position, Range, ProgressText);
206 }
207
208 VOID
209 UiDrawProgressBar(IN ULONG Left,
210 IN ULONG Top,
211 IN ULONG Right,
212 IN ULONG Bottom,
213 IN ULONG Position,
214 IN ULONG Range,
215 IN PCHAR ProgressText)
216 {
217 ULONG i, ProgressBarWidth;
218
219 /* Calculate the width of the bar proper */
220 ProgressBarWidth = (Right - Left) - 3;
221
222 /* First make sure the progress bar text fits */
223 UiTruncateStringEllipsis(ProgressText, ProgressBarWidth - 4);
224 if (Position > Range) Position = Range;
225
226 /* Draw the "Loading..." text */
227 UiDrawCenteredText(Left + 2, Top + 1, Right - 2, Top + 1, ProgressText, ATTR(7, 0));
228
229 /* Draw the percent complete */
230 for (i = 0; i < (Position * ProgressBarWidth) / Range; i++)
231 {
232 /* Use the fill character */
233 UiDrawText(Left + 2 + i, Top + 2, "\xDB", ATTR(UiTextColor, UiMenuBgColor));
234 }
235 }
236
237 VOID
238 UiShowMessageBoxesInSection(IN PCSTR SectionName)
239 {
240 return;
241 }
242
243 VOID
244 UiTruncateStringEllipsis(IN PCHAR StringText,
245 IN ULONG MaxChars)
246 {
247 /* If it's too large, just add some ellipsis past the maximum */
248 if (strlen(StringText) > MaxChars) strcpy(&StringText[MaxChars - 3], "...");
249 }
250
251 VOID
252 NTAPI
253 UiDrawMenuBox(IN PUI_MENU_INFO MenuInfo)
254 {
255 CHAR MenuLineText[80], TempString[80];
256 ULONG i;
257
258 /* If there is a timeout draw the time remaining */
259 if (MenuInfo->MenuTimeRemaining >= 0)
260 {
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;
265
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);
270
271 /* Add the last 2 chars */
272 strcat(MenuLineText, &UiTimeText[i - 2]);
273
274 /* Display under the menu directly */
275 UiDrawText(0,
276 MenuInfo->Bottom + 3,
277 MenuLineText,
278 ATTR(UiMenuFgColor, UiMenuBgColor));
279 }
280 else
281 {
282 /* Erase the timeout string with spaces, and 0-terminate for sure */
283 for (i=0; i<sizeof(MenuLineText)-1; i++)
284 {
285 MenuLineText[i] = ' ';
286 }
287 MenuLineText[sizeof(MenuLineText)-1] = 0;
288
289 /* Draw this "empty" string to erase */
290 UiDrawText(0,
291 MenuInfo->Bottom + 3,
292 MenuLineText,
293 ATTR(UiMenuFgColor, UiMenuBgColor));
294 }
295
296 /* Loop each item */
297 for (i = 0; i < MenuInfo->MenuItemCount; i++)
298 {
299 /* Check if it's a separator */
300 if (!(_stricmp(MenuInfo->MenuItemList[i], "SEPARATOR")))
301 {
302 /* Draw the separator line */
303 UiDrawText(MenuInfo->Left,
304 MenuInfo->Top + i + 1,
305 "\xC7",
306 ATTR(UiMenuFgColor, UiMenuBgColor));
307 UiDrawText(MenuInfo->Right,
308 MenuInfo->Top + i + 1,
309 "\xB6",
310 ATTR(UiMenuFgColor, UiMenuBgColor));
311 }
312 }
313 }
314
315 VOID
316 NTAPI
317 UiDrawMenuItem(IN PUI_MENU_INFO MenuInfo,
318 IN ULONG MenuItemNumber)
319 {
320 CHAR MenuLineText[80];
321 UCHAR Attribute = ATTR(UiTextColor, UiMenuBgColor);
322
323 /* Simply left-align it */
324 MenuLineText[0] = '\0';
325 strcat(MenuLineText, " ");
326
327 /* Now append the text string */
328 strcat(MenuLineText, MenuInfo->MenuItemList[MenuItemNumber]);
329
330 /* If it is a separator */
331 if (!(_stricmp(MenuInfo->MenuItemList[MenuItemNumber], "SEPARATOR")))
332 {
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);
337 }
338 else if (MenuItemNumber == MenuInfo->SelectedMenuItem)
339 {
340 /* If this is the selected item, use the selected colors */
341 Attribute = ATTR(UiSelectedTextColor, UiSelectedTextBgColor);
342 }
343
344 /* Draw the item */
345 UiDrawText(MenuInfo->Left + 1,
346 MenuInfo->Top + 1 + MenuItemNumber,
347 MenuLineText,
348 Attribute);
349 }
350
351 VOID
352 UiDrawMenu(IN PUI_MENU_INFO MenuInfo)
353 {
354 ULONG i;
355
356 /* No GUI status bar text, just minimal text. first to tell the user to choose */
357 UiDrawText(0,
358 MenuInfo->Top - 2,
359 "Please select the operating system to start:",
360 ATTR(UiMenuFgColor, UiMenuBgColor));
361
362 /* Now tell him how to choose */
363 UiDrawText(0,
364 MenuInfo->Bottom + 1,
365 "Use the up and down arrow keys to move the highlight to "
366 "your choice.",
367 ATTR(UiMenuFgColor, UiMenuBgColor));
368 UiDrawText(0,
369 MenuInfo->Bottom + 2,
370 "Press ENTER to choose.",
371 ATTR(UiMenuFgColor, UiMenuBgColor));
372
373 /* And offer F8 options */
374 UiDrawText(0,
375 UiScreenHeight - 4,
376 "For troubleshooting and advanced startup options for "
377 "ReactOS, press F8.",
378 ATTR(UiMenuFgColor, UiMenuBgColor));
379
380 /* Draw the menu box */
381 UiDrawMenuBox(MenuInfo);
382
383 /* Draw each line of the menu */
384 for (i = 0; i < MenuInfo->MenuItemCount; i++) UiDrawMenuItem(MenuInfo, i);
385 }
386
387 ULONG
388 NTAPI
389 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo,
390 IN UiMenuKeyPressFilterCallback KeyPressFilter)
391 {
392 ULONG KeyEvent = 0, Selected, Count;
393
394 /* Check for a keypress */
395 if (MachConsKbHit())
396 {
397 /* Check if the timeout is not already complete */
398 if (MenuInfo->MenuTimeRemaining != -1)
399 {
400 //
401 // Cancel it and remove it
402 //
403 MenuInfo->MenuTimeRemaining = -1;
404 UiDrawMenuBox(MenuInfo);
405 }
406
407 /* Get the key */
408 KeyEvent = MachConsGetCh();
409
410 /* Is it extended? Then get the extended key */
411 if (!KeyEvent) KeyEvent = MachConsGetCh();
412
413 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
414 if ((KeyPressFilter) && (KeyPressFilter(KeyEvent)))
415 {
416 /* It processed the key character, so redraw and exit */
417 UiDrawMenu(MenuInfo);
418 return 0;
419 }
420
421 /* Process the key */
422 if ((KeyEvent == KEY_UP) || (KeyEvent == KEY_DOWN))
423 {
424 /* Get the current selected item and count */
425 Selected = MenuInfo->SelectedMenuItem;
426 Count = MenuInfo->MenuItemCount - 1;
427
428 /* Check if this was a key up and there's a selected menu item */
429 if ((KeyEvent == KEY_UP) && (Selected))
430 {
431 /* Update the menu (Deselect previous item) */
432 MenuInfo->SelectedMenuItem--;
433 UiDrawMenuItem(MenuInfo, Selected);
434 Selected--;
435
436 /* Skip past any separators */
437 if ((Selected) &&
438 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
439 {
440 MenuInfo->SelectedMenuItem--;
441 }
442 }
443 else if ((KeyEvent == KEY_DOWN) && (Selected < Count))
444 {
445 /* Update the menu (deselect previous item) */
446 MenuInfo->SelectedMenuItem++;
447 UiDrawMenuItem(MenuInfo, Selected);
448 Selected++;
449
450 /* Skip past any separators */
451 if ((Selected < Count) &&
452 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
453 {
454 MenuInfo->SelectedMenuItem++;
455 }
456 }
457
458 /* Select new item and update video buffer */
459 UiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem);
460 }
461 }
462
463 /* Return the pressed key */
464 return KeyEvent;
465 }
466
467 VOID
468 NTAPI
469 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo)
470 {
471 ULONG i, Width = 0, Height, Length;
472
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
476
477 /* Loop every item */
478 for (i = 0; i < MenuInfo->MenuItemCount; i++)
479 {
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;
483 }
484
485 /* Allow room for left & right borders, plus 8 spaces on each side */
486 Width += 18;
487
488 /* Put the menu in the default left-corner position */
489 MenuInfo->Left = -1;
490 MenuInfo->Top = 4;
491
492 /* The other margins are the same */
493 MenuInfo->Right = (MenuInfo->Left) + Width;
494 MenuInfo->Bottom = (MenuInfo->Top) + Height;
495 }
496
497 BOOLEAN
498 UiDisplayMenu(IN PCSTR MenuItemList[],
499 IN ULONG MenuItemCount,
500 IN ULONG DefaultMenuItem,
501 IN LONG MenuTimeOut,
502 OUT PULONG SelectedMenuItem,
503 IN BOOLEAN CanEscape,
504 IN UiMenuKeyPressFilterCallback KeyPressFilter)
505 {
506 UI_MENU_INFO MenuInformation;
507 ULONG LastClockSecond;
508 ULONG CurrentClockSecond;
509 ULONG KeyPress;
510
511 /* Check if there's no timeout */
512 if (!MenuTimeOut)
513 {
514 /* Return the default selected item */
515 if (SelectedMenuItem) *SelectedMenuItem = DefaultMenuItem;
516 return TRUE;
517 }
518
519 /* Setup the MENU_INFO structure */
520 MenuInformation.MenuItemList = MenuItemList;
521 MenuInformation.MenuItemCount = MenuItemCount;
522 MenuInformation.MenuTimeRemaining = MenuTimeOut;
523 MenuInformation.SelectedMenuItem = DefaultMenuItem;
524
525 /* Calculate the size of the menu box */
526 UiCalcMenuBoxSize(&MenuInformation);
527
528 /* Draw the menu */
529 UiDrawMenu(&MenuInformation);
530
531 /* Get the current second of time */
532 LastClockSecond = ArcGetTime()->Second;
533
534 /* Process keys */
535 while (TRUE)
536 {
537 /* Process key presses */
538 KeyPress = UiProcessMenuKeyboardEvent(&MenuInformation,
539 KeyPressFilter);
540
541 /* Check for ENTER or ESC */
542 if (KeyPress == KEY_ENTER) break;
543 if (CanEscape && KeyPress == KEY_ESC) return FALSE;
544
545 /* Check if there is a countdown */
546 if (MenuInformation.MenuTimeRemaining)
547 {
548 /* Get the updated time, seconds only */
549 CurrentClockSecond = ArcGetTime()->Second;
550
551 /* Check if more then a second has now elapsed */
552 if (CurrentClockSecond != LastClockSecond)
553 {
554 /* Update the time information */
555 LastClockSecond = CurrentClockSecond;
556 MenuInformation.MenuTimeRemaining--;
557
558 /* Update the menu */
559 UiDrawMenuBox(&MenuInformation);
560 }
561 }
562 else
563 {
564 /* A time out occurred, exit this loop and return default OS */
565 break;
566 }
567 }
568
569 /* Return the selected item */
570 if (SelectedMenuItem) *SelectedMenuItem = MenuInformation.SelectedMenuItem;
571 return TRUE;
572 }
573
574 #endif