xref: /reactos/boot/freeldr/freeldr/ui/tui.c (revision 465745b6)
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 #ifndef _M_ARM
20 #include <freeldr.h>
21 
22 #define TAG_TUI_SCREENBUFFER 'SiuT'
23 #define TAG_TAG_TUI_PALETTE 'PiuT'
24 
25 PVOID TextVideoBuffer = NULL;
26 
27 /*
28  * TuiPrintf()
29  * Prints formatted text to the screen
30  */
31 int TuiPrintf(const char *Format, ...)
32 {
33     int i;
34     int Length;
35     va_list ap;
36     CHAR Buffer[512];
37 
38     va_start(ap, Format);
39     Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
40     va_end(ap);
41 
42     if (Length == -1) Length = sizeof(Buffer);
43 
44     for (i = 0; i < Length; i++)
45     {
46         MachConsPutChar(Buffer[i]);
47     }
48 
49     return Length;
50 }
51 
52 BOOLEAN TuiInitialize(VOID)
53 {
54     MachVideoHideShowTextCursor(FALSE);
55     MachVideoSetTextCursorPosition(0, 0);
56     MachVideoClearScreen(ATTR(COLOR_GRAY, COLOR_BLACK));
57 
58     TextVideoBuffer = VideoAllocateOffScreenBuffer();
59     if (TextVideoBuffer == NULL)
60     {
61         return FALSE;
62     }
63 
64     return TRUE;
65 }
66 
67 VOID TuiUnInitialize(VOID)
68 {
69     if (UiUseSpecialEffects)
70     {
71         TuiFadeOut();
72     }
73     else
74     {
75         MachVideoSetDisplayMode(NULL, FALSE);
76     }
77 
78     MachVideoClearScreen(ATTR(COLOR_GRAY, COLOR_BLACK));
79     MachVideoSetTextCursorPosition(0, 0);
80     MachVideoHideShowTextCursor(TRUE);
81 }
82 
83 VOID TuiDrawBackdrop(VOID)
84 {
85     //
86     // Fill in the background (excluding title box & status bar)
87     //
88     TuiFillArea(0,
89             TUI_TITLE_BOX_CHAR_HEIGHT,
90             UiScreenWidth - 1,
91             UiScreenHeight - 2,
92             UiBackdropFillStyle,
93             ATTR(UiBackdropFgColor, UiBackdropBgColor));
94 
95     //
96     // Draw the title box
97     //
98     TuiDrawBox(0,
99             0,
100             UiScreenWidth - 1,
101             TUI_TITLE_BOX_CHAR_HEIGHT - 1,
102             D_VERT,
103             D_HORZ,
104             TRUE,
105             FALSE,
106             ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
107 
108     //
109     // Draw version text
110     //
111     TuiDrawText(2,
112             1,
113             FrLdrVersionString,
114             ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
115 
116     //
117     // Draw copyright
118     //
119     TuiDrawText(2,
120             2,
121             BY_AUTHOR,
122             ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
123     TuiDrawText(2,
124             3,
125             AUTHOR_EMAIL,
126             ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
127 
128     //
129     // Draw help text
130     //
131     TuiDrawText(UiScreenWidth - 16, 3, /*"F1 for Help"*/"F8 for Options", ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
132 
133     //
134     // Draw title text
135     //
136     TuiDrawText((UiScreenWidth - (ULONG)strlen(UiTitleBoxTitleText)) / 2,
137             2,
138             UiTitleBoxTitleText,
139             ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
140 
141     //
142     // Update the date & time
143     //
144     TuiUpdateDateTime();
145 
146     VideoCopyOffScreenBufferToVRAM();
147 }
148 
149 /*
150  * FillArea()
151  * This function assumes coordinates are zero-based
152  */
153 VOID TuiFillArea(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, CHAR FillChar, UCHAR Attr /* Color Attributes */)
154 {
155     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
156     ULONG    i, j;
157 
158     // Clip the area to the screen
159     if ((Left >= UiScreenWidth) || (Top >= UiScreenHeight))
160     {
161         return;
162     }
163     if (Right >= UiScreenWidth)
164     {
165         Right = UiScreenWidth - 1;
166     }
167     if (Bottom >= UiScreenHeight)
168     {
169         Bottom = UiScreenHeight - 1;
170     }
171 
172     // Loop through each line and fill it in
173     for (i=Top; i<=Bottom; i++)
174     {
175         // Loop through each character (column) in the line and fill it in
176         for (j=Left; j<=Right; j++)
177         {
178             ScreenMemory[((i*2)*UiScreenWidth)+(j*2)] = (UCHAR)FillChar;
179             ScreenMemory[((i*2)*UiScreenWidth)+(j*2)+1] = Attr;
180         }
181     }
182 }
183 
184 /*
185  * DrawShadow()
186  * This function assumes coordinates are zero-based
187  */
188 VOID TuiDrawShadow(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom)
189 {
190     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
191     ULONG    Idx;
192 
193     // Shade the bottom of the area
194     if (Bottom < (UiScreenHeight - 1))
195     {
196         if (UiScreenHeight < 34)
197         {
198             Idx=Left + 2;
199         }
200         else
201         {
202             Idx=Left + 1;
203         }
204 
205         for (; Idx<=Right; Idx++)
206         {
207             ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+(Idx*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
208         }
209     }
210 
211     // Shade the right of the area
212     if (Right < (UiScreenWidth - 1))
213     {
214         for (Idx=Top+1; Idx<=Bottom; Idx++)
215         {
216             ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
217         }
218     }
219     if (UiScreenHeight < 34)
220     {
221         if ((Right + 1) < (UiScreenWidth - 1))
222         {
223             for (Idx=Top+1; Idx<=Bottom; Idx++)
224             {
225                 ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
226             }
227         }
228     }
229 
230     // Shade the bottom right corner
231     if ((Right < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
232     {
233         ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
234     }
235     if (UiScreenHeight < 34)
236     {
237         if (((Right + 1) < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
238         {
239             ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
240         }
241     }
242 }
243 
244 /*
245  * DrawBox()
246  * This function assumes coordinates are zero-based
247  */
248 VOID TuiDrawBox(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, UCHAR VertStyle, UCHAR HorzStyle, BOOLEAN Fill, BOOLEAN Shadow, UCHAR Attr)
249 {
250     UCHAR    ULCorner, URCorner, LLCorner, LRCorner;
251 
252     // Calculate the corner values
253     if (HorzStyle == HORZ)
254     {
255         if (VertStyle == VERT)
256         {
257             ULCorner = UL;
258             URCorner = UR;
259             LLCorner = LL;
260             LRCorner = LR;
261         }
262         else // VertStyle == D_VERT
263         {
264             ULCorner = VD_UL;
265             URCorner = VD_UR;
266             LLCorner = VD_LL;
267             LRCorner = VD_LR;
268         }
269     }
270     else // HorzStyle == D_HORZ
271     {
272         if (VertStyle == VERT)
273         {
274             ULCorner = HD_UL;
275             URCorner = HD_UR;
276             LLCorner = HD_LL;
277             LRCorner = HD_LR;
278         }
279         else // VertStyle == D_VERT
280         {
281             ULCorner = D_UL;
282             URCorner = D_UR;
283             LLCorner = D_LL;
284             LRCorner = D_LR;
285         }
286     }
287 
288     // Fill in box background
289     if (Fill)
290     {
291         TuiFillArea(Left, Top, Right, Bottom, ' ', Attr);
292     }
293 
294     // Fill in corners
295     TuiFillArea(Left, Top, Left, Top, ULCorner, Attr);
296     TuiFillArea(Right, Top, Right, Top, URCorner, Attr);
297     TuiFillArea(Left, Bottom, Left, Bottom, LLCorner, Attr);
298     TuiFillArea(Right, Bottom, Right, Bottom, LRCorner, Attr);
299 
300     // Fill in left line
301     TuiFillArea(Left, Top+1, Left, Bottom-1, VertStyle, Attr);
302     // Fill in top line
303     TuiFillArea(Left+1, Top, Right-1, Top, HorzStyle, Attr);
304     // Fill in right line
305     TuiFillArea(Right, Top+1, Right, Bottom-1, VertStyle, Attr);
306     // Fill in bottom line
307     TuiFillArea(Left+1, Bottom, Right-1, Bottom, HorzStyle, Attr);
308 
309     // Draw the shadow
310     if (Shadow)
311     {
312         TuiDrawShadow(Left, Top, Right, Bottom);
313     }
314 }
315 
316 /*
317  * DrawText()
318  * This function assumes coordinates are zero-based
319  */
320 VOID TuiDrawText(ULONG X, ULONG Y, PCSTR Text, UCHAR Attr)
321 {
322     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
323     ULONG    i, j;
324 
325     // Draw the text
326     for (i = X, j = 0; Text[j] && i < UiScreenWidth; i++, j++)
327     {
328         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)] = (UCHAR)Text[j];
329         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)+1] = Attr;
330     }
331 }
332 
333 /*
334  * DrawText2()
335  * This function assumes coordinates are zero-based.
336  * MaxNumChars is the maximum number of characters to display.
337  * If MaxNumChars == 0, then display the whole string.
338  */
339 VOID TuiDrawText2(ULONG X, ULONG Y, ULONG MaxNumChars, PCSTR Text, UCHAR Attr)
340 {
341     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
342     ULONG    i, j;
343 
344     // Draw the text
345     for (i = X, j = 0; Text[j] && i < UiScreenWidth && (MaxNumChars > 0 ? j < MaxNumChars : TRUE); i++, j++)
346     {
347         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)] = (UCHAR)Text[j];
348         ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)+1] = Attr;
349     }
350 }
351 
352 VOID TuiDrawCenteredText(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, PCSTR TextString, UCHAR Attr)
353 {
354     SIZE_T    TextLength;
355     ULONG    BoxWidth;
356     ULONG    BoxHeight;
357     ULONG    LineBreakCount;
358     SIZE_T    Index;
359     SIZE_T    LastIndex;
360     ULONG    RealLeft;
361     ULONG    RealTop;
362     ULONG    X;
363     ULONG    Y;
364     CHAR    Temp[2];
365 
366     TextLength = strlen(TextString);
367 
368     // Count the new lines and the box width
369     LineBreakCount = 0;
370     BoxWidth = 0;
371     LastIndex = 0;
372     for (Index=0; Index<TextLength; Index++)
373     {
374         if (TextString[Index] == '\n')
375         {
376             LastIndex = Index;
377             LineBreakCount++;
378         }
379         else
380         {
381             if ((Index - LastIndex) > BoxWidth)
382             {
383                 BoxWidth = (ULONG)(Index - LastIndex);
384             }
385         }
386     }
387 
388     BoxHeight = LineBreakCount + 1;
389 
390     RealLeft = (((Right - Left) - BoxWidth) / 2) + Left;
391     RealTop = (((Bottom - Top) - BoxHeight) / 2) + Top;
392 
393     LastIndex = 0;
394     for (Index=0; Index<TextLength; Index++)
395     {
396         if (TextString[Index] == '\n')
397         {
398             RealTop++;
399             LastIndex = 0;
400         }
401         else
402         {
403             X = (ULONG)(RealLeft + LastIndex);
404             Y = RealTop;
405             LastIndex++;
406             Temp[0] = TextString[Index];
407             Temp[1] = 0;
408             TuiDrawText(X, Y, Temp, Attr);
409         }
410     }
411 }
412 
413 VOID TuiDrawStatusText(PCSTR StatusText)
414 {
415     SIZE_T    i;
416 
417     TuiDrawText(0, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
418     TuiDrawText(1, UiScreenHeight-1, StatusText, ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
419 
420     for (i=strlen(StatusText)+1; i<UiScreenWidth; i++)
421     {
422         TuiDrawText((ULONG)i, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
423     }
424 
425     VideoCopyOffScreenBufferToVRAM();
426 }
427 
428 VOID TuiUpdateDateTime(VOID)
429 {
430     TIMEINFO*    TimeInfo;
431     char    DateString[40];
432     CHAR    TimeString[40];
433     CHAR    TempString[20];
434     BOOLEAN    PMHour = FALSE;
435 
436     /* Don't draw the time if this has been disabled */
437     if (!UiDrawTime) return;
438 
439     TimeInfo = ArcGetTime();
440     if (TimeInfo->Year < 1 || 9999 < TimeInfo->Year ||
441         TimeInfo->Month < 1 || 12 < TimeInfo->Month ||
442         TimeInfo->Day < 1 || 31 < TimeInfo->Day ||
443         23 < TimeInfo->Hour ||
444         59 < TimeInfo->Minute ||
445         59 < TimeInfo->Second)
446     {
447         /* This happens on QEmu sometimes. We just skip updating */
448         return;
449     }
450     // Get the month name
451     strcpy(DateString, UiMonthNames[TimeInfo->Month - 1]);
452     // Get the day
453     _itoa(TimeInfo->Day, TempString, 10);
454     // Get the day postfix
455     if (1 == TimeInfo->Day || 21 == TimeInfo->Day || 31 == TimeInfo->Day)
456     {
457         strcat(TempString, "st");
458     }
459     else if (2 == TimeInfo->Day || 22 == TimeInfo->Day)
460     {
461         strcat(TempString, "nd");
462     }
463     else if (3 == TimeInfo->Day || 23 == TimeInfo->Day)
464     {
465         strcat(TempString, "rd");
466     }
467     else
468     {
469         strcat(TempString, "th");
470     }
471 
472     // Add the day to the date
473     strcat(DateString, TempString);
474     strcat(DateString, " ");
475 
476     // Get the year and add it to the date
477     _itoa(TimeInfo->Year, TempString, 10);
478     strcat(DateString, TempString);
479 
480     // Draw the date
481     TuiDrawText(UiScreenWidth-(ULONG)strlen(DateString)-2, 1, DateString, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
482 
483     // Get the hour and change from 24-hour mode to 12-hour
484     if (TimeInfo->Hour > 12)
485     {
486         TimeInfo->Hour -= 12;
487         PMHour = TRUE;
488     }
489     if (TimeInfo->Hour == 0)
490     {
491         TimeInfo->Hour = 12;
492     }
493     _itoa(TimeInfo->Hour, TempString, 10);
494     strcpy(TimeString, "    ");
495     strcat(TimeString, TempString);
496     strcat(TimeString, ":");
497     _itoa(TimeInfo->Minute, TempString, 10);
498     if (TimeInfo->Minute < 10)
499     {
500         strcat(TimeString, "0");
501     }
502     strcat(TimeString, TempString);
503     strcat(TimeString, ":");
504     _itoa(TimeInfo->Second, TempString, 10);
505     if (TimeInfo->Second < 10)
506     {
507         strcat(TimeString, "0");
508     }
509     strcat(TimeString, TempString);
510     if (PMHour)
511     {
512         strcat(TimeString, " PM");
513     }
514     else
515     {
516         strcat(TimeString, " AM");
517     }
518 
519     // Draw the time
520     TuiDrawText(UiScreenWidth-(ULONG)strlen(TimeString)-2, 2, TimeString, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
521 }
522 
523 VOID TuiSaveScreen(PUCHAR Buffer)
524 {
525     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
526     ULONG    i;
527 
528     for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
529     {
530         Buffer[i] = ScreenMemory[i];
531     }
532 }
533 
534 VOID TuiRestoreScreen(PUCHAR Buffer)
535 {
536     PUCHAR    ScreenMemory = (PUCHAR)TextVideoBuffer;
537     ULONG    i;
538 
539     for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
540     {
541         ScreenMemory[i] = Buffer[i];
542     }
543 }
544 
545 VOID TuiMessageBox(PCSTR MessageText)
546 {
547     PVOID    ScreenBuffer;
548 
549     // Save the screen contents
550     ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2,
551                                   TAG_TUI_SCREENBUFFER);
552     TuiSaveScreen(ScreenBuffer);
553 
554     // Display the message box
555     TuiMessageBoxCritical(MessageText);
556 
557     // Restore the screen contents
558     TuiRestoreScreen(ScreenBuffer);
559     FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER);
560 }
561 
562 VOID TuiMessageBoxCritical(PCSTR MessageText)
563 {
564     int        width = 8;
565     unsigned int    height = 1;
566     int        curline = 0;
567     int        k;
568     size_t        i , j;
569     int        x1, x2, y1, y2;
570     char    temp[260];
571     char    key;
572 
573     // Find the height
574     for (i=0; i<strlen(MessageText); i++)
575     {
576         if (MessageText[i] == '\n')
577             height++;
578     }
579 
580     // Find the width
581     for (i=0,j=0,k=0; i<height; i++)
582     {
583         while ((MessageText[j] != '\n') && (MessageText[j] != 0))
584         {
585             j++;
586             k++;
587         }
588 
589         if (k > width)
590             width = k;
591 
592         k = 0;
593         j++;
594     }
595 
596     // Calculate box area
597     x1 = (UiScreenWidth - (width+2))/2;
598     x2 = x1 + width + 3;
599     y1 = ((UiScreenHeight - height - 2)/2) + 1;
600     y2 = y1 + height + 4;
601 
602     // Draw the box
603     TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
604 
605     // Draw the text
606     for (i=0,j=0; i<strlen(MessageText)+1; i++)
607     {
608         if ((MessageText[i] == '\n') || (MessageText[i] == 0))
609         {
610             temp[j] = 0;
611             j = 0;
612             UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
613             curline++;
614         }
615         else
616             temp[j++] = MessageText[i];
617     }
618 
619     // Draw OK button
620     strcpy(temp, "   OK   ");
621     UiDrawText(x1+((x2-x1)/2)-3, y2-2, temp, ATTR(COLOR_BLACK, COLOR_GRAY));
622 
623     // Draw status text
624     UiDrawStatusText("Press ENTER to continue");
625 
626     VideoCopyOffScreenBufferToVRAM();
627 
628     for (;;)
629     {
630         if (MachConsKbHit())
631         {
632             key = MachConsGetCh();
633             if (key == KEY_EXTENDED)
634                 key = MachConsGetCh();
635 
636             if ((key == KEY_ENTER) || (key == KEY_SPACE) || (key == KEY_ESC))
637                 break;
638         }
639 
640         TuiUpdateDateTime();
641 
642         VideoCopyOffScreenBufferToVRAM();
643 
644         MachHwIdle();
645     }
646 }
647 
648 VOID TuiDrawProgressBarCenter(ULONG Position, ULONG Range, PCHAR ProgressText)
649 {
650     ULONG    Left, Top, Right, Bottom;
651     ULONG    Width = 50; // Allow for 50 "bars"
652     ULONG    Height = 2;
653 
654     Left = (UiScreenWidth - Width - 4) / 2;
655     Right = Left + Width + 3;
656     Top = (UiScreenHeight - Height - 2) / 2;
657     Top += 2;
658     Bottom = Top + Height + 1;
659 
660     TuiDrawProgressBar(Left, Top, Right, Bottom, Position, Range, ProgressText);
661 }
662 
663 VOID TuiDrawProgressBar(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, ULONG Position, ULONG Range, PCHAR ProgressText)
664 {
665     ULONG    i;
666     ULONG    ProgressBarWidth = (Right - Left) - 3;
667 
668     // First make sure the progress bar text fits
669     UiTruncateStringEllipsis(ProgressText, ProgressBarWidth - 4);
670 
671     if (Position > Range)
672     {
673         Position = Range;
674     }
675 
676     // Draw the box
677     TuiDrawBox(Left, Top, Right, Bottom, VERT, HORZ, TRUE, TRUE, ATTR(UiMenuFgColor, UiMenuBgColor));
678 
679     //
680     //  Draw the "Loading..." text
681     //
682     TuiDrawCenteredText(Left + 2, Top + 2, Right - 2, Top + 2, ProgressText, ATTR(UiTextColor, UiMenuBgColor));
683 
684     // Draw the percent complete
685     for (i=0; i<(Position*ProgressBarWidth)/Range; i++)
686     {
687         TuiDrawText(Left+2+i, Top+2, "\xDB", ATTR(UiTextColor, UiMenuBgColor));
688     }
689 
690     // Draw the shadow
691     for (; i<ProgressBarWidth; i++)
692     {
693         TuiDrawText(Left+2+i, Top+2, "\xB2", ATTR(UiTextColor, UiMenuBgColor));
694     }
695 
696     TuiUpdateDateTime();
697     VideoCopyOffScreenBufferToVRAM();
698 }
699 
700 UCHAR TuiTextToColor(PCSTR ColorText)
701 {
702     static const struct
703     {
704         PCSTR ColorName;
705         UCHAR ColorValue;
706     } Colors[] =
707     {
708         {"Black"  , COLOR_BLACK  },
709         {"Blue"   , COLOR_BLUE   },
710         {"Green"  , COLOR_GREEN  },
711         {"Cyan"   , COLOR_CYAN   },
712         {"Red"    , COLOR_RED    },
713         {"Magenta", COLOR_MAGENTA},
714         {"Brown"  , COLOR_BROWN  },
715         {"Gray"   , COLOR_GRAY   },
716         {"DarkGray"    , COLOR_DARKGRAY    },
717         {"LightBlue"   , COLOR_LIGHTBLUE   },
718         {"LightGreen"  , COLOR_LIGHTGREEN  },
719         {"LightCyan"   , COLOR_LIGHTCYAN   },
720         {"LightRed"    , COLOR_LIGHTRED    },
721         {"LightMagenta", COLOR_LIGHTMAGENTA},
722         {"Yellow"      , COLOR_YELLOW      },
723         {"White"       , COLOR_WHITE       },
724     };
725     ULONG i;
726 
727     for (i = 0; i < sizeof(Colors)/sizeof(Colors[0]); ++i)
728     {
729         if (_stricmp(ColorText, Colors[i].ColorName) == 0)
730             return Colors[i].ColorValue;
731     }
732 
733     return COLOR_BLACK;
734 }
735 
736 UCHAR TuiTextToFillStyle(PCSTR FillStyleText)
737 {
738     static const struct
739     {
740         PCSTR FillStyleName;
741         UCHAR FillStyleValue;
742     } FillStyles[] =
743     {
744         {"Light" , LIGHT_FILL },
745         {"Medium", MEDIUM_FILL},
746         {"Dark"  , DARK_FILL  },
747     };
748     ULONG i;
749 
750     for (i = 0; i < sizeof(FillStyles)/sizeof(FillStyles[0]); ++i)
751     {
752         if (_stricmp(FillStyleText, FillStyles[i].FillStyleName) == 0)
753             return FillStyles[i].FillStyleValue;
754     }
755 
756     return LIGHT_FILL;
757 }
758 
759 VOID TuiFadeInBackdrop(VOID)
760 {
761     PPALETTE_ENTRY TuiFadePalette = NULL;
762 
763     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
764     {
765         TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64,
766                                                         TAG_TAG_TUI_PALETTE);
767 
768         if (TuiFadePalette != NULL)
769         {
770             VideoSavePaletteState(TuiFadePalette, 64);
771             VideoSetAllColorsToBlack(64);
772         }
773     }
774 
775     // Draw the backdrop and title box
776     TuiDrawBackdrop();
777 
778     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
779     {
780         VideoFadeIn(TuiFadePalette, 64);
781         FrLdrTempFree(TuiFadePalette, TAG_TAG_TUI_PALETTE);
782     }
783 }
784 
785 VOID TuiFadeOut(VOID)
786 {
787     PPALETTE_ENTRY TuiFadePalette = NULL;
788 
789     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
790     {
791         TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64,
792                                                         TAG_TAG_TUI_PALETTE);
793 
794         if (TuiFadePalette != NULL)
795         {
796             VideoSavePaletteState(TuiFadePalette, 64);
797         }
798     }
799 
800     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
801     {
802         VideoFadeOut(64);
803     }
804 
805     MachVideoSetDisplayMode(NULL, FALSE);
806 
807     if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
808     {
809         VideoRestorePaletteState(TuiFadePalette, 64);
810         FrLdrTempFree(TuiFadePalette, TAG_TAG_TUI_PALETTE);
811     }
812 
813 }
814 
815 BOOLEAN TuiEditBox(PCSTR MessageText, PCHAR EditTextBuffer, ULONG Length)
816 {
817     INT        width = 8;
818     ULONG    height = 1;
819     INT        curline = 0;
820     INT        k;
821     size_t    i , j;
822     INT        x1, x2, y1, y2;
823     CHAR    temp[260];
824     CHAR    key;
825     BOOLEAN    Extended;
826     INT        EditBoxLine;
827     ULONG    EditBoxStartX, EditBoxEndX;
828     INT        EditBoxCursorX;
829     ULONG    EditBoxTextLength, EditBoxTextPosition;
830     INT        EditBoxTextDisplayIndex;
831     BOOLEAN    ReturnCode;
832     PVOID    ScreenBuffer;
833 
834     // Save the screen contents
835     ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2,
836                                   TAG_TUI_SCREENBUFFER);
837     TuiSaveScreen(ScreenBuffer);
838 
839     // Find the height
840     for (i=0; i<strlen(MessageText); i++)
841     {
842         if (MessageText[i] == '\n')
843             height++;
844     }
845 
846     // Find the width
847     for (i=0,j=0,k=0; i<height; i++)
848     {
849         while ((MessageText[j] != '\n') && (MessageText[j] != 0))
850         {
851             j++;
852             k++;
853         }
854 
855         if (k > width)
856             width = k;
857 
858         k = 0;
859         j++;
860     }
861 
862     // Calculate box area
863     x1 = (UiScreenWidth - (width+2))/2;
864     x2 = x1 + width + 3;
865     y1 = ((UiScreenHeight - height - 2)/2) + 1;
866     y2 = y1 + height + 4;
867 
868     // Draw the box
869     TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
870 
871     // Draw the text
872     for (i=0,j=0; i<strlen(MessageText)+1; i++)
873     {
874         if ((MessageText[i] == '\n') || (MessageText[i] == 0))
875         {
876             temp[j] = 0;
877             j = 0;
878             UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
879             curline++;
880         }
881         else
882             temp[j++] = MessageText[i];
883     }
884 
885     EditBoxTextLength = (ULONG)strlen(EditTextBuffer);
886     EditBoxTextLength = min(EditBoxTextLength, Length - 1);
887     EditBoxTextPosition = 0;
888     EditBoxLine = y2 - 2;
889     EditBoxStartX = x1 + 3;
890     EditBoxEndX = x2 - 3;
891 
892     // Draw the edit box background and the text
893     UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
894     UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, EditTextBuffer, ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
895 
896     // Show the cursor
897     EditBoxCursorX = EditBoxStartX;
898     MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);
899     MachVideoHideShowTextCursor(TRUE);
900 
901     // Draw status text
902     UiDrawStatusText("Press ENTER to continue, or ESC to cancel");
903 
904     VideoCopyOffScreenBufferToVRAM();
905 
906     //
907     // Enter the text. Please keep in mind that the default input mode
908     // of the edit boxes is in insertion mode, that is, you can insert
909     // text without erasing the existing one.
910     //
911     for (;;)
912     {
913         if (MachConsKbHit())
914         {
915             Extended = FALSE;
916             key = MachConsGetCh();
917             if (key == KEY_EXTENDED)
918             {
919                 Extended = TRUE;
920                 key = MachConsGetCh();
921             }
922 
923             if (key == KEY_ENTER)
924             {
925                 ReturnCode = TRUE;
926                 break;
927             }
928             else if (key == KEY_ESC)
929             {
930                 ReturnCode = FALSE;
931                 break;
932             }
933             else if (key == KEY_BACKSPACE) // Remove a character
934             {
935                 if ( (EditBoxTextLength > 0) && (EditBoxTextPosition > 0) &&
936                      (EditBoxTextPosition <= EditBoxTextLength) )
937                 {
938                     EditBoxTextPosition--;
939                     memmove(EditTextBuffer + EditBoxTextPosition,
940                             EditTextBuffer + EditBoxTextPosition + 1,
941                             EditBoxTextLength - EditBoxTextPosition);
942                     EditBoxTextLength--;
943                     EditTextBuffer[EditBoxTextLength] = 0;
944                 }
945                 else
946                 {
947                     MachBeep();
948                 }
949             }
950             else if (Extended && key == KEY_DELETE) // Remove a character
951             {
952                 if ( (EditBoxTextLength > 0) &&
953                      (EditBoxTextPosition < EditBoxTextLength) )
954                 {
955                     memmove(EditTextBuffer + EditBoxTextPosition,
956                             EditTextBuffer + EditBoxTextPosition + 1,
957                             EditBoxTextLength - EditBoxTextPosition);
958                     EditBoxTextLength--;
959                     EditTextBuffer[EditBoxTextLength] = 0;
960                 }
961                 else
962                 {
963                     MachBeep();
964                 }
965             }
966             else if (Extended && key == KEY_HOME) // Go to the start of the buffer
967             {
968                 EditBoxTextPosition = 0;
969             }
970             else if (Extended && key == KEY_END) // Go to the end of the buffer
971             {
972                 EditBoxTextPosition = EditBoxTextLength;
973             }
974             else if (Extended && key == KEY_RIGHT) // Go right
975             {
976                 if (EditBoxTextPosition < EditBoxTextLength)
977                     EditBoxTextPosition++;
978                 else
979                     MachBeep();
980             }
981             else if (Extended && key == KEY_LEFT) // Go left
982             {
983                 if (EditBoxTextPosition > 0)
984                     EditBoxTextPosition--;
985                 else
986                     MachBeep();
987             }
988             else if (!Extended) // Add this key to the buffer
989             {
990                 if ( (EditBoxTextLength   < Length - 1) &&
991                      (EditBoxTextPosition < Length - 1) )
992                 {
993                     memmove(EditTextBuffer + EditBoxTextPosition + 1,
994                             EditTextBuffer + EditBoxTextPosition,
995                             EditBoxTextLength - EditBoxTextPosition);
996                     EditTextBuffer[EditBoxTextPosition] = key;
997                     EditBoxTextPosition++;
998                     EditBoxTextLength++;
999                     EditTextBuffer[EditBoxTextLength] = 0;
1000                 }
1001                 else
1002                 {
1003                     MachBeep();
1004                 }
1005             }
1006             else
1007             {
1008                 MachBeep();
1009             }
1010         }
1011 
1012         // Draw the edit box background
1013         UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1014 
1015         // Fill the text in
1016         if (EditBoxTextPosition > (EditBoxEndX - EditBoxStartX))
1017         {
1018             EditBoxTextDisplayIndex = EditBoxTextPosition - (EditBoxEndX - EditBoxStartX);
1019             EditBoxCursorX = EditBoxEndX;
1020         }
1021         else
1022         {
1023             EditBoxTextDisplayIndex = 0;
1024             EditBoxCursorX = EditBoxStartX + EditBoxTextPosition;
1025         }
1026         UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, &EditTextBuffer[EditBoxTextDisplayIndex], ATTR(UiEditBoxTextColor, UiEditBoxBgColor));
1027 
1028         // Move the cursor
1029         MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);
1030 
1031         TuiUpdateDateTime();
1032 
1033         VideoCopyOffScreenBufferToVRAM();
1034 
1035         MachHwIdle();
1036     }
1037 
1038     // Hide the cursor again
1039     MachVideoHideShowTextCursor(FALSE);
1040 
1041     // Restore the screen contents
1042     TuiRestoreScreen(ScreenBuffer);
1043     FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER);
1044 
1045     return ReturnCode;
1046 }
1047 
1048 const UIVTBL TuiVtbl =
1049 {
1050     TuiInitialize,
1051     TuiUnInitialize,
1052     TuiDrawBackdrop,
1053     TuiFillArea,
1054     TuiDrawShadow,
1055     TuiDrawBox,
1056     TuiDrawText,
1057     TuiDrawText2,
1058     TuiDrawCenteredText,
1059     TuiDrawStatusText,
1060     TuiUpdateDateTime,
1061     TuiMessageBox,
1062     TuiMessageBoxCritical,
1063     TuiDrawProgressBarCenter,
1064     TuiDrawProgressBar,
1065     TuiEditBox,
1066     TuiTextToColor,
1067     TuiTextToFillStyle,
1068     TuiFadeInBackdrop,
1069     TuiFadeOut,
1070     TuiDisplayMenu,
1071     TuiDrawMenu,
1072 };
1073 #endif
1074