18e2a821da97c840e7a944c98a356b6a2be433a9
[reactos.git] / reactos / 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 + 4,
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 + 4,
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 MenuInfo->MenuTitle,
360 ATTR(UiMenuFgColor, UiMenuBgColor));
361
362 /* Now tell him how to choose */
363 UiDrawText(0,
364 MenuInfo->Bottom + 1,
365 "Use \x18 and \x19 to move the highlight to your choice.",
366 ATTR(UiMenuFgColor, UiMenuBgColor));
367 UiDrawText(0,
368 MenuInfo->Bottom + 2,
369 "Press ENTER to choose.",
370 ATTR(UiMenuFgColor, UiMenuBgColor));
371
372 /* And offer F8 options */
373 UiDrawText(0,
374 UiScreenHeight - 4,
375 "For troubleshooting and advanced startup options for "
376 "ReactOS, press F8.",
377 ATTR(UiMenuFgColor, UiMenuBgColor));
378
379 /* Draw the menu box */
380 UiDrawMenuBox(MenuInfo);
381
382 /* Draw each line of the menu */
383 for (i = 0; i < MenuInfo->MenuItemCount; i++)
384 {
385 UiDrawMenuItem(MenuInfo, i);
386 }
387 }
388
389 ULONG
390 NTAPI
391 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo,
392 IN UiMenuKeyPressFilterCallback KeyPressFilter)
393 {
394 ULONG KeyEvent = 0, Selected, Count;
395
396 /* Check for a keypress */
397 if (MachConsKbHit())
398 {
399 /* Check if the timeout is not already complete */
400 if (MenuInfo->MenuTimeRemaining != -1)
401 {
402 //
403 // Cancel it and remove it
404 //
405 MenuInfo->MenuTimeRemaining = -1;
406 UiDrawMenuBox(MenuInfo);
407 }
408
409 /* Get the key */
410 KeyEvent = MachConsGetCh();
411
412 /* Is it extended? Then get the extended key */
413 if (!KeyEvent) KeyEvent = MachConsGetCh();
414
415 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
416 if ((KeyPressFilter) && (KeyPressFilter(KeyEvent)))
417 {
418 /* It processed the key character, so redraw and exit */
419 UiDrawMenu(MenuInfo);
420 return 0;
421 }
422
423 /* Process the key */
424 if ((KeyEvent == KEY_UP) || (KeyEvent == KEY_DOWN))
425 {
426 /* Get the current selected item and count */
427 Selected = MenuInfo->SelectedMenuItem;
428 Count = MenuInfo->MenuItemCount - 1;
429
430 /* Check if this was a key up and there's a selected menu item */
431 if ((KeyEvent == KEY_UP) && (Selected))
432 {
433 /* Update the menu (Deselect previous item) */
434 MenuInfo->SelectedMenuItem--;
435 UiDrawMenuItem(MenuInfo, Selected);
436 Selected--;
437
438 /* Skip past any separators */
439 if ((Selected) &&
440 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
441 {
442 MenuInfo->SelectedMenuItem--;
443 }
444 }
445 else if ((KeyEvent == KEY_DOWN) && (Selected < Count))
446 {
447 /* Update the menu (deselect previous item) */
448 MenuInfo->SelectedMenuItem++;
449 UiDrawMenuItem(MenuInfo, Selected);
450 Selected++;
451
452 /* Skip past any separators */
453 if ((Selected < Count) &&
454 !(_stricmp(MenuInfo->MenuItemList[Selected], "SEPARATOR")))
455 {
456 MenuInfo->SelectedMenuItem++;
457 }
458 }
459
460 /* Select new item and update video buffer */
461 UiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem);
462 }
463 }
464
465 /* Return the pressed key */
466 return KeyEvent;
467 }
468
469 VOID
470 NTAPI
471 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo)
472 {
473 ULONG i, Width = 0, Height, Length;
474
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
478
479 /* Loop every item */
480 for (i = 0; i < MenuInfo->MenuItemCount; i++)
481 {
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;
485 }
486
487 /* Allow room for left & right borders, plus 8 spaces on each side */
488 Width += 18;
489
490 /* Put the menu in the default left-corner position */
491 MenuInfo->Left = -1;
492 MenuInfo->Top = 4;
493
494 /* The other margins are the same */
495 MenuInfo->Right = (MenuInfo->Left) + Width;
496 MenuInfo->Bottom = (MenuInfo->Top) + Height;
497 }
498
499 BOOLEAN
500 UiDisplayMenu(IN PCSTR MenuTitle,
501 IN PCSTR MenuItemList[],
502 IN ULONG MenuItemCount,
503 IN ULONG DefaultMenuItem,
504 IN LONG MenuTimeOut,
505 OUT PULONG SelectedMenuItem,
506 IN BOOLEAN CanEscape,
507 IN UiMenuKeyPressFilterCallback KeyPressFilter)
508 {
509 UI_MENU_INFO MenuInformation;
510 ULONG LastClockSecond;
511 ULONG CurrentClockSecond;
512 ULONG KeyPress;
513
514 /* Check if there's no timeout */
515 if (!MenuTimeOut)
516 {
517 /* Return the default selected item */
518 if (SelectedMenuItem) *SelectedMenuItem = DefaultMenuItem;
519 return TRUE;
520 }
521
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;
528
529 /* Calculate the size of the menu box */
530 UiCalcMenuBoxSize(&MenuInformation);
531
532 /* Draw the menu */
533 UiDrawMenu(&MenuInformation);
534
535 /* Get the current second of time */
536 LastClockSecond = ArcGetTime()->Second;
537
538 /* Process keys */
539 while (TRUE)
540 {
541 /* Process key presses */
542 KeyPress = UiProcessMenuKeyboardEvent(&MenuInformation,
543 KeyPressFilter);
544
545 /* Check for ENTER or ESC */
546 if (KeyPress == KEY_ENTER) break;
547 if (CanEscape && KeyPress == KEY_ESC) return FALSE;
548
549 /* Check if there is a countdown */
550 if (MenuInformation.MenuTimeRemaining)
551 {
552 /* Get the updated time, seconds only */
553 CurrentClockSecond = ArcGetTime()->Second;
554
555 /* Check if more then a second has now elapsed */
556 if (CurrentClockSecond != LastClockSecond)
557 {
558 /* Update the time information */
559 LastClockSecond = CurrentClockSecond;
560 MenuInformation.MenuTimeRemaining--;
561
562 /* Update the menu */
563 UiDrawMenuBox(&MenuInformation);
564 }
565 }
566 else
567 {
568 /* A time out occurred, exit this loop and return default OS */
569 break;
570 }
571 }
572
573 /* Return the selected item */
574 if (SelectedMenuItem) *SelectedMenuItem = MenuInformation.SelectedMenuItem;
575 return TRUE;
576 }
577
578
579 /* SETUP MODE *****************************************************************/
580
581 BOOLEAN SetupUiInitialize(VOID)
582 {
583 /* Nothing to do */
584 return TRUE;
585 }
586
587 #endif