xref: /reactos/ntoskrnl/inbv/inbv.c (revision 7e069ccd)
1 /* INCLUDES ******************************************************************/
2 
3 #include <ntoskrnl.h>
4 
5 #define NDEBUG
6 #include <debug.h>
7 
8 #include "inbv/logo.h"
9 
10 /* See also mm/ARM3/miarm.h */
11 #define MM_READONLY     1   // PAGE_READONLY
12 #define MM_READWRITE    4   // PAGE_WRITECOPY
13 
14 #ifndef TAG_OSTR
15 #define TAG_OSTR    'RTSO'
16 #endif
17 
18 /* GLOBALS *******************************************************************/
19 
20 /*
21  * Enable this define if you want Inbv to use coloured headless mode.
22  */
23 // #define INBV_HEADLESS_COLORS
24 
25 /*
26  * ReactOS uses the same boot screen for all the products.
27  * Also it doesn't use a parallel system thread for the
28  * rotating "progress" bar.
29  */
30 
31 /*
32  * Enable this define when ReactOS will have different SKUs
33  * (Workstation, Server, Storage Server, Cluster Server, etc...).
34  */
35 // #define REACTOS_SKUS
36 
37 typedef struct _INBV_PROGRESS_STATE
38 {
39     ULONG Floor;
40     ULONG Ceiling;
41     ULONG Bias;
42 } INBV_PROGRESS_STATE;
43 
44 typedef struct _BT_PROGRESS_INDICATOR
45 {
46     ULONG Count;
47     ULONG Expected;
48     ULONG Percentage;
49 } BT_PROGRESS_INDICATOR, *PBT_PROGRESS_INDICATOR;
50 
51 typedef enum _ROT_BAR_TYPE
52 {
53     RB_UNSPECIFIED,
54     RB_SQUARE_CELLS,
55     RB_PROGRESS_BAR
56 } ROT_BAR_TYPE;
57 
58 /*
59  * BitBltAligned() alignments
60  */
61 typedef enum _BBLT_VERT_ALIGNMENT
62 {
63     AL_VERTICAL_TOP = 0,
64     AL_VERTICAL_CENTER,
65     AL_VERTICAL_BOTTOM
66 } BBLT_VERT_ALIGNMENT;
67 
68 typedef enum _BBLT_HORZ_ALIGNMENT
69 {
70     AL_HORIZONTAL_LEFT = 0,
71     AL_HORIZONTAL_CENTER,
72     AL_HORIZONTAL_RIGHT
73 } BBLT_HORZ_ALIGNMENT;
74 
75 /*
76  * Enable this define when Inbv will support rotating progress bar.
77  */
78 #define INBV_ROTBAR_IMPLEMENTED
79 
80 static KSPIN_LOCK BootDriverLock;
81 static KIRQL InbvOldIrql;
82 static INBV_DISPLAY_STATE InbvDisplayState = INBV_DISPLAY_STATE_DISABLED;
83 BOOLEAN InbvBootDriverInstalled = FALSE;
84 static BOOLEAN InbvDisplayDebugStrings = FALSE;
85 static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL;
86 static ULONG ProgressBarLeft = 0, ProgressBarTop = 0;
87 static BOOLEAN ShowProgressBar = FALSE;
88 static INBV_PROGRESS_STATE InbvProgressState;
89 static BT_PROGRESS_INDICATOR InbvProgressIndicator = {0, 25, 0};
90 static INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters = NULL;
91 static ULONG ResourceCount = 0;
92 static PUCHAR ResourceList[1 + IDB_MAX_RESOURCE]; // First entry == NULL, followed by 'ResourceCount' entries.
93 
94 #ifdef INBV_ROTBAR_IMPLEMENTED
95 /*
96  * Change this to modify progress bar behaviour
97  */
98 #define ROT_BAR_DEFAULT_MODE    RB_PROGRESS_BAR
99 
100 /*
101  * Values for PltRotBarStatus:
102  * - PltRotBarStatus == 1, do palette fading-in (done elsewhere in ReactOS);
103  * - PltRotBarStatus == 2, do rotation bar animation;
104  * - PltRotBarStatus == 3, stop the animation thread.
105  * - Any other value is ignored and the animation thread continues to run.
106  */
107 typedef enum _ROT_BAR_STATUS
108 {
109     RBS_FADEIN = 1,
110     RBS_ANIMATE,
111     RBS_STOP_ANIMATE,
112     RBS_STATUS_MAX
113 } ROT_BAR_STATUS;
114 
115 static BOOLEAN RotBarThreadActive = FALSE;
116 static ROT_BAR_TYPE RotBarSelection = RB_UNSPECIFIED;
117 static ROT_BAR_STATUS PltRotBarStatus = 0;
118 static UCHAR RotBarBuffer[24 * 9];
119 static UCHAR RotLineBuffer[SCREEN_WIDTH * 6];
120 #endif
121 
122 
123 /*
124  * Headless terminal text colors
125  */
126 
127 #ifdef INBV_HEADLESS_COLORS
128 
129 // Conversion table CGA to ANSI color index
130 static const UCHAR CGA_TO_ANSI_COLOR_TABLE[16] =
131 {
132     0,  // Black
133     4,  // Blue
134     2,  // Green
135     6,  // Cyan
136     1,  // Red
137     5,  // Magenta
138     3,  // Brown/Yellow
139     7,  // Grey/White
140 
141     60, // Bright Black
142     64, // Bright Blue
143     62, // Bright Green
144     66, // Bright Cyan
145     61, // Bright Red
146     65, // Bright Magenta
147     63, // Bright Yellow
148     67  // Bright Grey (White)
149 };
150 
151 #define CGA_TO_ANSI_COLOR(CgaColor) \
152     CGA_TO_ANSI_COLOR_TABLE[CgaColor & 0x0F]
153 
154 #endif
155 
156 // Default colors: text in white, background in black
157 static ULONG InbvTerminalTextColor = 37;
158 static ULONG InbvTerminalBkgdColor = 40;
159 
160 
161 /* FADING FUNCTION ***********************************************************/
162 
163 /** From include/psdk/wingdi.h **/
164 typedef struct tagRGBQUAD
165 {
166     UCHAR    rgbBlue;
167     UCHAR    rgbGreen;
168     UCHAR    rgbRed;
169     UCHAR    rgbReserved;
170 } RGBQUAD,*LPRGBQUAD;
171 /*******************************/
172 
173 static RGBQUAD MainPalette[16];
174 
175 #define PALETTE_FADE_STEPS  12
176 #define PALETTE_FADE_TIME   (15 * 1000) /* 15 ms */
177 
178 /** From bootvid/precomp.h **/
179 //
180 // Bitmap Header
181 //
182 typedef struct tagBITMAPINFOHEADER
183 {
184     ULONG biSize;
185     LONG biWidth;
186     LONG biHeight;
187     USHORT biPlanes;
188     USHORT biBitCount;
189     ULONG biCompression;
190     ULONG biSizeImage;
191     LONG biXPelsPerMeter;
192     LONG biYPelsPerMeter;
193     ULONG biClrUsed;
194     ULONG biClrImportant;
195 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
196 /****************************/
197 
198 //
199 // Needed prototypes
200 //
201 VOID NTAPI InbvAcquireLock(VOID);
202 VOID NTAPI InbvReleaseLock(VOID);
203 
204 static VOID
205 BootLogoFadeIn(VOID)
206 {
207     UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)];
208     PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer;
209     LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
210     ULONG Iteration, Index, ClrUsed;
211 
212     LARGE_INTEGER Delay;
213     Delay.QuadPart = -(PALETTE_FADE_TIME * 10);
214 
215     /* Check if we are installed and we own the display */
216     if (!InbvBootDriverInstalled ||
217         (InbvDisplayState != INBV_DISPLAY_STATE_OWNED))
218     {
219         return;
220     }
221 
222     /*
223      * Build a bitmap containing the fade-in palette. The palette entries
224      * are then processed in a loop and set using VidBitBlt function.
225      */
226     ClrUsed = RTL_NUMBER_OF(MainPalette);
227     RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
228     PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
229     PaletteBitmap->biBitCount = 4;
230     PaletteBitmap->biClrUsed = ClrUsed;
231 
232     /*
233      * Main animation loop.
234      */
235     for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
236     {
237         for (Index = 0; Index < ClrUsed; Index++)
238         {
239             Palette[Index].rgbRed = (UCHAR)
240                 (MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
241             Palette[Index].rgbGreen = (UCHAR)
242                 (MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
243             Palette[Index].rgbBlue = (UCHAR)
244                 (MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
245         }
246 
247         /* Do the animation */
248         InbvAcquireLock();
249         VidBitBlt(PaletteBitmapBuffer, 0, 0);
250         InbvReleaseLock();
251 
252         /* Wait for a bit */
253         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
254     }
255 }
256 
257 static VOID
258 BitBltPalette(
259     IN PVOID Image,
260     IN BOOLEAN NoPalette,
261     IN ULONG X,
262     IN ULONG Y)
263 {
264     LPRGBQUAD Palette;
265     RGBQUAD OrigPalette[RTL_NUMBER_OF(MainPalette)];
266 
267     /* If requested, remove the palette from the image */
268     if (NoPalette)
269     {
270         /* Get bitmap header and palette */
271         PBITMAPINFOHEADER BitmapInfoHeader = Image;
272         Palette = (LPRGBQUAD)((PUCHAR)Image + BitmapInfoHeader->biSize);
273 
274         /* Save the image original palette and remove palette information */
275         RtlCopyMemory(OrigPalette, Palette, sizeof(OrigPalette));
276         RtlZeroMemory(Palette, sizeof(OrigPalette));
277     }
278 
279     /* Draw the image */
280     InbvBitBlt(Image, X, Y);
281 
282     /* Restore the image original palette */
283     if (NoPalette)
284     {
285         RtlCopyMemory(Palette, OrigPalette, sizeof(OrigPalette));
286     }
287 }
288 
289 static VOID
290 BitBltAligned(
291     IN PVOID Image,
292     IN BOOLEAN NoPalette,
293     IN BBLT_HORZ_ALIGNMENT HorizontalAlignment,
294     IN BBLT_VERT_ALIGNMENT VerticalAlignment,
295     IN ULONG MarginLeft,
296     IN ULONG MarginTop,
297     IN ULONG MarginRight,
298     IN ULONG MarginBottom)
299 {
300     PBITMAPINFOHEADER BitmapInfoHeader = Image;
301     ULONG X, Y;
302 
303     /* Calculate X */
304     switch (HorizontalAlignment)
305     {
306         case AL_HORIZONTAL_LEFT:
307             X = MarginLeft - MarginRight;
308             break;
309 
310         case AL_HORIZONTAL_CENTER:
311             X = MarginLeft - MarginRight + (SCREEN_WIDTH - BitmapInfoHeader->biWidth + 1) / 2;
312             break;
313 
314         case AL_HORIZONTAL_RIGHT:
315             X = MarginLeft - MarginRight + SCREEN_WIDTH - BitmapInfoHeader->biWidth;
316             break;
317 
318         default:
319             /* Unknown */
320             return;
321     }
322 
323     /* Calculate Y */
324     switch (VerticalAlignment)
325     {
326         case AL_VERTICAL_TOP:
327             Y = MarginTop - MarginBottom;
328             break;
329 
330         case AL_VERTICAL_CENTER:
331             Y = MarginTop - MarginBottom + (SCREEN_HEIGHT - BitmapInfoHeader->biHeight + 1) / 2;
332             break;
333 
334         case AL_VERTICAL_BOTTOM:
335             Y = MarginTop - MarginBottom + SCREEN_HEIGHT - BitmapInfoHeader->biHeight;
336             break;
337 
338         default:
339             /* Unknown */
340             return;
341     }
342 
343     /* Finally draw the image */
344     BitBltPalette(Image, NoPalette, X, Y);
345 }
346 
347 /* FUNCTIONS *****************************************************************/
348 
349 INIT_FUNCTION
350 PVOID
351 NTAPI
352 FindBitmapResource(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
353                    IN ULONG ResourceId)
354 {
355     UNICODE_STRING UpString = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
356     UNICODE_STRING MpString = RTL_CONSTANT_STRING(L"ntkrnlmp.exe");
357     PLIST_ENTRY NextEntry, ListHead;
358     PLDR_DATA_TABLE_ENTRY LdrEntry;
359     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
360     LDR_RESOURCE_INFO ResourceInfo;
361     NTSTATUS Status;
362     PVOID Data = NULL;
363 
364     /* Loop the driver list */
365     ListHead = &LoaderBlock->LoadOrderListHead;
366     NextEntry = ListHead->Flink;
367     while (NextEntry != ListHead)
368     {
369         /* Get the entry */
370         LdrEntry = CONTAINING_RECORD(NextEntry,
371                                      LDR_DATA_TABLE_ENTRY,
372                                      InLoadOrderLinks);
373 
374         /* Check for a match */
375         if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE) ||
376             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE))
377         {
378             /* Break out */
379             break;
380         }
381     }
382 
383     /* Check if we found it */
384     if (NextEntry != ListHead)
385     {
386         /* Try to find the resource */
387         ResourceInfo.Type = 2; // RT_BITMAP;
388         ResourceInfo.Name = ResourceId;
389         ResourceInfo.Language = 0;
390         Status = LdrFindResource_U(LdrEntry->DllBase,
391                                    &ResourceInfo,
392                                    RESOURCE_DATA_LEVEL,
393                                    &ResourceDataEntry);
394         if (NT_SUCCESS(Status))
395         {
396             /* Access the resource */
397             ULONG Size = 0;
398             Status = LdrAccessResource(LdrEntry->DllBase,
399                                        ResourceDataEntry,
400                                        &Data,
401                                        &Size);
402             if ((Data) && (ResourceId < 3))
403             {
404                 KiBugCheckData[4] ^= RtlComputeCrc32(0, Data, Size);
405             }
406             if (!NT_SUCCESS(Status)) Data = NULL;
407         }
408     }
409 
410     /* Return the pointer */
411     return Data;
412 }
413 
414 INIT_FUNCTION
415 BOOLEAN
416 NTAPI
417 InbvDriverInitialize(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
418                      IN ULONG Count)
419 {
420     PCHAR CommandLine;
421     BOOLEAN ResetMode = FALSE; // By default do not reset the video mode
422     ULONG i;
423 
424     /* Quit if we're already installed */
425     if (InbvBootDriverInstalled) return TRUE;
426 
427     /* Initialize the lock and check the current display state */
428     KeInitializeSpinLock(&BootDriverLock);
429     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
430     {
431         /* Reset the video mode in case we do not have a custom boot logo */
432         CommandLine = (LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL);
433         ResetMode   = (CommandLine == NULL) || (strstr(CommandLine, "BOOTLOGO") == NULL);
434     }
435 
436     /* Initialize the video */
437     InbvBootDriverInstalled = VidInitialize(ResetMode);
438     if (InbvBootDriverInstalled)
439     {
440         /* Find bitmap resources in the kernel */
441         ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
442         for (i = 1; i <= ResourceCount; i++)
443         {
444             /* Do the lookup */
445             ResourceList[i] = FindBitmapResource(LoaderBlock, i);
446         }
447 
448         /* Set the progress bar ranges */
449         InbvSetProgressBarSubset(0, 100);
450     }
451 
452     /* Return install state */
453     return InbvBootDriverInstalled;
454 }
455 
456 VOID
457 NTAPI
458 InbvAcquireLock(VOID)
459 {
460     KIRQL OldIrql;
461 
462     /* Check if we're at dispatch level or lower */
463     OldIrql = KeGetCurrentIrql();
464     if (OldIrql <= DISPATCH_LEVEL)
465     {
466         /* Loop until the lock is free */
467         while (!KeTestSpinLock(&BootDriverLock));
468 
469         /* Raise IRQL to dispatch level */
470         KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
471     }
472 
473     /* Acquire the lock */
474     KiAcquireSpinLock(&BootDriverLock);
475     InbvOldIrql = OldIrql;
476 }
477 
478 VOID
479 NTAPI
480 InbvReleaseLock(VOID)
481 {
482     KIRQL OldIrql;
483 
484     /* Capture the old IRQL */
485     OldIrql = InbvOldIrql;
486 
487     /* Release the driver lock */
488     KiReleaseSpinLock(&BootDriverLock);
489 
490     /* If we were at dispatch level or lower, restore the old IRQL */
491     if (InbvOldIrql <= DISPATCH_LEVEL) KeLowerIrql(OldIrql);
492 }
493 
494 VOID
495 NTAPI
496 InbvEnableBootDriver(IN BOOLEAN Enable)
497 {
498     /* Check if we're installed */
499     if (InbvBootDriverInstalled)
500     {
501         /* Check for lost state */
502         if (InbvDisplayState >= INBV_DISPLAY_STATE_LOST) return;
503 
504         /* Acquire the lock */
505         InbvAcquireLock();
506 
507         /* Cleanup the screen if we own it */
508         if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) VidCleanUp();
509 
510         /* Set the new display state */
511         InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
512                                     INBV_DISPLAY_STATE_DISABLED;
513 
514         /* Release the lock */
515         InbvReleaseLock();
516     }
517     else
518     {
519         /* Set the new display state */
520         InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
521                                     INBV_DISPLAY_STATE_DISABLED;
522     }
523 }
524 
525 VOID
526 NTAPI
527 InbvAcquireDisplayOwnership(VOID)
528 {
529     /* Check if we have a callback and we're just acquiring it now */
530     if ((InbvResetDisplayParameters) &&
531         (InbvDisplayState == INBV_DISPLAY_STATE_LOST))
532     {
533         /* Call the callback */
534         InbvResetDisplayParameters(80, 50);
535     }
536 
537     /* Acquire the display */
538     InbvDisplayState = INBV_DISPLAY_STATE_OWNED;
539 }
540 
541 VOID
542 NTAPI
543 InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned)
544 {
545     /* Set the new display state */
546     InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED :
547                                       INBV_DISPLAY_STATE_LOST;
548 }
549 
550 BOOLEAN
551 NTAPI
552 InbvCheckDisplayOwnership(VOID)
553 {
554     /* Return if we own it or not */
555     return InbvDisplayState != INBV_DISPLAY_STATE_LOST;
556 }
557 
558 INBV_DISPLAY_STATE
559 NTAPI
560 InbvGetDisplayState(VOID)
561 {
562     /* Return the actual state */
563     return InbvDisplayState;
564 }
565 
566 BOOLEAN
567 NTAPI
568 InbvDisplayString(IN PCHAR String)
569 {
570     /* Make sure we own the display */
571     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
572     {
573         /* If we're not allowed, return success anyway */
574         if (!InbvDisplayDebugStrings) return TRUE;
575 
576         /* Check if a filter is installed */
577         if (InbvDisplayFilter) InbvDisplayFilter(&String);
578 
579         /* Acquire the lock */
580         InbvAcquireLock();
581 
582         /* Make sure we're installed and display the string */
583         if (InbvBootDriverInstalled) VidDisplayString((PUCHAR)String);
584 
585         /* Print the string on the EMS port */
586         HeadlessDispatch(HeadlessCmdPutString,
587                          String,
588                          strlen(String) + sizeof(ANSI_NULL),
589                          NULL,
590                          NULL);
591 
592         /* Release the lock */
593         InbvReleaseLock();
594 
595         /* All done */
596         return TRUE;
597     }
598 
599     /* We don't own it, fail */
600     return FALSE;
601 }
602 
603 BOOLEAN
604 NTAPI
605 InbvEnableDisplayString(IN BOOLEAN Enable)
606 {
607     BOOLEAN OldSetting;
608 
609     /* Get the old setting */
610     OldSetting = InbvDisplayDebugStrings;
611 
612     /* Update it */
613     InbvDisplayDebugStrings = Enable;
614 
615     /* Return the old setting */
616     return OldSetting;
617 }
618 
619 VOID
620 NTAPI
621 InbvInstallDisplayStringFilter(IN INBV_DISPLAY_STRING_FILTER Filter)
622 {
623     /* Save the filter */
624     InbvDisplayFilter = Filter;
625 }
626 
627 BOOLEAN
628 NTAPI
629 InbvIsBootDriverInstalled(VOID)
630 {
631     /* Return driver state */
632     return InbvBootDriverInstalled;
633 }
634 
635 VOID
636 NTAPI
637 InbvNotifyDisplayOwnershipLost(IN INBV_RESET_DISPLAY_PARAMETERS Callback)
638 {
639     /* Check if we're installed */
640     if (InbvBootDriverInstalled)
641     {
642         /* Acquire the lock and cleanup if we own the screen */
643         InbvAcquireLock();
644         if (InbvDisplayState != INBV_DISPLAY_STATE_LOST) VidCleanUp();
645 
646         /* Set the reset callback and display state */
647         InbvResetDisplayParameters = Callback;
648         InbvDisplayState = INBV_DISPLAY_STATE_LOST;
649 
650         /* Release the lock */
651         InbvReleaseLock();
652     }
653     else
654     {
655         /* Set the reset callback and display state */
656         InbvResetDisplayParameters = Callback;
657         InbvDisplayState = INBV_DISPLAY_STATE_LOST;
658     }
659 }
660 
661 BOOLEAN
662 NTAPI
663 InbvResetDisplay(VOID)
664 {
665     /* Check if we're installed and we own it */
666     if (InbvBootDriverInstalled &&
667         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
668     {
669         /* Do the reset */
670         VidResetDisplay(TRUE);
671         return TRUE;
672     }
673 
674     /* Nothing to reset */
675     return FALSE;
676 }
677 
678 VOID
679 NTAPI
680 InbvSetScrollRegion(IN ULONG Left,
681                     IN ULONG Top,
682                     IN ULONG Right,
683                     IN ULONG Bottom)
684 {
685     /* Just call bootvid */
686     VidSetScrollRegion(Left, Top, Right, Bottom);
687 }
688 
689 VOID
690 NTAPI
691 InbvSetTextColor(IN ULONG Color)
692 {
693     HEADLESS_CMD_SET_COLOR HeadlessSetColor;
694 
695     /* Set color for EMS port */
696 #ifdef INBV_HEADLESS_COLORS
697     InbvTerminalTextColor = 30 + CGA_TO_ANSI_COLOR(Color);
698 #else
699     InbvTerminalTextColor = 37;
700 #endif
701     HeadlessSetColor.TextColor = InbvTerminalTextColor;
702     HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
703     HeadlessDispatch(HeadlessCmdSetColor,
704                      &HeadlessSetColor,
705                      sizeof(HeadlessSetColor),
706                      NULL,
707                      NULL);
708 
709     /* Update the text color */
710     VidSetTextColor(Color);
711 }
712 
713 VOID
714 NTAPI
715 InbvSolidColorFill(IN ULONG Left,
716                    IN ULONG Top,
717                    IN ULONG Right,
718                    IN ULONG Bottom,
719                    IN ULONG Color)
720 {
721     HEADLESS_CMD_SET_COLOR HeadlessSetColor;
722 
723     /* Make sure we own it */
724     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
725     {
726         /* Acquire the lock */
727         InbvAcquireLock();
728 
729         /* Check if we're installed */
730         if (InbvBootDriverInstalled)
731         {
732             /* Call bootvid */
733             VidSolidColorFill(Left, Top, Right, Bottom, (UCHAR)Color);
734         }
735 
736         /* Set color for EMS port and clear display */
737 #ifdef INBV_HEADLESS_COLORS
738         InbvTerminalBkgdColor = 40 + CGA_TO_ANSI_COLOR(Color);
739 #else
740         InbvTerminalBkgdColor = 40;
741 #endif
742         HeadlessSetColor.TextColor = InbvTerminalTextColor;
743         HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
744         HeadlessDispatch(HeadlessCmdSetColor,
745                          &HeadlessSetColor,
746                          sizeof(HeadlessSetColor),
747                          NULL,
748                          NULL);
749         HeadlessDispatch(HeadlessCmdClearDisplay,
750                          NULL, 0,
751                          NULL, NULL);
752 
753         /* Release the lock */
754         InbvReleaseLock();
755     }
756 }
757 
758 INIT_FUNCTION
759 VOID
760 NTAPI
761 InbvUpdateProgressBar(IN ULONG Progress)
762 {
763     ULONG FillCount, BoundedProgress;
764 
765     /* Make sure the progress bar is enabled, that we own and are installed */
766     if (ShowProgressBar &&
767         InbvBootDriverInstalled &&
768         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
769     {
770         /* Compute fill count */
771         BoundedProgress = (InbvProgressState.Floor / 100) + Progress;
772         FillCount = 121 * (InbvProgressState.Bias * BoundedProgress) / 1000000;
773 
774         /* Acquire the lock */
775         InbvAcquireLock();
776 
777         /* Fill the progress bar */
778         VidSolidColorFill(ProgressBarLeft,
779                           ProgressBarTop,
780                           ProgressBarLeft + FillCount,
781                           ProgressBarTop + 12,
782                           15);
783 
784         /* Release the lock */
785         InbvReleaseLock();
786     }
787 }
788 
789 VOID
790 NTAPI
791 InbvBufferToScreenBlt(IN PUCHAR Buffer,
792                       IN ULONG X,
793                       IN ULONG Y,
794                       IN ULONG Width,
795                       IN ULONG Height,
796                       IN ULONG Delta)
797 {
798     /* Check if we're installed and we own it */
799     if (InbvBootDriverInstalled &&
800         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
801     {
802         /* Do the blit */
803         VidBufferToScreenBlt(Buffer, X, Y, Width, Height, Delta);
804     }
805 }
806 
807 VOID
808 NTAPI
809 InbvBitBlt(IN PUCHAR Buffer,
810            IN ULONG X,
811            IN ULONG Y)
812 {
813     /* Check if we're installed and we own it */
814     if (InbvBootDriverInstalled &&
815         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
816     {
817         /* Acquire the lock */
818         InbvAcquireLock();
819 
820         /* Do the blit */
821         VidBitBlt(Buffer, X, Y);
822 
823         /* Release the lock */
824         InbvReleaseLock();
825     }
826 }
827 
828 VOID
829 NTAPI
830 InbvScreenToBufferBlt(IN PUCHAR Buffer,
831                       IN ULONG X,
832                       IN ULONG Y,
833                       IN ULONG Width,
834                       IN ULONG Height,
835                       IN ULONG Delta)
836 {
837     /* Check if we're installed and we own it */
838     if (InbvBootDriverInstalled &&
839         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
840     {
841         /* Do the blit */
842         VidScreenToBufferBlt(Buffer, X, Y, Width, Height, Delta);
843     }
844 }
845 
846 INIT_FUNCTION
847 VOID
848 NTAPI
849 InbvSetProgressBarCoordinates(IN ULONG Left,
850                               IN ULONG Top)
851 {
852     /* Update the coordinates */
853     ProgressBarLeft = Left;
854     ProgressBarTop  = Top;
855 
856     /* Enable the progress bar */
857     ShowProgressBar = TRUE;
858 }
859 
860 INIT_FUNCTION
861 VOID
862 NTAPI
863 InbvSetProgressBarSubset(IN ULONG Floor,
864                          IN ULONG Ceiling)
865 {
866     /* Sanity checks */
867     ASSERT(Floor < Ceiling);
868     ASSERT(Ceiling <= 100);
869 
870     /* Update the progress bar state */
871     InbvProgressState.Floor = Floor * 100;
872     InbvProgressState.Ceiling = Ceiling * 100;
873     InbvProgressState.Bias = (Ceiling * 100) - Floor;
874 }
875 
876 INIT_FUNCTION
877 VOID
878 NTAPI
879 InbvIndicateProgress(VOID)
880 {
881     ULONG Percentage;
882 
883     /* Increase progress */
884     InbvProgressIndicator.Count++;
885 
886     /* Compute new percentage */
887     Percentage = min(100 * InbvProgressIndicator.Count /
888                            InbvProgressIndicator.Expected,
889                      99);
890     if (Percentage != InbvProgressIndicator.Percentage)
891     {
892         /* Percentage has moved, update the progress bar */
893         InbvProgressIndicator.Percentage = Percentage;
894         InbvUpdateProgressBar(Percentage);
895     }
896 }
897 
898 PUCHAR
899 NTAPI
900 InbvGetResourceAddress(IN ULONG ResourceNumber)
901 {
902     /* Validate the resource number */
903     if (ResourceNumber > ResourceCount) return NULL;
904 
905     /* Return the address */
906     return ResourceList[ResourceNumber];
907 }
908 
909 NTSTATUS
910 NTAPI
911 NtDisplayString(IN PUNICODE_STRING DisplayString)
912 {
913     NTSTATUS Status;
914     UNICODE_STRING CapturedString;
915     OEM_STRING OemString;
916     ULONG OemLength;
917     KPROCESSOR_MODE PreviousMode;
918 
919     PAGED_CODE();
920 
921     PreviousMode = ExGetPreviousMode();
922 
923     /* We require the TCB privilege */
924     if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
925         return STATUS_PRIVILEGE_NOT_HELD;
926 
927     /* Capture the string */
928     Status = ProbeAndCaptureUnicodeString(&CapturedString, PreviousMode, DisplayString);
929     if (!NT_SUCCESS(Status))
930         return Status;
931 
932     /* Do not display the string if it is empty */
933     if (CapturedString.Length == 0 || CapturedString.Buffer == NULL)
934     {
935         Status = STATUS_SUCCESS;
936         goto Quit;
937     }
938 
939     /*
940      * Convert the string since INBV understands only ANSI/OEM. Allocate the
941      * string buffer in non-paged pool because INBV passes it down to BOOTVID.
942      * We cannot perform the allocation using RtlUnicodeStringToOemString()
943      * since its allocator uses PagedPool.
944      */
945     OemLength = RtlUnicodeStringToOemSize(&CapturedString);
946     if (OemLength > MAXUSHORT)
947     {
948         Status = STATUS_BUFFER_OVERFLOW;
949         goto Quit;
950     }
951     RtlInitEmptyAnsiString((PANSI_STRING)&OemString, NULL, (USHORT)OemLength);
952     OemString.Buffer = ExAllocatePoolWithTag(NonPagedPool, OemLength, TAG_OSTR);
953     if (OemString.Buffer == NULL)
954     {
955         Status = STATUS_NO_MEMORY;
956         goto Quit;
957     }
958     RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
959 
960     /* Display the string */
961     InbvDisplayString(OemString.Buffer);
962 
963     /* Free the string buffer */
964     ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
965 
966     Status = STATUS_SUCCESS;
967 
968 Quit:
969     /* Free the captured string */
970     ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
971 
972     return Status;
973 }
974 
975 #ifdef INBV_ROTBAR_IMPLEMENTED
976 static
977 VOID
978 NTAPI
979 InbvRotationThread(
980     _In_ PVOID Context)
981 {
982     ULONG X, Y, Index, Total;
983     LARGE_INTEGER Delay = {{0}};
984 
985     InbvAcquireLock();
986     if (RotBarSelection == RB_SQUARE_CELLS)
987     {
988         Index = 0;
989     }
990     else
991     {
992         Index = 32;
993     }
994     X = ProgressBarLeft + 2;
995     Y = ProgressBarTop + 2;
996     InbvReleaseLock();
997 
998     while (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
999     {
1000         /* Wait for a bit */
1001         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
1002 
1003         InbvAcquireLock();
1004 
1005         /* Unknown unexpected command */
1006         ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
1007 
1008         if (PltRotBarStatus == RBS_STOP_ANIMATE)
1009         {
1010             /* Stop the thread */
1011             InbvReleaseLock();
1012             break;
1013         }
1014 
1015         if (RotBarSelection == RB_SQUARE_CELLS)
1016         {
1017             Delay.QuadPart = -800000; // 80 ms
1018             Total = 18;
1019             Index %= Total;
1020 
1021             if (Index >= 3)
1022             {
1023                 /* Fill previous bar position */
1024                 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, 0);
1025             }
1026             if (Index < Total - 1)
1027             {
1028                 /* Draw the progress bar bit */
1029                 if (Index < 2)
1030                 {
1031                     /* Appearing from the left */
1032                     VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
1033                 }
1034                 else if (Index >= Total - 3)
1035                 {
1036                     /* Hiding to the right */
1037                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
1038                 }
1039                 else
1040                 {
1041                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
1042                 }
1043             }
1044             Index++;
1045         }
1046         else if (RotBarSelection == RB_PROGRESS_BAR)
1047         {
1048             Delay.QuadPart = -600000; // 60 ms
1049             Total = SCREEN_WIDTH;
1050             Index %= Total;
1051 
1052             /* Right part */
1053             VidBufferToScreenBlt(RotLineBuffer, Index, SCREEN_HEIGHT-6, SCREEN_WIDTH - Index, 6, SCREEN_WIDTH);
1054             if (Index > 0)
1055             {
1056                 /* Left part */
1057                 VidBufferToScreenBlt(RotLineBuffer + (SCREEN_WIDTH - Index) / 2, 0, SCREEN_HEIGHT-6, Index - 2, 6, SCREEN_WIDTH);
1058             }
1059             Index += 32;
1060         }
1061 
1062         InbvReleaseLock();
1063     }
1064 
1065     PsTerminateSystemThread(STATUS_SUCCESS);
1066 }
1067 
1068 INIT_FUNCTION
1069 VOID
1070 NTAPI
1071 InbvRotBarInit(VOID)
1072 {
1073     PltRotBarStatus = RBS_FADEIN;
1074     /* Perform other initialization if needed */
1075 }
1076 #endif
1077 
1078 INIT_FUNCTION
1079 VOID
1080 NTAPI
1081 DisplayBootBitmap(IN BOOLEAN TextMode)
1082 {
1083     PVOID BootCopy = NULL, BootProgress = NULL, BootLogo = NULL, Header = NULL, Footer = NULL;
1084 
1085 #ifdef INBV_ROTBAR_IMPLEMENTED
1086     UCHAR Buffer[24 * 9];
1087     PVOID Bar = NULL, LineBmp = NULL;
1088     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
1089     NTSTATUS Status;
1090     HANDLE ThreadHandle = NULL;
1091 #endif
1092 
1093 #ifdef REACTOS_SKUS
1094     PVOID Text = NULL;
1095 #endif
1096 
1097 #ifdef INBV_ROTBAR_IMPLEMENTED
1098     /* Check if the animation thread has already been created */
1099     if (RotBarThreadActive)
1100     {
1101         /* Yes, just reset the progress bar but keep the thread alive */
1102         InbvAcquireLock();
1103         RotBarSelection = RB_UNSPECIFIED;
1104         InbvReleaseLock();
1105     }
1106 #endif
1107 
1108     ShowProgressBar = FALSE;
1109 
1110     /* Check if this is text mode */
1111     if (TextMode)
1112     {
1113         /*
1114          * Make the kernel resource section temporarily writable,
1115          * as we are going to change the bitmaps' palette in place.
1116          */
1117         MmChangeKernelResourceSectionProtection(MM_READWRITE);
1118 
1119         /* Check the type of the OS: workstation or server */
1120         if (SharedUserData->NtProductType == NtProductWinNt)
1121         {
1122             /* Workstation; set colors */
1123             InbvSetTextColor(15);
1124             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 7);
1125             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 1);
1126 
1127             /* Get resources */
1128             Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
1129             Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
1130         }
1131         else
1132         {
1133             /* Server; set colors */
1134             InbvSetTextColor(14);
1135             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 6);
1136             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 1);
1137 
1138             /* Get resources */
1139             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
1140             Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
1141         }
1142 
1143         /* Set the scrolling region */
1144         InbvSetScrollRegion(VID_SCROLL_AREA_LEFT, VID_SCROLL_AREA_TOP,
1145                             VID_SCROLL_AREA_RIGHT, VID_SCROLL_AREA_BOTTOM);
1146 
1147         /* Make sure we have resources */
1148         if (Header && Footer)
1149         {
1150             /* BitBlt them on the screen */
1151             BitBltAligned(Footer,
1152                           TRUE,
1153                           AL_HORIZONTAL_CENTER,
1154                           AL_VERTICAL_BOTTOM,
1155                           0, 0, 0, 59);
1156             BitBltAligned(Header,
1157                           FALSE,
1158                           AL_HORIZONTAL_CENTER,
1159                           AL_VERTICAL_TOP,
1160                           0, 0, 0, 0);
1161         }
1162 
1163         /* Restore the kernel resource section protection to be read-only */
1164         MmChangeKernelResourceSectionProtection(MM_READONLY);
1165     }
1166     else
1167     {
1168         /* Is the boot driver installed? */
1169         if (!InbvBootDriverInstalled) return;
1170 
1171         /*
1172          * Make the kernel resource section temporarily writable,
1173          * as we are going to change the bitmaps' palette in place.
1174          */
1175         MmChangeKernelResourceSectionProtection(MM_READWRITE);
1176 
1177         /* Load boot screen logo */
1178         BootLogo = InbvGetResourceAddress(IDB_LOGO_DEFAULT);
1179 
1180 #ifdef REACTOS_SKUS
1181         Text = NULL;
1182         if (SharedUserData->NtProductType == NtProductWinNt)
1183         {
1184 #ifdef INBV_ROTBAR_IMPLEMENTED
1185             /* Workstation product, use appropriate status bar color */
1186             Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1187 #endif
1188         }
1189         else
1190         {
1191             /* Display correct branding based on server suite */
1192             if (ExVerifySuite(StorageServer))
1193             {
1194                 /* Storage Server Edition */
1195                 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
1196             }
1197             else if (ExVerifySuite(ComputeServer))
1198             {
1199                 /* Compute Cluster Edition */
1200                 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
1201             }
1202             else
1203             {
1204                 /* Normal edition */
1205                 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
1206             }
1207 
1208 #ifdef INBV_ROTBAR_IMPLEMENTED
1209             /* Server product, use appropriate status bar color */
1210             Bar = InbvGetResourceAddress(IDB_BAR_DEFAULT);
1211 #endif
1212         }
1213 #else
1214         /* Use default status bar */
1215         Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1216 #endif
1217 
1218         /* Make sure we have a logo */
1219         if (BootLogo)
1220         {
1221             /* Save the main image palette for implementing the fade-in effect */
1222             PBITMAPINFOHEADER BitmapInfoHeader = BootLogo;
1223             LPRGBQUAD Palette = (LPRGBQUAD)((PUCHAR)BootLogo + BitmapInfoHeader->biSize);
1224             RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
1225 
1226             /* Draw the logo at the center of the screen */
1227             BitBltAligned(BootLogo,
1228                           TRUE,
1229                           AL_HORIZONTAL_CENTER,
1230                           AL_VERTICAL_CENTER,
1231                           0, 0, 0, 34);
1232 
1233 #ifdef INBV_ROTBAR_IMPLEMENTED
1234             /* Choose progress bar */
1235             TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
1236 #endif
1237 
1238             /* Set progress bar coordinates and display it */
1239             InbvSetProgressBarCoordinates(VID_PROGRESS_BAR_LEFT, VID_PROGRESS_BAR_TOP);
1240 
1241 #ifdef REACTOS_SKUS
1242             /* Check for non-workstation products */
1243             if (SharedUserData->NtProductType != NtProductWinNt)
1244             {
1245                 /* Overwrite part of the logo for a server product */
1246                 InbvScreenToBufferBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
1247                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
1248                 InbvSolidColorFill(VID_SKU_AREA_LEFT, VID_SKU_AREA_TOP,
1249                                    VID_SKU_AREA_RIGHT, VID_SKU_AREA_BOTTOM, 0);
1250                 InbvBufferToScreenBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
1251                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
1252 
1253                 /* In setup mode, you haven't selected a SKU yet */
1254                 if (ExpInTextModeSetup) Text = NULL;
1255             }
1256 #endif
1257         }
1258 
1259         /* Load and draw progress bar bitmap */
1260         BootProgress = InbvGetResourceAddress(IDB_PROGRESS_BAR);
1261         BitBltAligned(BootProgress,
1262                       TRUE,
1263                       AL_HORIZONTAL_CENTER,
1264                       AL_VERTICAL_CENTER,
1265                       0, 118, 0, 0);
1266 
1267         /* Load and draw copyright text bitmap */
1268         BootCopy = InbvGetResourceAddress(IDB_COPYRIGHT);
1269         BitBltAligned(BootCopy,
1270                       TRUE,
1271                       AL_HORIZONTAL_LEFT,
1272                       AL_VERTICAL_BOTTOM,
1273                       22, 0, 0, 20);
1274 
1275 #ifdef REACTOS_SKUS
1276         /* Draw the SKU text if it exits */
1277         if (Text)
1278             BitBltPalette(Text, TRUE, VID_SKU_TEXT_LEFT, VID_SKU_TEXT_TOP);
1279 #endif
1280 
1281 #ifdef INBV_ROTBAR_IMPLEMENTED
1282         if (Bar)
1283         {
1284             /* Save previous screen pixels to buffer */
1285             InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
1286             /* Draw the progress bar bit */
1287             InbvBitBlt(Bar, 0, 0);
1288             /* Store it in global buffer */
1289             InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
1290             /* Restore screen pixels */
1291             InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
1292         }
1293 
1294         /*
1295          * Add a rotating bottom horizontal bar when using a progress bar,
1296          * to show that ReactOS can be still alive when the bar does not
1297          * appear to progress.
1298          */
1299         if (TempRotBarSelection == RB_PROGRESS_BAR)
1300         {
1301             LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
1302             if (LineBmp)
1303             {
1304                 /* Draw the line and store it in global buffer */
1305                 BitBltPalette(LineBmp, TRUE, 0, SCREEN_HEIGHT-6);
1306                 InbvScreenToBufferBlt(RotLineBuffer, 0, SCREEN_HEIGHT-6, SCREEN_WIDTH, 6, SCREEN_WIDTH);
1307             }
1308         }
1309         else
1310         {
1311             /* Hide the simple progress bar if not used */
1312             ShowProgressBar = FALSE;
1313         }
1314 #endif
1315 
1316         /* Restore the kernel resource section protection to be read-only */
1317         MmChangeKernelResourceSectionProtection(MM_READONLY);
1318 
1319         /* Display the boot logo and fade it in */
1320         BootLogoFadeIn();
1321 
1322 #ifdef INBV_ROTBAR_IMPLEMENTED
1323         if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
1324         {
1325             /* Start the animation thread */
1326             Status = PsCreateSystemThread(&ThreadHandle,
1327                                           0,
1328                                           NULL,
1329                                           NULL,
1330                                           NULL,
1331                                           InbvRotationThread,
1332                                           NULL);
1333             if (NT_SUCCESS(Status))
1334             {
1335                 /* The thread has started, close the handle as we don't need it */
1336                 RotBarThreadActive = TRUE;
1337                 ObCloseHandle(ThreadHandle, KernelMode);
1338             }
1339         }
1340 #endif
1341 
1342         /* Set filter which will draw text display if needed */
1343         InbvInstallDisplayStringFilter(DisplayFilter);
1344     }
1345 
1346 #ifdef INBV_ROTBAR_IMPLEMENTED
1347     /* Do we have the animation thread? */
1348     if (RotBarThreadActive)
1349     {
1350         /* We do, initialize the progress bar */
1351         InbvAcquireLock();
1352         RotBarSelection = TempRotBarSelection;
1353         InbvRotBarInit();
1354         InbvReleaseLock();
1355     }
1356 #endif
1357 }
1358 
1359 INIT_FUNCTION
1360 VOID
1361 NTAPI
1362 DisplayFilter(PCHAR *String)
1363 {
1364     /* Windows hack to skip first dots */
1365     static BOOLEAN DotHack = TRUE;
1366 
1367     /* If "." is given set *String to empty string */
1368     if (DotHack && strcmp(*String, ".") == 0)
1369         *String = "";
1370 
1371     if (**String)
1372     {
1373         /* Remove the filter */
1374         InbvInstallDisplayStringFilter(NULL);
1375 
1376         DotHack = FALSE;
1377 
1378         /* Draw text screen */
1379         DisplayBootBitmap(TRUE);
1380     }
1381 }
1382 
1383 INIT_FUNCTION
1384 VOID
1385 NTAPI
1386 FinalizeBootLogo(VOID)
1387 {
1388     /* Acquire lock and check the display state */
1389     InbvAcquireLock();
1390     if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
1391     {
1392         /* Clear the screen */
1393         VidSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, 0);
1394     }
1395 
1396     /* Reset progress bar and lock */
1397 #ifdef INBV_ROTBAR_IMPLEMENTED
1398     PltRotBarStatus = RBS_STOP_ANIMATE;
1399     RotBarThreadActive = FALSE;
1400 #endif
1401     InbvReleaseLock();
1402 }
1403