[FREELDR]
[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 (MenuInfo->MenuItemList[i] == NULL)
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 if (MenuInfo->MenuItemList[MenuItemNumber])
329 strcat(MenuLineText, MenuInfo->MenuItemList[MenuItemNumber]);
330
331 /* If it is a separator */
332 if (MenuInfo->MenuItemList[MenuItemNumber] == NULL)
333 {
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);
338 }
339 else if (MenuItemNumber == MenuInfo->SelectedMenuItem)
340 {
341 /* If this is the selected item, use the selected colors */
342 Attribute = ATTR(UiSelectedTextColor, UiSelectedTextBgColor);
343 }
344
345 /* Draw the item */
346 UiDrawText(MenuInfo->Left + 1,
347 MenuInfo->Top + 1 + MenuItemNumber,
348 MenuLineText,
349 Attribute);
350 }
351
352 VOID
353 UiDrawMenu(IN PUI_MENU_INFO MenuInfo)
354 {
355 ULONG i;
356
357 /* No GUI status bar text, just minimal text. Show the menu header. */
358 UiDrawText(0,
359 MenuInfo->Top - 2,
360 MenuInfo->MenuHeader,
361 ATTR(UiMenuFgColor, UiMenuBgColor));
362
363 /* Now tell the user how to choose */
364 UiDrawText(0,
365 MenuInfo->Bottom + 1,
366 "Use \x18 and \x19 to move the highlight to 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 show the menu footer */
374 UiDrawText(0,
375 UiScreenHeight - 4,
376 MenuInfo->MenuFooter,
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 /* Display the boot options if needed */
389 if (MenuInfo->ShowBootOptions)
390 {
391 DisplayBootTimeOptions();
392 }
393 }
394
395 ULONG
396 NTAPI
397 UiProcessMenuKeyboardEvent(IN PUI_MENU_INFO MenuInfo,
398 IN UiMenuKeyPressFilterCallback KeyPressFilter)
399 {
400 ULONG KeyEvent = 0, Selected, Count;
401
402 /* Check for a keypress */
403 if (MachConsKbHit())
404 {
405 /* Check if the timeout is not already complete */
406 if (MenuInfo->MenuTimeRemaining != -1)
407 {
408 //
409 // Cancel it and remove it
410 //
411 MenuInfo->MenuTimeRemaining = -1;
412 UiDrawMenuBox(MenuInfo);
413 }
414
415 /* Get the key */
416 KeyEvent = MachConsGetCh();
417
418 /* Is it extended? Then get the extended key */
419 if (!KeyEvent) KeyEvent = MachConsGetCh();
420
421 /* Call the supplied key filter callback function to see if it is going to handle this keypress. */
422 if ((KeyPressFilter) && (KeyPressFilter(KeyEvent)))
423 {
424 /* It processed the key character, so redraw and exit */
425 UiDrawMenu(MenuInfo);
426 return 0;
427 }
428
429 /* Process the key */
430 if ((KeyEvent == KEY_UP) || (KeyEvent == KEY_DOWN))
431 {
432 /* Get the current selected item and count */
433 Selected = MenuInfo->SelectedMenuItem;
434 Count = MenuInfo->MenuItemCount - 1;
435
436 /* Check if this was a key up and there's a selected menu item */
437 if ((KeyEvent == KEY_UP) && (Selected))
438 {
439 /* Update the menu (Deselect previous item) */
440 MenuInfo->SelectedMenuItem--;
441 UiDrawMenuItem(MenuInfo, Selected);
442 Selected--;
443
444 /* Skip past any separators */
445 if ((Selected) &&
446 (MenuInfo->MenuItemList[Selected] == NULL))
447 {
448 MenuInfo->SelectedMenuItem--;
449 }
450 }
451 else if ((KeyEvent == KEY_DOWN) && (Selected < Count))
452 {
453 /* Update the menu (deselect previous item) */
454 MenuInfo->SelectedMenuItem++;
455 UiDrawMenuItem(MenuInfo, Selected);
456 Selected++;
457
458 /* Skip past any separators */
459 if ((Selected < Count) &&
460 (MenuInfo->MenuItemList[Selected] == NULL))
461 {
462 MenuInfo->SelectedMenuItem++;
463 }
464 }
465
466 /* Select new item and update video buffer */
467 UiDrawMenuItem(MenuInfo, MenuInfo->SelectedMenuItem);
468 }
469 }
470
471 /* Return the pressed key */
472 return KeyEvent;
473 }
474
475 VOID
476 NTAPI
477 UiCalcMenuBoxSize(IN PUI_MENU_INFO MenuInfo)
478 {
479 ULONG i, Width = 0, Height, Length;
480
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
484
485 /* Loop every item */
486 for (i = 0; i < MenuInfo->MenuItemCount; i++)
487 {
488 /* Get the string length and make it become the new width if necessary */
489 if (MenuInfo->MenuItemList[i])
490 {
491 Length = (ULONG)strlen(MenuInfo->MenuItemList[i]);
492 if (Length > Width) Width = Length;
493 }
494 }
495
496 /* Allow room for left & right borders, plus 8 spaces on each side */
497 Width += 18;
498
499 /* Put the menu in the default left-corner position */
500 MenuInfo->Left = -1;
501 MenuInfo->Top = 4;
502
503 /* The other margins are the same */
504 MenuInfo->Right = (MenuInfo->Left) + Width;
505 MenuInfo->Bottom = (MenuInfo->Top) + Height;
506 }
507
508 BOOLEAN
509 UiDisplayMenu(IN PCSTR MenuHeader,
510 IN PCSTR MenuFooter,
511 IN BOOLEAN ShowBootOptions,
512 IN PCSTR MenuItemList[],
513 IN ULONG MenuItemCount,
514 IN ULONG DefaultMenuItem,
515 IN LONG MenuTimeOut,
516 OUT PULONG SelectedMenuItem,
517 IN BOOLEAN CanEscape,
518 IN UiMenuKeyPressFilterCallback KeyPressFilter)
519 {
520 UI_MENU_INFO MenuInformation;
521 ULONG LastClockSecond;
522 ULONG CurrentClockSecond;
523 ULONG KeyPress;
524
525 /* Check if there's no timeout */
526 if (!MenuTimeOut)
527 {
528 /* Return the default selected item */
529 if (SelectedMenuItem) *SelectedMenuItem = DefaultMenuItem;
530 return TRUE;
531 }
532
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;
541
542 /* Calculate the size of the menu box */
543 UiCalcMenuBoxSize(&MenuInformation);
544
545 /* Draw the menu */
546 UiDrawMenu(&MenuInformation);
547
548 /* Get the current second of time */
549 LastClockSecond = ArcGetTime()->Second;
550
551 /* Process keys */
552 while (TRUE)
553 {
554 /* Process key presses */
555 KeyPress = UiProcessMenuKeyboardEvent(&MenuInformation,
556 KeyPressFilter);
557
558 /* Check for ENTER or ESC */
559 if (KeyPress == KEY_ENTER) break;
560 if (CanEscape && KeyPress == KEY_ESC) return FALSE;
561
562 /* Check if there is a countdown */
563 if (MenuInformation.MenuTimeRemaining > 0)
564 {
565 /* Get the updated time, seconds only */
566 CurrentClockSecond = ArcGetTime()->Second;
567
568 /* Check if more then a second has now elapsed */
569 if (CurrentClockSecond != LastClockSecond)
570 {
571 /* Update the time information */
572 LastClockSecond = CurrentClockSecond;
573 MenuInformation.MenuTimeRemaining--;
574
575 /* Update the menu */
576 UiDrawMenuBox(&MenuInformation);
577 }
578 }
579 else if (MenuInformation.MenuTimeRemaining == 0)
580 {
581 /* A time out occurred, exit this loop and return default OS */
582 break;
583 }
584 }
585
586 /* Return the selected item */
587 if (SelectedMenuItem) *SelectedMenuItem = MenuInformation.SelectedMenuItem;
588 return TRUE;
589 }
590
591 #endif