xref: /reactos/boot/freeldr/freeldr/ui/tui.c (revision 864aed6b)
1 /*
2  *  FreeLoader
3  *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include <freeldr.h>
21 
22 PVOID TextVideoBuffer = NULL;
23 
24 /* GENERIC TUI UTILS *********************************************************/
25 
26 /*
27  * TuiPrintf()
28  * Prints formatted text to the screen.
29  */
30 INT
31 TuiPrintf(
32     _In_ PCSTR Format, ...)
33 {
34     INT i;
35     INT Length;
36     va_list ap;
37     CHAR Buffer[512];
38 
39     va_start(ap, Format);
40     Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
41     va_end(ap);
42 
43     if (Length == -1)
44         Length = (INT)sizeof(Buffer);
45 
46     for (i = 0; i < Length; i++)
47     {
48         MachConsPutChar(Buffer[i]);
49     }
50 
51     return Length;
52 }
53 
54 VOID
55 TuiTruncateStringEllipsis(
56     _Inout_z_ PSTR StringText,
57     _In_ ULONG MaxChars)
58 {
59     /* If it's too large, just add some ellipsis past the maximum */
60     if (strlen(StringText) > MaxChars)
61         strcpy(&StringText[MaxChars - 3], "...");
62 }
63 
64 /*
65  * DrawText()
66  * Displays a string on a single screen line.
67  * This function assumes coordinates are zero-based.
68  */
69 VOID
70 TuiDrawText(
71     _In_ ULONG X,
72     _In_ ULONG Y,
73     _In_ PCSTR Text,
74     _In_ UCHAR Attr)
75 {
76     TuiDrawText2(X, Y, 0 /*(ULONG)strlen(Text)*/, Text, Attr);
77 }
78 
79 /*
80  * DrawText2()
81  * Displays a string on a single screen line.
82  * This function assumes coordinates are zero-based.
83  * MaxNumChars is the maximum number of characters to display.
84  * If MaxNumChars == 0, then display the whole string.
85  */
86 VOID
87 TuiDrawText2(
88     _In_ ULONG X,
89     _In_ ULONG Y,
90     _In_opt_ ULONG MaxNumChars,
91     _In_reads_or_z_(MaxNumChars) PCSTR Text,
92     _In_ UCHAR Attr)
93 {
94     PUCHAR ScreenMemory = (PUCHAR)TextVideoBuffer;
95     ULONG i, j;
96 
97     /* Don't display anything if we are out of the screen */
98     if ((X >= UiScreenWidth) || (Y >= UiScreenHeight))
99         return;
100 
101     /* Draw the text, not exceeding the width */
102     for (i = X, j = 0; Text[j] && i < UiScreenWidth && (MaxNumChars > 0 ? j < MaxNumChars : TRUE); i++, j++)
103     {
104         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)]   = (UCHAR)Text[j];
105         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)+1] = Attr;
106     }
107 }
108 
109 VOID
110 TuiDrawCenteredText(
111     _In_ ULONG Left,
112     _In_ ULONG Top,
113     _In_ ULONG Right,
114     _In_ ULONG Bottom,
115     _In_ PCSTR TextString,
116     _In_ UCHAR Attr)
117 {
118     SIZE_T TextLength;
119     SIZE_T Index, LastIndex;
120     ULONG  LineBreakCount;
121     ULONG  BoxWidth, BoxHeight;
122     ULONG  RealLeft, RealTop;
123     ULONG  X, Y;
124     CHAR   Temp[2];
125 
126     /* Query text length */
127     TextLength = strlen(TextString);
128 
129     /* Count the new lines and the box width */
130     LineBreakCount = 0;
131     BoxWidth = 0;
132     LastIndex = 0;
133     for (Index = 0; Index < TextLength; Index++)
134     {
135         /* Scan for new lines */
136         if (TextString[Index] == '\n')
137         {
138             /* Remember the new line */
139             LastIndex = Index;
140             LineBreakCount++;
141         }
142         else
143         {
144             /* Check for new larger box width */
145             if ((Index - LastIndex) > BoxWidth)
146             {
147                 /* Update it */
148                 BoxWidth = (ULONG)(Index - LastIndex);
149             }
150         }
151     }
152 
153     /* Base the box height on the number of lines */
154     BoxHeight = LineBreakCount + 1;
155 
156     /*
157      * Create the centered coordinates.
158      * Here, the Left/Top/Right/Bottom rectangle is a hint, around
159      * which we center the "real" text rectangle RealLeft/RealTop.
160      */
161     RealLeft = (Left + Right - BoxWidth + 1) / 2;
162     RealTop  = (Top + Bottom - BoxHeight + 1) / 2;
163 
164     /* Now go for a second scan */
165     LastIndex = 0;
166     for (Index = 0; Index < TextLength; Index++)
167     {
168         /* Look for new lines again */
169         if (TextString[Index] == '\n')
170         {
171             /* Update where the text should start */
172             RealTop++;
173             LastIndex = 0;
174         }
175         else
176         {
177             /* We've got a line of text to print, do it */
178             X = (ULONG)(RealLeft + LastIndex);
179             Y = RealTop;
180             LastIndex++;
181             Temp[0] = TextString[Index];
182             Temp[1] = 0;
183             TuiDrawText(X, Y, Temp, Attr);
184         }
185     }
186 }
187 
188 /* FULL TUI THEME ************************************************************/
189 
190 #define TAG_TUI_SCREENBUFFER 'SiuT'
191 #define TAG_TUI_PALETTE      'PiuT'
192 
193 extern UCHAR MachDefaultTextColor;
194 
195 BOOLEAN TuiInitialize(VOID)
196 {
197     MachVideoHideShowTextCursor(FALSE);
198     MachVideoSetTextCursorPosition(0, 0);
199     MachVideoClearScreen(ATTR(COLOR_GRAY, COLOR_BLACK));
200 
201     TextVideoBuffer = VideoAllocateOffScreenBuffer();
202     if (TextVideoBuffer == NULL)
203     {
204         return FALSE;
205     }
206 
207     /* Load default settings with "Full" TUI Theme */
208 
209     UiStatusBarFgColor    = COLOR_BLACK;
210     UiStatusBarBgColor    = COLOR_CYAN;
211     UiBackdropFgColor     = COLOR_WHITE;
212     UiBackdropBgColor     = COLOR_BLUE;
213     UiBackdropFillStyle   = MEDIUM_FILL;
214     UiTitleBoxFgColor     = COLOR_WHITE;
215     UiTitleBoxBgColor     = COLOR_RED;
216     UiMessageBoxFgColor   = COLOR_WHITE;
217     UiMessageBoxBgColor   = COLOR_BLUE;
218     UiMenuFgColor         = COLOR_WHITE;
219     UiMenuBgColor         = COLOR_BLUE;
220     UiTextColor           = COLOR_YELLOW;
221     UiSelectedTextColor   = COLOR_BLACK;
222     UiSelectedTextBgColor = COLOR_GRAY;
223     UiEditBoxTextColor    = COLOR_WHITE;
224     UiEditBoxBgColor      = COLOR_BLACK;
225 
226     UiShowTime          = TRUE;
227     UiMenuBox           = TRUE;
228     UiCenterMenu        = TRUE;
229     UiUseSpecialEffects = FALSE;
230 
231     // TODO: Have a boolean to show/hide title box?
232     RtlStringCbCopyA(UiTitleBoxTitleText, sizeof(UiTitleBoxTitleText),
233                      "Boot Menu");
234 
235     RtlStringCbCopyA(UiTimeText, sizeof(UiTimeText),
236                      "[Time Remaining: %d]");
237 
238     return TRUE;
239 }
240 
241 VOID TuiUnInitialize(VOID)
242 {
243     /* Do nothing if already uninitialized */
244     if (!TextVideoBuffer)
245         return;
246 
247     if (UiUseSpecialEffects)
248     {
249         TuiFadeOut();
250     }
251     else
252     {
253         MachVideoSetDisplayMode(NULL, FALSE);
254     }
255 
256     VideoFreeOffScreenBuffer();
257     TextVideoBuffer = NULL;
258 
259     MachVideoClearScreen(ATTR(COLOR_GRAY, COLOR_BLACK));
260     MachVideoSetTextCursorPosition(0, 0);
261     MachVideoHideShowTextCursor(TRUE);
262 }
263 
264 VOID TuiDrawBackdrop(VOID)
265 {
266     /* Fill in the background (excluding title box & status bar) */
267     TuiFillArea(0,
268                 TUI_TITLE_BOX_CHAR_HEIGHT,
269                 UiScreenWidth - 1,
270                 UiScreenHeight - 2,
271                 UiBackdropFillStyle,
272                 ATTR(UiBackdropFgColor, UiBackdropBgColor));
273 
274     /* Draw the title box */
275     TuiDrawBox(0,
276                0,
277                UiScreenWidth - 1,
278                TUI_TITLE_BOX_CHAR_HEIGHT - 1,
279                D_VERT,
280                D_HORZ,
281                TRUE,
282                FALSE,
283                ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
284 
285     /* Draw version text */
286     TuiDrawText(2,
287                 1,
288                 FrLdrVersionString,
289                 ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
290 
291     /* Draw copyright */
292     TuiDrawText(2,
293                 2,
294                 BY_AUTHOR,
295                 ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
296     TuiDrawText(2,
297                 3,
298                 AUTHOR_EMAIL,
299                 ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
300 
301     /* Draw help text */
302     TuiDrawText(UiScreenWidth - 16, 3,
303                 /*"F1 for Help"*/ "F8 for Options",
304                 ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
305 
306     /* Draw title text */
307     TuiDrawText((UiScreenWidth - (ULONG)strlen(UiTitleBoxTitleText)) / 2,
308                 2,
309                 UiTitleBoxTitleText,
310                 ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
311 
312     /* Update the date & time */
313     TuiUpdateDateTime();
314     VideoCopyOffScreenBufferToVRAM();
315 }
316 
317 /*
318  * FillArea()
319  * This function assumes coordinates are zero-based
320  */
321 VOID TuiFillArea(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, CHAR FillChar, UCHAR Attr /* Color Attributes */)
322 {
323     PUCHAR ScreenMemory = (PUCHAR)TextVideoBuffer;
324     ULONG  i, j;
325 
326     /* Clip the area to the screen */
327     if ((Left >= UiScreenWidth) || (Top >= UiScreenHeight))
328     {
329         return;
330     }
331     if (Right >= UiScreenWidth)
332         Right = UiScreenWidth - 1;
333     if (Bottom >= UiScreenHeight)
334         Bottom = UiScreenHeight - 1;
335 
336     /* Loop through each line and column and fill it in */
337     for (i = Top; i <= Bottom; ++i)
338     {
339         for (j = Left; j <= Right; ++j)
340         {
341             ScreenMemory[((i*2)*UiScreenWidth)+(j*2)] = (UCHAR)FillChar;
342             ScreenMemory[((i*2)*UiScreenWidth)+(j*2)+1] = Attr;
343         }
344     }
345 }
346 
347 /*
348  * DrawShadow()
349  * This function assumes coordinates are zero-based
350  */
351 VOID TuiDrawShadow(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom)
352 {
353     PUCHAR ScreenMemory = (PUCHAR)TextVideoBuffer;
354     ULONG  Idx;
355 
356     /* Shade the bottom of the area */
357     if (Bottom < (UiScreenHeight - 1))
358     {
359         if (UiScreenHeight < 34)
360             Idx = Left + 2;
361         else
362             Idx = Left + 1;
363 
364         for (; Idx <= Right; ++Idx)
365         {
366             ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+(Idx*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
367         }
368     }
369 
370     /* Shade the right of the area */
371     if (Right < (UiScreenWidth - 1))
372     {
373         for (Idx=Top+1; Idx<=Bottom; Idx++)
374         {
375             ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
376         }
377     }
378     if (UiScreenHeight < 34)
379     {
380         if ((Right + 1) < (UiScreenWidth - 1))
381         {
382             for (Idx=Top+1; Idx<=Bottom; Idx++)
383             {
384                 ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
385             }
386         }
387     }
388 
389     /* Shade the bottom right corner */
390     if ((Right < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
391     {
392         ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
393     }
394     if (UiScreenHeight < 34)
395     {
396         if (((Right + 1) < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
397         {
398             ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
399         }
400     }
401 }
402 
403 /*
404  * DrawBox()
405  * This function assumes coordinates are zero-based
406  */
407 VOID
408 TuiDrawBoxTopLine(
409     _In_ ULONG Left,
410     _In_ ULONG Top,
411     _In_ ULONG Right,
412     _In_ UCHAR VertStyle,
413     _In_ UCHAR HorzStyle,
414     _In_ UCHAR Attr)
415 {
416     UCHAR ULCorner, URCorner;
417 
418     /* Calculate the corner values */
419     if (HorzStyle == HORZ)
420     {
421         if (VertStyle == VERT)
422         {
423             ULCorner = UL;
424             URCorner = UR;
425         }
426         else // VertStyle == D_VERT
427         {
428             ULCorner = VD_UL;
429             URCorner = VD_UR;
430         }
431     }
432     else // HorzStyle == D_HORZ
433     {
434         if (VertStyle == VERT)
435         {
436             ULCorner = HD_UL;
437             URCorner = HD_UR;
438         }
439         else // VertStyle == D_VERT
440         {
441             ULCorner = D_UL;
442             URCorner = D_UR;
443         }
444     }
445 
446     TuiFillArea(Left, Top, Left, Top, ULCorner, Attr);
447     TuiFillArea(Left+1, Top, Right-1, Top, HorzStyle, Attr);
448     TuiFillArea(Right, Top, Right, Top, URCorner, Attr);
449 }
450 
451 VOID
452 TuiDrawBoxBottomLine(
453     _In_ ULONG Left,
454     _In_ ULONG Bottom,
455     _In_ ULONG Right,
456     _In_ UCHAR VertStyle,
457     _In_ UCHAR HorzStyle,
458     _In_ UCHAR Attr)
459 {
460     UCHAR LLCorner, LRCorner;
461 
462     /* Calculate the corner values */
463     if (HorzStyle == HORZ)
464     {
465         if (VertStyle == VERT)
466         {
467             LLCorner = LL;
468             LRCorner = LR;
469         }
470         else // VertStyle == D_VERT
471         {
472             LLCorner = VD_LL;
473             LRCorner = VD_LR;
474         }
475     }
476     else // HorzStyle == D_HORZ
477     {
478         if (VertStyle == VERT)
479         {
480             LLCorner = HD_LL;
481             LRCorner = HD_LR;
482         }
483         else // VertStyle == D_VERT
484         {
485             LLCorner = D_LL;
486             LRCorner = D_LR;
487         }
488     }
489 
490     TuiFillArea(Left, Bottom, Left, Bottom, LLCorner, Attr);
491     TuiFillArea(Left+1, Bottom, Right-1, Bottom, HorzStyle, Attr);
492     TuiFillArea(Right, Bottom, Right, Bottom, LRCorner, Attr);
493 }
494 
495 VOID
496 TuiDrawBox(
497     _In_ ULONG Left,
498     _In_ ULONG Top,
499     _In_ ULONG Right,
500     _In_ ULONG Bottom,
501     _In_ UCHAR VertStyle,
502     _In_ UCHAR HorzStyle,
503     _In_ BOOLEAN Fill,
504     _In_ BOOLEAN Shadow,
505     _In_ UCHAR Attr)
506 {
507     /* Fill in the box background */
508     if (Fill)
509         TuiFillArea(Left, Top, Right, Bottom, ' ', Attr);
510 
511     /* Fill in the top horizontal line */
512     TuiDrawBoxTopLine(Left, Top, Right, VertStyle, HorzStyle, Attr);
513 
514     /* Fill in the vertical left and right lines */
515     TuiFillArea(Left, Top+1, Left, Bottom-1, VertStyle, Attr);
516     TuiFillArea(Right, Top+1, Right, Bottom-1, VertStyle, Attr);
517 
518     /* Fill in the bottom horizontal line */
519     TuiDrawBoxBottomLine(Left, Bottom, Right, VertStyle, HorzStyle, Attr);
520 
521     /* Draw the shadow */
522     if (Shadow)
523         TuiDrawShadow(Left, Top, Right, Bottom);
524 }
525 
526 VOID TuiDrawStatusText(PCSTR StatusText)
527 {
528     SIZE_T    i;
529 
530     TuiDrawText(0, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
531     TuiDrawText(1, UiScreenHeight-1, StatusText, ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
532 
533     for (i=strlen(StatusText)+1; i<UiScreenWidth; i++)
534     {
535         TuiDrawText((ULONG)i, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
536     }
537 
538     VideoCopyOffScreenBufferToVRAM();
539 }
540 
541 VOID TuiUpdateDateTime(VOID)
542 {
543     TIMEINFO* TimeInfo;
544     PCSTR   DayPostfix;
545     BOOLEAN PMHour = FALSE;
546     CHAR Buffer[40];
547 
548     /* Don't draw the time if this has been disabled */
549     if (!UiShowTime) return;
550 
551     TimeInfo = ArcGetTime();
552     if (TimeInfo->Year < 1 || 9999 < TimeInfo->Year ||
553         TimeInfo->Month < 1 || 12 < TimeInfo->Month ||
554         TimeInfo->Day < 1 || 31 < TimeInfo->Day ||
555         23 < TimeInfo->Hour ||
556         59 < TimeInfo->Minute ||
557         59 < TimeInfo->Second)
558     {
559         /* This happens on QEmu sometimes. We just skip updating. */
560         return;
561     }
562 
563     /* Get the day postfix */
564     if (1 == TimeInfo->Day || 21 == TimeInfo->Day || 31 == TimeInfo->Day)
565         DayPostfix = "st";
566     else if (2 == TimeInfo->Day || 22 == TimeInfo->Day)
567         DayPostfix = "nd";
568     else if (3 == TimeInfo->Day || 23 == TimeInfo->Day)
569         DayPostfix = "rd";
570     else
571         DayPostfix = "th";
572 
573     /* Build the date string in format: "MMMM dx yyyy" */
574     RtlStringCbPrintfA(Buffer, sizeof(Buffer),
575                        "%s %d%s %d",
576                        UiMonthNames[TimeInfo->Month - 1],
577                        TimeInfo->Day,
578                        DayPostfix,
579                        TimeInfo->Year);
580 
581     /* Draw the date */
582     TuiDrawText(UiScreenWidth - (ULONG)strlen(Buffer) - 2, 1,
583                 Buffer, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
584 
585     /* Get the hour and change from 24-hour mode to 12-hour */
586     if (TimeInfo->Hour > 12)
587     {
588         TimeInfo->Hour -= 12;
589         PMHour = TRUE;
590     }
591     if (TimeInfo->Hour == 0)
592     {
593         TimeInfo->Hour = 12;
594     }
595 
596     /* Build the time string in format: "h:mm:ss tt" */
597     RtlStringCbPrintfA(Buffer, sizeof(Buffer),
598                        "    %d:%02d:%02d %s",
599                        TimeInfo->Hour,
600                        TimeInfo->Minute,
601                        TimeInfo->Second,
602                        PMHour ? "PM" : "AM");
603 
604     /* Draw the time */
605     TuiDrawText(UiScreenWidth - (ULONG)strlen(Buffer) - 2, 2,
606                 Buffer, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
607 }
608 
609 VOID TuiSaveScreen(PUCHAR Buffer)
610 {
611     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
612     ULONG    i;
613 
614     for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
615     {
616         Buffer[i] = ScreenMemory[i];
617     }
618 }
619 
620 VOID TuiRestoreScreen(PUCHAR Buffer)
621 {
622     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
623     ULONG    i;
624 
625     for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
626     {
627         ScreenMemory[i] = Buffer[i];
628     }
629     VideoCopyOffScreenBufferToVRAM();
630 }
631 
632 VOID TuiMessageBox(PCSTR MessageText)
633 {
634     PVOID    ScreenBuffer;
635 
636     // Save the screen contents
637     ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2,
638                                   TAG_TUI_SCREENBUFFER);
639     TuiSaveScreen(ScreenBuffer);
640 
641     // Display the message box
642     TuiMessageBoxCritical(MessageText);
643 
644     // Restore the screen contents
645     TuiRestoreScreen(ScreenBuffer);
646     FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER);
647 }
648 
649 VOID TuiMessageBoxCritical(PCSTR MessageText)
650 {
651     int        width = 8;
652     unsigned int    height = 1;
653     int        curline = 0;
654     int        k;
655     size_t        i , j;
656     int        x1, x2, y1, y2;
657     char    temp[260];
658     char    key;
659 
660     // Find the height
661     for (i=0; i<strlen(MessageText); i++)
662     {
663         if (MessageText[i] == '\n')
664             height++;
665     }
666 
667     // Find the width
668     for (i=0,j=0,k=0; i<height; i++)
669     {
670         while ((MessageText[j] != '\n') && (MessageText[j] != 0))
671         {
672             j++;
673             k++;
674         }
675 
676         if (k > width)
677             width = k;
678 
679         k = 0;
680         j++;
681     }
682 
683     // Calculate box area
684     x1 = (UiScreenWidth - (width+2))/2;
685     x2 = x1 + width + 3;
686     y1 = ((UiScreenHeight - height - 2)/2) + 1;
687     y2 = y1 + height + 4;
688 
689     // Draw the box
690     TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
691 
692     // Draw the text
693     for (i=0,j=0; i<strlen(MessageText)+1; i++)
694     {
695         if ((MessageText[i] == '\n') || (MessageText[i] == 0))
696         {
697             temp[j] = 0;
698             j = 0;
699             UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
700             curline++;
701         }
702         else
703             temp[j++] = MessageText[i];
704     }
705 
706     // Draw OK button
707     strcpy(temp, "   OK   ");
708     UiDrawText(x1+((x2-x1)/2)-3, y2-2, temp, ATTR(COLOR_BLACK, COLOR_GRAY));
709 
710     // Draw status text
711     UiDrawStatusText("Press ENTER to continue");
712 
713     VideoCopyOffScreenBufferToVRAM();
714 
715     for (;;)
716     {
717         if (MachConsKbHit())
718         {
719             key = MachConsGetCh();
720             if (key == KEY_EXTENDED)
721                 key = MachConsGetCh();
722 
723             if ((key == KEY_ENTER) || (key == KEY_SPACE) || (key == KEY_ESC))
724                 break;
725         }
726 
727         TuiUpdateDateTime();
728 
729         VideoCopyOffScreenBufferToVRAM();
730 
731         MachHwIdle();
732     }
733 }
734 
735 static VOID
736 TuiSetProgressBarText(
737     _In_ PCSTR ProgressText)
738 {
739     ULONG ProgressBarWidth;
740     CHAR ProgressString[256];
741 
742     /* Make sure the progress bar is enabled */
743     ASSERT(UiProgressBar.Show);
744 
745     /* Calculate the width of the bar proper */
746     ProgressBarWidth = UiProgressBar.Right - UiProgressBar.Left + 1;
747 
748     /* First make sure the progress bar text fits */
749     RtlStringCbCopyA(ProgressString, sizeof(ProgressString), ProgressText);
750     TuiTruncateStringEllipsis(ProgressString, ProgressBarWidth);
751 
752     /* Clear the text area */
753     TuiFillArea(UiProgressBar.Left, UiProgressBar.Top,
754                 UiProgressBar.Right, UiProgressBar.Bottom - 1,
755                 ' ', ATTR(UiTextColor, UiMenuBgColor));
756 
757     /* Draw the "Loading..." text */
758     TuiDrawCenteredText(UiProgressBar.Left, UiProgressBar.Top,
759                         UiProgressBar.Right, UiProgressBar.Bottom - 1,
760                         ProgressString, ATTR(UiTextColor, UiMenuBgColor));
761 }
762 
763 static VOID
764 TuiTickProgressBar(
765     _In_ ULONG SubPercentTimes100)
766 {
767     ULONG ProgressBarWidth;
768     ULONG FillCount;
769 
770     /* Make sure the progress bar is enabled */
771     ASSERT(UiProgressBar.Show);
772 
773     ASSERT(SubPercentTimes100 <= (100 * 100));
774 
775     /* Calculate the width of the bar proper */
776     ProgressBarWidth = UiProgressBar.Right - UiProgressBar.Left + 1;
777 
778     /* Compute fill count */
779     // FillCount = (ProgressBarWidth * Position) / Range;
780     FillCount = ProgressBarWidth * SubPercentTimes100 / (100 * 100);
781 
782     /* Fill the progress bar */
783     /* Draw the percent complete -- Use the fill character */
784     if (FillCount > 0)
785     {
786         TuiFillArea(UiProgressBar.Left, UiProgressBar.Bottom,
787                     UiProgressBar.Left + FillCount - 1, UiProgressBar.Bottom,
788                     '\xDB', ATTR(UiTextColor, UiMenuBgColor));
789     }
790     /* Fill the remaining with shadow blanks */
791     TuiFillArea(UiProgressBar.Left + FillCount, UiProgressBar.Bottom,
792                 UiProgressBar.Right, UiProgressBar.Bottom,
793                 '\xB2', ATTR(UiTextColor, UiMenuBgColor));
794 
795     TuiUpdateDateTime();
796     VideoCopyOffScreenBufferToVRAM();
797 }
798 
799 static VOID
800 TuiDrawProgressBar(
801     _In_ ULONG Left,
802     _In_ ULONG Top,
803     _In_ ULONG Right,
804     _In_ ULONG Bottom,
805     _In_ PCSTR ProgressText);
806 
807 static VOID
808 TuiDrawProgressBarCenter(
809     _In_ PCSTR ProgressText)
810 {
811     ULONG Left, Top, Right, Bottom, Width, Height;
812 
813     /* Build the coordinates and sizes */
814     Height = 2;
815     Width  = 50; // Allow for 50 "bars"
816     Left = (UiScreenWidth - Width) / 2;
817     Top  = (UiScreenHeight - Height + 4) / 2;
818     Right  = Left + Width - 1;
819     Bottom = Top + Height - 1;
820 
821     /* Inflate to include the box margins */
822     Left -= 2;
823     Right += 2;
824     Top -= 1;
825     Bottom += 1;
826 
827     /* Draw the progress bar */
828     TuiDrawProgressBar(Left, Top, Right, Bottom, ProgressText);
829 }
830 
831 static VOID
832 TuiDrawProgressBar(
833     _In_ ULONG Left,
834     _In_ ULONG Top,
835     _In_ ULONG Right,
836     _In_ ULONG Bottom,
837     _In_ PCSTR ProgressText)
838 {
839     /* Draw the box */
840     TuiDrawBox(Left, Top, Right, Bottom,
841                VERT, HORZ, TRUE, TRUE,
842                ATTR(UiMenuFgColor, UiMenuBgColor));
843 
844     /* Exclude the box margins */
845     Left += 2;
846     Right -= 2;
847     Top += 1;
848     Bottom -= 1;
849 
850     UiInitProgressBar(Left, Top, Right, Bottom, ProgressText);
851 }
852 
853 UCHAR TuiTextToColor(PCSTR ColorText)
854 {
855     static const struct
856     {
857         PCSTR ColorName;
858         UCHAR ColorValue;
859     } Colors[] =
860     {
861         {"Black"  , COLOR_BLACK  },
862         {"Blue"   , COLOR_BLUE   },
863         {"Green"  , COLOR_GREEN  },
864         {"Cyan"   , COLOR_CYAN   },
865         {"Red"    , COLOR_RED    },
866         {"Magenta", COLOR_MAGENTA},
867         {"Brown"  , COLOR_BROWN  },
868         {"Gray"   , COLOR_GRAY   },
869         {"DarkGray"    , COLOR_DARKGRAY    },
870         {"LightBlue"   , COLOR_LIGHTBLUE   },
871         {"LightGreen"  , COLOR_LIGHTGREEN  },
872         {"LightCyan"   , COLOR_LIGHTCYAN   },
873         {"LightRed"    , COLOR_LIGHTRED    },
874         {"LightMagenta", COLOR_LIGHTMAGENTA},
875         {"Yellow"      , COLOR_YELLOW      },
876         {"White"       , COLOR_WHITE       },
877     };
878     ULONG i;
879 
880     if (_stricmp(ColorText, "Default") == 0)
881         return MachDefaultTextColor;
882 
883     for (i = 0; i < RTL_NUMBER_OF(Colors); ++i)
884     {
885         if (_stricmp(ColorText, Colors[i].ColorName) == 0)
886             return Colors[i].ColorValue;
887     }
888 
889     return COLOR_BLACK;
890 }
891 
892 UCHAR TuiTextToFillStyle(PCSTR FillStyleText)
893 {
894     static const struct
895     {
896         PCSTR FillStyleName;
897         UCHAR FillStyleValue;
898     } FillStyles[] =
899     {
900         {"None"  , ' '},
901         {"Light" , LIGHT_FILL },
902         {"Medium", MEDIUM_FILL},
903         {"Dark"  , DARK_FILL  },
904     };
905     ULONG i;
906 
907     for (i = 0; i < RTL_NUMBER_OF(FillStyles); ++i)
908     {
909         if (_stricmp(FillStyleText, FillStyles[i].FillStyleName) == 0)
910             return FillStyles[i].FillStyleValue;
911     }
912 
913     return LIGHT_FILL;
914 }
915 
916 VOID TuiFadeInBackdrop(VOID)
917 {
918     PPALETTE_ENTRY TuiFadePalette = NULL;
919 
920     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
921     {
922         TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64,
923                                                         TAG_TUI_PALETTE);
924 
925         if (TuiFadePalette != NULL)
926         {
927             VideoSavePaletteState(TuiFadePalette, 64);
928             VideoSetAllColorsToBlack(64);
929         }
930     }
931 
932     // Draw the backdrop and title box
933     TuiDrawBackdrop();
934 
935     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
936     {
937         VideoFadeIn(TuiFadePalette, 64);
938         FrLdrTempFree(TuiFadePalette, TAG_TUI_PALETTE);
939     }
940 }
941 
942 VOID TuiFadeOut(VOID)
943 {
944     PPALETTE_ENTRY TuiFadePalette = NULL;
945 
946     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
947     {
948         TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64,
949                                                         TAG_TUI_PALETTE);
950 
951         if (TuiFadePalette != NULL)
952         {
953             VideoSavePaletteState(TuiFadePalette, 64);
954         }
955     }
956 
957     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
958     {
959         VideoFadeOut(64);
960     }
961 
962     MachVideoSetDisplayMode(NULL, FALSE);
963 
964     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
965     {
966         VideoRestorePaletteState(TuiFadePalette, 64);
967         FrLdrTempFree(TuiFadePalette, TAG_TUI_PALETTE);
968     }
969 
970 }
971 
972 BOOLEAN TuiEditBox(PCSTR MessageText, PCHAR EditTextBuffer, ULONG Length)
973 {
974     INT        width = 8;
975     ULONG    height = 1;
976     INT        curline = 0;
977     INT        k;
978     size_t    i , j;
979     INT        x1, x2, y1, y2;
980     CHAR    temp[260];
981     CHAR    key;
982     BOOLEAN    Extended;
983     INT        EditBoxLine;
984     ULONG    EditBoxStartX, EditBoxEndX;
985     INT        EditBoxCursorX;
986     ULONG    EditBoxTextLength, EditBoxTextPosition;
987     INT        EditBoxTextDisplayIndex;
988     BOOLEAN    ReturnCode;
989     PVOID    ScreenBuffer;
990 
991     // Save the screen contents
992     ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2,
993                                   TAG_TUI_SCREENBUFFER);
994     TuiSaveScreen(ScreenBuffer);
995 
996     // Find the height
997     for (i=0; i<strlen(MessageText); i++)
998     {
999         if (MessageText[i] == '\n')
1000             height++;
1001     }
1002 
1003     // Find the width
1004     for (i=0,j=0,k=0; i<height; i++)
1005     {
1006         while ((MessageText[j] != '\n') && (MessageText[j] != 0))
1007         {
1008             j++;
1009             k++;
1010         }
1011 
1012         if (k > width)
1013             width = k;
1014 
1015         k = 0;
1016         j++;
1017     }
1018 
1019     // Calculate box area
1020     x1 = (UiScreenWidth - (width+2))/2;
1021     x2 = x1 + width + 3;
1022     y1 = ((UiScreenHeight - height - 2)/2) + 1;
1023     y2 = y1 + height + 4;
1024 
1025     // Draw the box
1026     TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
1027 
1028     // Draw the text
1029     for (i=0,j=0; i<strlen(MessageText)+1; i++)
1030     {
1031         if ((MessageText[i] == '\n') || (MessageText[i] == 0))
1032         {
1033             temp[j] = 0;
1034             j = 0;
1035             UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
1036             curline++;
1037         }
1038         else
1039             temp[j++] = MessageText[i];
1040     }
1041 
1042     EditBoxTextLength = (ULONG)strlen(EditTextBuffer);
1043     EditBoxTextLength = min(EditBoxTextLength, Length - 1);
1044     EditBoxTextPosition = 0;
1045     EditBoxLine = y2 - 2;
1046     EditBoxStartX = x1 + 3;
1047     EditBoxEndX = x2 - 3;
1048 
1049     // Draw the edit box background and the text
1050     UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1051     UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, EditTextBuffer, ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1052 
1053     // Show the cursor
1054     EditBoxCursorX = EditBoxStartX;
1055     MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);
1056     MachVideoHideShowTextCursor(TRUE);
1057 
1058     // Draw status text
1059     UiDrawStatusText("Press ENTER to continue, or ESC to cancel");
1060 
1061     VideoCopyOffScreenBufferToVRAM();
1062 
1063     //
1064     // Enter the text. Please keep in mind that the default input mode
1065     // of the edit boxes is in insertion mode, that is, you can insert
1066     // text without erasing the existing one.
1067     //
1068     for (;;)
1069     {
1070         if (MachConsKbHit())
1071         {
1072             Extended = FALSE;
1073             key = MachConsGetCh();
1074             if (key == KEY_EXTENDED)
1075             {
1076                 Extended = TRUE;
1077                 key = MachConsGetCh();
1078             }
1079 
1080             if (key == KEY_ENTER)
1081             {
1082                 ReturnCode = TRUE;
1083                 break;
1084             }
1085             else if (key == KEY_ESC)
1086             {
1087                 ReturnCode = FALSE;
1088                 break;
1089             }
1090             else if (key == KEY_BACKSPACE) // Remove a character
1091             {
1092                 if ( (EditBoxTextLength > 0) && (EditBoxTextPosition > 0) &&
1093                      (EditBoxTextPosition <= EditBoxTextLength) )
1094                 {
1095                     EditBoxTextPosition--;
1096                     memmove(EditTextBuffer + EditBoxTextPosition,
1097                             EditTextBuffer + EditBoxTextPosition + 1,
1098                             EditBoxTextLength - EditBoxTextPosition);
1099                     EditBoxTextLength--;
1100                     EditTextBuffer[EditBoxTextLength] = 0;
1101                 }
1102                 else
1103                 {
1104                     MachBeep();
1105                 }
1106             }
1107             else if (Extended && key == KEY_DELETE) // Remove a character
1108             {
1109                 if ( (EditBoxTextLength > 0) &&
1110                      (EditBoxTextPosition < EditBoxTextLength) )
1111                 {
1112                     memmove(EditTextBuffer + EditBoxTextPosition,
1113                             EditTextBuffer + EditBoxTextPosition + 1,
1114                             EditBoxTextLength - EditBoxTextPosition);
1115                     EditBoxTextLength--;
1116                     EditTextBuffer[EditBoxTextLength] = 0;
1117                 }
1118                 else
1119                 {
1120                     MachBeep();
1121                 }
1122             }
1123             else if (Extended && key == KEY_HOME) // Go to the start of the buffer
1124             {
1125                 EditBoxTextPosition = 0;
1126             }
1127             else if (Extended && key == KEY_END) // Go to the end of the buffer
1128             {
1129                 EditBoxTextPosition = EditBoxTextLength;
1130             }
1131             else if (Extended && key == KEY_RIGHT) // Go right
1132             {
1133                 if (EditBoxTextPosition < EditBoxTextLength)
1134                     EditBoxTextPosition++;
1135                 else
1136                     MachBeep();
1137             }
1138             else if (Extended && key == KEY_LEFT) // Go left
1139             {
1140                 if (EditBoxTextPosition > 0)
1141                     EditBoxTextPosition--;
1142                 else
1143                     MachBeep();
1144             }
1145             else if (!Extended) // Add this key to the buffer
1146             {
1147                 if ( (EditBoxTextLength   < Length - 1) &&
1148                      (EditBoxTextPosition < Length - 1) )
1149                 {
1150                     memmove(EditTextBuffer + EditBoxTextPosition + 1,
1151                             EditTextBuffer + EditBoxTextPosition,
1152                             EditBoxTextLength - EditBoxTextPosition);
1153                     EditTextBuffer[EditBoxTextPosition] = key;
1154                     EditBoxTextPosition++;
1155                     EditBoxTextLength++;
1156                     EditTextBuffer[EditBoxTextLength] = 0;
1157                 }
1158                 else
1159                 {
1160                     MachBeep();
1161                 }
1162             }
1163             else
1164             {
1165                 MachBeep();
1166             }
1167         }
1168 
1169         // Draw the edit box background
1170         UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1171 
1172         // Fill the text in
1173         if (EditBoxTextPosition > (EditBoxEndX - EditBoxStartX))
1174         {
1175             EditBoxTextDisplayIndex = EditBoxTextPosition - (EditBoxEndX - EditBoxStartX);
1176             EditBoxCursorX = EditBoxEndX;
1177         }
1178         else
1179         {
1180             EditBoxTextDisplayIndex = 0;
1181             EditBoxCursorX = EditBoxStartX + EditBoxTextPosition;
1182         }
1183         UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, &EditTextBuffer[EditBoxTextDisplayIndex], ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1184 
1185         // Move the cursor
1186         MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);
1187 
1188         TuiUpdateDateTime();
1189 
1190         VideoCopyOffScreenBufferToVRAM();
1191 
1192         MachHwIdle();
1193     }
1194 
1195     // Hide the cursor again
1196     MachVideoHideShowTextCursor(FALSE);
1197 
1198     // Restore the screen contents
1199     TuiRestoreScreen(ScreenBuffer);
1200     FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER);
1201 
1202     return ReturnCode;
1203 }
1204 
1205 const UIVTBL TuiVtbl =
1206 {
1207     TuiInitialize,
1208     TuiUnInitialize,
1209     TuiDrawBackdrop,
1210     TuiFillArea,
1211     TuiDrawShadow,
1212     TuiDrawBox,
1213     TuiDrawText,
1214     TuiDrawText2,
1215     TuiDrawCenteredText,
1216     TuiDrawStatusText,
1217     TuiUpdateDateTime,
1218     TuiMessageBox,
1219     TuiMessageBoxCritical,
1220     TuiDrawProgressBarCenter,
1221     TuiDrawProgressBar,
1222     TuiSetProgressBarText,
1223     TuiTickProgressBar,
1224     TuiEditBox,
1225     TuiTextToColor,
1226     TuiTextToFillStyle,
1227     TuiFadeInBackdrop,
1228     TuiFadeOut,
1229     TuiDisplayMenu,
1230     TuiDrawMenu,
1231 };
1232