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