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