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 } 630 631 VOID TuiMessageBox(PCSTR MessageText) 632 { 633 PVOID ScreenBuffer; 634 635 // Save the screen contents 636 ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2, 637 TAG_TUI_SCREENBUFFER); 638 TuiSaveScreen(ScreenBuffer); 639 640 // Display the message box 641 TuiMessageBoxCritical(MessageText); 642 643 // Restore the screen contents 644 TuiRestoreScreen(ScreenBuffer); 645 FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER); 646 } 647 648 VOID TuiMessageBoxCritical(PCSTR MessageText) 649 { 650 int width = 8; 651 unsigned int height = 1; 652 int curline = 0; 653 int k; 654 size_t i , j; 655 int x1, x2, y1, y2; 656 char temp[260]; 657 char key; 658 659 // Find the height 660 for (i=0; i<strlen(MessageText); i++) 661 { 662 if (MessageText[i] == '\n') 663 height++; 664 } 665 666 // Find the width 667 for (i=0,j=0,k=0; i<height; i++) 668 { 669 while ((MessageText[j] != '\n') && (MessageText[j] != 0)) 670 { 671 j++; 672 k++; 673 } 674 675 if (k > width) 676 width = k; 677 678 k = 0; 679 j++; 680 } 681 682 // Calculate box area 683 x1 = (UiScreenWidth - (width+2))/2; 684 x2 = x1 + width + 3; 685 y1 = ((UiScreenHeight - height - 2)/2) + 1; 686 y2 = y1 + height + 4; 687 688 // Draw the box 689 TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor)); 690 691 // Draw the text 692 for (i=0,j=0; i<strlen(MessageText)+1; i++) 693 { 694 if ((MessageText[i] == '\n') || (MessageText[i] == 0)) 695 { 696 temp[j] = 0; 697 j = 0; 698 UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor)); 699 curline++; 700 } 701 else 702 temp[j++] = MessageText[i]; 703 } 704 705 // Draw OK button 706 strcpy(temp, " OK "); 707 UiDrawText(x1+((x2-x1)/2)-3, y2-2, temp, ATTR(COLOR_BLACK, COLOR_GRAY)); 708 709 // Draw status text 710 UiDrawStatusText("Press ENTER to continue"); 711 712 VideoCopyOffScreenBufferToVRAM(); 713 714 for (;;) 715 { 716 if (MachConsKbHit()) 717 { 718 key = MachConsGetCh(); 719 if (key == KEY_EXTENDED) 720 key = MachConsGetCh(); 721 722 if ((key == KEY_ENTER) || (key == KEY_SPACE) || (key == KEY_ESC)) 723 break; 724 } 725 726 TuiUpdateDateTime(); 727 728 VideoCopyOffScreenBufferToVRAM(); 729 730 MachHwIdle(); 731 } 732 } 733 734 static VOID 735 TuiSetProgressBarText( 736 _In_ PCSTR ProgressText) 737 { 738 ULONG ProgressBarWidth; 739 CHAR ProgressString[256]; 740 741 /* Make sure the progress bar is enabled */ 742 ASSERT(UiProgressBar.Show); 743 744 /* Calculate the width of the bar proper */ 745 ProgressBarWidth = UiProgressBar.Right - UiProgressBar.Left + 1; 746 747 /* First make sure the progress bar text fits */ 748 RtlStringCbCopyA(ProgressString, sizeof(ProgressString), ProgressText); 749 TuiTruncateStringEllipsis(ProgressString, ProgressBarWidth); 750 751 /* Clear the text area */ 752 TuiFillArea(UiProgressBar.Left, UiProgressBar.Top, 753 UiProgressBar.Right, UiProgressBar.Bottom - 1, 754 ' ', ATTR(UiTextColor, UiMenuBgColor)); 755 756 /* Draw the "Loading..." text */ 757 TuiDrawCenteredText(UiProgressBar.Left, UiProgressBar.Top, 758 UiProgressBar.Right, UiProgressBar.Bottom - 1, 759 ProgressString, ATTR(UiTextColor, UiMenuBgColor)); 760 } 761 762 static VOID 763 TuiTickProgressBar( 764 _In_ ULONG SubPercentTimes100) 765 { 766 ULONG ProgressBarWidth; 767 ULONG FillCount; 768 769 /* Make sure the progress bar is enabled */ 770 ASSERT(UiProgressBar.Show); 771 772 ASSERT(SubPercentTimes100 <= (100 * 100)); 773 774 /* Calculate the width of the bar proper */ 775 ProgressBarWidth = UiProgressBar.Right - UiProgressBar.Left + 1; 776 777 /* Compute fill count */ 778 // FillCount = (ProgressBarWidth * Position) / Range; 779 FillCount = ProgressBarWidth * SubPercentTimes100 / (100 * 100); 780 781 /* Fill the progress bar */ 782 /* Draw the percent complete -- Use the fill character */ 783 if (FillCount > 0) 784 { 785 TuiFillArea(UiProgressBar.Left, UiProgressBar.Bottom, 786 UiProgressBar.Left + FillCount - 1, UiProgressBar.Bottom, 787 '\xDB', ATTR(UiTextColor, UiMenuBgColor)); 788 } 789 /* Fill the remaining with shadow blanks */ 790 TuiFillArea(UiProgressBar.Left + FillCount, UiProgressBar.Bottom, 791 UiProgressBar.Right, UiProgressBar.Bottom, 792 '\xB2', ATTR(UiTextColor, UiMenuBgColor)); 793 794 TuiUpdateDateTime(); 795 VideoCopyOffScreenBufferToVRAM(); 796 } 797 798 static VOID 799 TuiDrawProgressBar( 800 _In_ ULONG Left, 801 _In_ ULONG Top, 802 _In_ ULONG Right, 803 _In_ ULONG Bottom, 804 _In_ PCSTR ProgressText); 805 806 static VOID 807 TuiDrawProgressBarCenter( 808 _In_ PCSTR ProgressText) 809 { 810 ULONG Left, Top, Right, Bottom, Width, Height; 811 812 /* Build the coordinates and sizes */ 813 Height = 2; 814 Width = 50; // Allow for 50 "bars" 815 Left = (UiScreenWidth - Width) / 2; 816 Top = (UiScreenHeight - Height + 4) / 2; 817 Right = Left + Width - 1; 818 Bottom = Top + Height - 1; 819 820 /* Inflate to include the box margins */ 821 Left -= 2; 822 Right += 2; 823 Top -= 1; 824 Bottom += 1; 825 826 /* Draw the progress bar */ 827 TuiDrawProgressBar(Left, Top, Right, Bottom, ProgressText); 828 } 829 830 static VOID 831 TuiDrawProgressBar( 832 _In_ ULONG Left, 833 _In_ ULONG Top, 834 _In_ ULONG Right, 835 _In_ ULONG Bottom, 836 _In_ PCSTR ProgressText) 837 { 838 /* Draw the box */ 839 TuiDrawBox(Left, Top, Right, Bottom, 840 VERT, HORZ, TRUE, TRUE, 841 ATTR(UiMenuFgColor, UiMenuBgColor)); 842 843 /* Exclude the box margins */ 844 Left += 2; 845 Right -= 2; 846 Top += 1; 847 Bottom -= 1; 848 849 UiInitProgressBar(Left, Top, Right, Bottom, ProgressText); 850 } 851 852 UCHAR TuiTextToColor(PCSTR ColorText) 853 { 854 static const struct 855 { 856 PCSTR ColorName; 857 UCHAR ColorValue; 858 } Colors[] = 859 { 860 {"Black" , COLOR_BLACK }, 861 {"Blue" , COLOR_BLUE }, 862 {"Green" , COLOR_GREEN }, 863 {"Cyan" , COLOR_CYAN }, 864 {"Red" , COLOR_RED }, 865 {"Magenta", COLOR_MAGENTA}, 866 {"Brown" , COLOR_BROWN }, 867 {"Gray" , COLOR_GRAY }, 868 {"DarkGray" , COLOR_DARKGRAY }, 869 {"LightBlue" , COLOR_LIGHTBLUE }, 870 {"LightGreen" , COLOR_LIGHTGREEN }, 871 {"LightCyan" , COLOR_LIGHTCYAN }, 872 {"LightRed" , COLOR_LIGHTRED }, 873 {"LightMagenta", COLOR_LIGHTMAGENTA}, 874 {"Yellow" , COLOR_YELLOW }, 875 {"White" , COLOR_WHITE }, 876 }; 877 ULONG i; 878 879 if (_stricmp(ColorText, "Default") == 0) 880 return MachDefaultTextColor; 881 882 for (i = 0; i < RTL_NUMBER_OF(Colors); ++i) 883 { 884 if (_stricmp(ColorText, Colors[i].ColorName) == 0) 885 return Colors[i].ColorValue; 886 } 887 888 return COLOR_BLACK; 889 } 890 891 UCHAR TuiTextToFillStyle(PCSTR FillStyleText) 892 { 893 static const struct 894 { 895 PCSTR FillStyleName; 896 UCHAR FillStyleValue; 897 } FillStyles[] = 898 { 899 {"None" , ' '}, 900 {"Light" , LIGHT_FILL }, 901 {"Medium", MEDIUM_FILL}, 902 {"Dark" , DARK_FILL }, 903 }; 904 ULONG i; 905 906 for (i = 0; i < RTL_NUMBER_OF(FillStyles); ++i) 907 { 908 if (_stricmp(FillStyleText, FillStyles[i].FillStyleName) == 0) 909 return FillStyles[i].FillStyleValue; 910 } 911 912 return LIGHT_FILL; 913 } 914 915 VOID TuiFadeInBackdrop(VOID) 916 { 917 PPALETTE_ENTRY TuiFadePalette = NULL; 918 919 if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed()) 920 { 921 TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64, 922 TAG_TUI_PALETTE); 923 924 if (TuiFadePalette != NULL) 925 { 926 VideoSavePaletteState(TuiFadePalette, 64); 927 VideoSetAllColorsToBlack(64); 928 } 929 } 930 931 // Draw the backdrop and title box 932 TuiDrawBackdrop(); 933 934 if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL) 935 { 936 VideoFadeIn(TuiFadePalette, 64); 937 FrLdrTempFree(TuiFadePalette, TAG_TUI_PALETTE); 938 } 939 } 940 941 VOID TuiFadeOut(VOID) 942 { 943 PPALETTE_ENTRY TuiFadePalette = NULL; 944 945 if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed()) 946 { 947 TuiFadePalette = (PPALETTE_ENTRY)FrLdrTempAlloc(sizeof(PALETTE_ENTRY) * 64, 948 TAG_TUI_PALETTE); 949 950 if (TuiFadePalette != NULL) 951 { 952 VideoSavePaletteState(TuiFadePalette, 64); 953 } 954 } 955 956 if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL) 957 { 958 VideoFadeOut(64); 959 } 960 961 MachVideoSetDisplayMode(NULL, FALSE); 962 963 if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL) 964 { 965 VideoRestorePaletteState(TuiFadePalette, 64); 966 FrLdrTempFree(TuiFadePalette, TAG_TUI_PALETTE); 967 } 968 969 } 970 971 BOOLEAN TuiEditBox(PCSTR MessageText, PCHAR EditTextBuffer, ULONG Length) 972 { 973 INT width = 8; 974 ULONG height = 1; 975 INT curline = 0; 976 INT k; 977 size_t i , j; 978 INT x1, x2, y1, y2; 979 CHAR temp[260]; 980 CHAR key; 981 BOOLEAN Extended; 982 INT EditBoxLine; 983 ULONG EditBoxStartX, EditBoxEndX; 984 INT EditBoxCursorX; 985 ULONG EditBoxTextLength, EditBoxTextPosition; 986 INT EditBoxTextDisplayIndex; 987 BOOLEAN ReturnCode; 988 PVOID ScreenBuffer; 989 990 // Save the screen contents 991 ScreenBuffer = FrLdrTempAlloc(UiScreenWidth * UiScreenHeight * 2, 992 TAG_TUI_SCREENBUFFER); 993 TuiSaveScreen(ScreenBuffer); 994 995 // Find the height 996 for (i=0; i<strlen(MessageText); i++) 997 { 998 if (MessageText[i] == '\n') 999 height++; 1000 } 1001 1002 // Find the width 1003 for (i=0,j=0,k=0; i<height; i++) 1004 { 1005 while ((MessageText[j] != '\n') && (MessageText[j] != 0)) 1006 { 1007 j++; 1008 k++; 1009 } 1010 1011 if (k > width) 1012 width = k; 1013 1014 k = 0; 1015 j++; 1016 } 1017 1018 // Calculate box area 1019 x1 = (UiScreenWidth - (width+2))/2; 1020 x2 = x1 + width + 3; 1021 y1 = ((UiScreenHeight - height - 2)/2) + 1; 1022 y2 = y1 + height + 4; 1023 1024 // Draw the box 1025 TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor)); 1026 1027 // Draw the text 1028 for (i=0,j=0; i<strlen(MessageText)+1; i++) 1029 { 1030 if ((MessageText[i] == '\n') || (MessageText[i] == 0)) 1031 { 1032 temp[j] = 0; 1033 j = 0; 1034 UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor)); 1035 curline++; 1036 } 1037 else 1038 temp[j++] = MessageText[i]; 1039 } 1040 1041 EditBoxTextLength = (ULONG)strlen(EditTextBuffer); 1042 EditBoxTextLength = min(EditBoxTextLength, Length - 1); 1043 EditBoxTextPosition = 0; 1044 EditBoxLine = y2 - 2; 1045 EditBoxStartX = x1 + 3; 1046 EditBoxEndX = x2 - 3; 1047 1048 // Draw the edit box background and the text 1049 UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor)); 1050 UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, EditTextBuffer, ATTR(UiEditBoxTextColor, UiEditBoxBgColor)); 1051 1052 // Show the cursor 1053 EditBoxCursorX = EditBoxStartX; 1054 MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine); 1055 MachVideoHideShowTextCursor(TRUE); 1056 1057 // Draw status text 1058 UiDrawStatusText("Press ENTER to continue, or ESC to cancel"); 1059 1060 VideoCopyOffScreenBufferToVRAM(); 1061 1062 // 1063 // Enter the text. Please keep in mind that the default input mode 1064 // of the edit boxes is in insertion mode, that is, you can insert 1065 // text without erasing the existing one. 1066 // 1067 for (;;) 1068 { 1069 if (MachConsKbHit()) 1070 { 1071 Extended = FALSE; 1072 key = MachConsGetCh(); 1073 if (key == KEY_EXTENDED) 1074 { 1075 Extended = TRUE; 1076 key = MachConsGetCh(); 1077 } 1078 1079 if (key == KEY_ENTER) 1080 { 1081 ReturnCode = TRUE; 1082 break; 1083 } 1084 else if (key == KEY_ESC) 1085 { 1086 ReturnCode = FALSE; 1087 break; 1088 } 1089 else if (key == KEY_BACKSPACE) // Remove a character 1090 { 1091 if ( (EditBoxTextLength > 0) && (EditBoxTextPosition > 0) && 1092 (EditBoxTextPosition <= EditBoxTextLength) ) 1093 { 1094 EditBoxTextPosition--; 1095 memmove(EditTextBuffer + EditBoxTextPosition, 1096 EditTextBuffer + EditBoxTextPosition + 1, 1097 EditBoxTextLength - EditBoxTextPosition); 1098 EditBoxTextLength--; 1099 EditTextBuffer[EditBoxTextLength] = 0; 1100 } 1101 else 1102 { 1103 MachBeep(); 1104 } 1105 } 1106 else if (Extended && key == KEY_DELETE) // Remove a character 1107 { 1108 if ( (EditBoxTextLength > 0) && 1109 (EditBoxTextPosition < EditBoxTextLength) ) 1110 { 1111 memmove(EditTextBuffer + EditBoxTextPosition, 1112 EditTextBuffer + EditBoxTextPosition + 1, 1113 EditBoxTextLength - EditBoxTextPosition); 1114 EditBoxTextLength--; 1115 EditTextBuffer[EditBoxTextLength] = 0; 1116 } 1117 else 1118 { 1119 MachBeep(); 1120 } 1121 } 1122 else if (Extended && key == KEY_HOME) // Go to the start of the buffer 1123 { 1124 EditBoxTextPosition = 0; 1125 } 1126 else if (Extended && key == KEY_END) // Go to the end of the buffer 1127 { 1128 EditBoxTextPosition = EditBoxTextLength; 1129 } 1130 else if (Extended && key == KEY_RIGHT) // Go right 1131 { 1132 if (EditBoxTextPosition < EditBoxTextLength) 1133 EditBoxTextPosition++; 1134 else 1135 MachBeep(); 1136 } 1137 else if (Extended && key == KEY_LEFT) // Go left 1138 { 1139 if (EditBoxTextPosition > 0) 1140 EditBoxTextPosition--; 1141 else 1142 MachBeep(); 1143 } 1144 else if (!Extended) // Add this key to the buffer 1145 { 1146 if ( (EditBoxTextLength < Length - 1) && 1147 (EditBoxTextPosition < Length - 1) ) 1148 { 1149 memmove(EditTextBuffer + EditBoxTextPosition + 1, 1150 EditTextBuffer + EditBoxTextPosition, 1151 EditBoxTextLength - EditBoxTextPosition); 1152 EditTextBuffer[EditBoxTextPosition] = key; 1153 EditBoxTextPosition++; 1154 EditBoxTextLength++; 1155 EditTextBuffer[EditBoxTextLength] = 0; 1156 } 1157 else 1158 { 1159 MachBeep(); 1160 } 1161 } 1162 else 1163 { 1164 MachBeep(); 1165 } 1166 } 1167 1168 // Draw the edit box background 1169 UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor)); 1170 1171 // Fill the text in 1172 if (EditBoxTextPosition > (EditBoxEndX - EditBoxStartX)) 1173 { 1174 EditBoxTextDisplayIndex = EditBoxTextPosition - (EditBoxEndX - EditBoxStartX); 1175 EditBoxCursorX = EditBoxEndX; 1176 } 1177 else 1178 { 1179 EditBoxTextDisplayIndex = 0; 1180 EditBoxCursorX = EditBoxStartX + EditBoxTextPosition; 1181 } 1182 UiDrawText2(EditBoxStartX, EditBoxLine, EditBoxEndX - EditBoxStartX + 1, &EditTextBuffer[EditBoxTextDisplayIndex], ATTR(UiEditBoxTextColor, UiEditBoxBgColor)); 1183 1184 // Move the cursor 1185 MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine); 1186 1187 TuiUpdateDateTime(); 1188 1189 VideoCopyOffScreenBufferToVRAM(); 1190 1191 MachHwIdle(); 1192 } 1193 1194 // Hide the cursor again 1195 MachVideoHideShowTextCursor(FALSE); 1196 1197 // Restore the screen contents 1198 TuiRestoreScreen(ScreenBuffer); 1199 FrLdrTempFree(ScreenBuffer, TAG_TUI_SCREENBUFFER); 1200 1201 return ReturnCode; 1202 } 1203 1204 const UIVTBL TuiVtbl = 1205 { 1206 TuiInitialize, 1207 TuiUnInitialize, 1208 TuiDrawBackdrop, 1209 TuiFillArea, 1210 TuiDrawShadow, 1211 TuiDrawBox, 1212 TuiDrawText, 1213 TuiDrawText2, 1214 TuiDrawCenteredText, 1215 TuiDrawStatusText, 1216 TuiUpdateDateTime, 1217 TuiMessageBox, 1218 TuiMessageBoxCritical, 1219 TuiDrawProgressBarCenter, 1220 TuiDrawProgressBar, 1221 TuiSetProgressBarText, 1222 TuiTickProgressBar, 1223 TuiEditBox, 1224 TuiTextToColor, 1225 TuiTextToFillStyle, 1226 TuiFadeInBackdrop, 1227 TuiFadeOut, 1228 TuiDisplayMenu, 1229 TuiDrawMenu, 1230 }; 1231