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