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