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