xref: /reactos/ntoskrnl/inbv/inbv.c (revision 8a92b556)
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     RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
964 
965     /* Display the string */
966     InbvDisplayString(OemString.Buffer);
967 
968     /* Free the string buffer */
969     ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
970 
971     Status = STATUS_SUCCESS;
972 
973 Quit:
974     /* Free the captured string */
975     ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
976 
977     return Status;
978 }
979 
980 #ifdef INBV_ROTBAR_IMPLEMENTED
981 static
982 VOID
983 NTAPI
984 InbvRotationThread(
985     _In_ PVOID Context)
986 {
987     ULONG X, Y, Index, Total;
988     LARGE_INTEGER Delay = {{0}};
989 
990     InbvAcquireLock();
991     if (RotBarSelection == RB_SQUARE_CELLS)
992     {
993         Index = 0;
994     }
995     else
996     {
997         Index = 32;
998     }
999     X = ProgressBarLeft + 2;
1000     Y = ProgressBarTop + 2;
1001     InbvReleaseLock();
1002 
1003     while (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
1004     {
1005         /* Wait for a bit */
1006         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
1007 
1008         InbvAcquireLock();
1009 
1010         /* Unknown unexpected command */
1011         ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
1012 
1013         if (PltRotBarStatus == RBS_STOP_ANIMATE)
1014         {
1015             /* Stop the thread */
1016             InbvReleaseLock();
1017             break;
1018         }
1019 
1020         if (RotBarSelection == RB_SQUARE_CELLS)
1021         {
1022             Delay.QuadPart = -800000; // 80 ms
1023             Total = 18;
1024             Index %= Total;
1025 
1026             if (Index >= 3)
1027             {
1028                 /* Fill previous bar position */
1029                 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, BV_COLOR_BLACK);
1030             }
1031             if (Index < Total - 1)
1032             {
1033                 /* Draw the progress bar bit */
1034                 if (Index < 2)
1035                 {
1036                     /* Appearing from the left */
1037                     VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
1038                 }
1039                 else if (Index >= Total - 3)
1040                 {
1041                     /* Hiding to the right */
1042                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
1043                 }
1044                 else
1045                 {
1046                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
1047                 }
1048             }
1049             Index++;
1050         }
1051         else if (RotBarSelection == RB_PROGRESS_BAR)
1052         {
1053             Delay.QuadPart = -600000; // 60 ms
1054             Total = SCREEN_WIDTH;
1055             Index %= Total;
1056 
1057             /* Right part */
1058             VidBufferToScreenBlt(RotLineBuffer, Index, SCREEN_HEIGHT-6, SCREEN_WIDTH - Index, 6, SCREEN_WIDTH);
1059             if (Index > 0)
1060             {
1061                 /* Left part */
1062                 VidBufferToScreenBlt(RotLineBuffer + (SCREEN_WIDTH - Index) / 2, 0, SCREEN_HEIGHT-6, Index - 2, 6, SCREEN_WIDTH);
1063             }
1064             Index += 32;
1065         }
1066 
1067         InbvReleaseLock();
1068     }
1069 
1070     PsTerminateSystemThread(STATUS_SUCCESS);
1071 }
1072 
1073 CODE_SEG("INIT")
1074 VOID
1075 NTAPI
1076 InbvRotBarInit(VOID)
1077 {
1078     PltRotBarStatus = RBS_FADEIN;
1079     /* Perform other initialization if needed */
1080 }
1081 #endif
1082 
1083 CODE_SEG("INIT")
1084 VOID
1085 NTAPI
1086 DisplayBootBitmap(IN BOOLEAN TextMode)
1087 {
1088     PVOID BootCopy = NULL, BootProgress = NULL, BootLogo = NULL, Header = NULL, Footer = NULL;
1089 
1090 #ifdef INBV_ROTBAR_IMPLEMENTED
1091     UCHAR Buffer[24 * 9];
1092     PVOID Bar = NULL, LineBmp = NULL;
1093     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
1094     NTSTATUS Status;
1095     HANDLE ThreadHandle = NULL;
1096 #endif
1097 
1098 #ifdef REACTOS_SKUS
1099     PVOID Text = NULL;
1100 #endif
1101 
1102 #ifdef INBV_ROTBAR_IMPLEMENTED
1103     /* Check if the animation thread has already been created */
1104     if (RotBarThreadActive)
1105     {
1106         /* Yes, just reset the progress bar but keep the thread alive */
1107         InbvAcquireLock();
1108         RotBarSelection = RB_UNSPECIFIED;
1109         InbvReleaseLock();
1110     }
1111 #endif
1112 
1113     ShowProgressBar = FALSE;
1114 
1115     /* Check if this is text mode */
1116     if (TextMode)
1117     {
1118         /*
1119          * Make the kernel resource section temporarily writable,
1120          * as we are going to change the bitmaps' palette in place.
1121          */
1122         MmChangeKernelResourceSectionProtection(MM_READWRITE);
1123 
1124         /* Check the type of the OS: workstation or server */
1125         if (SharedUserData->NtProductType == NtProductWinNt)
1126         {
1127             /* Workstation; set colors */
1128             InbvSetTextColor(BV_COLOR_WHITE);
1129             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_DARK_GRAY);
1130             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
1131 
1132             /* Get resources */
1133             Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
1134             Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
1135         }
1136         else
1137         {
1138             /* Server; set colors */
1139             InbvSetTextColor(BV_COLOR_LIGHT_CYAN);
1140             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_CYAN);
1141             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
1142 
1143             /* Get resources */
1144             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
1145             Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
1146         }
1147 
1148         /* Set the scrolling region */
1149         InbvSetScrollRegion(VID_SCROLL_AREA_LEFT, VID_SCROLL_AREA_TOP,
1150                             VID_SCROLL_AREA_RIGHT, VID_SCROLL_AREA_BOTTOM);
1151 
1152         /* Make sure we have resources */
1153         if (Header && Footer)
1154         {
1155             /* BitBlt them on the screen */
1156             BitBltAligned(Footer,
1157                           TRUE,
1158                           AL_HORIZONTAL_CENTER,
1159                           AL_VERTICAL_BOTTOM,
1160                           0, 0, 0, 59);
1161             BitBltAligned(Header,
1162                           FALSE,
1163                           AL_HORIZONTAL_CENTER,
1164                           AL_VERTICAL_TOP,
1165                           0, 0, 0, 0);
1166         }
1167 
1168         /* Restore the kernel resource section protection to be read-only */
1169         MmChangeKernelResourceSectionProtection(MM_READONLY);
1170     }
1171     else
1172     {
1173         /* Is the boot driver installed? */
1174         if (!InbvBootDriverInstalled) return;
1175 
1176         /*
1177          * Make the kernel resource section temporarily writable,
1178          * as we are going to change the bitmaps' palette in place.
1179          */
1180         MmChangeKernelResourceSectionProtection(MM_READWRITE);
1181 
1182         /* Load boot screen logo */
1183         BootLogo = InbvGetResourceAddress(IDB_LOGO_DEFAULT);
1184 
1185 #ifdef REACTOS_SKUS
1186         Text = NULL;
1187         if (SharedUserData->NtProductType == NtProductWinNt)
1188         {
1189 #ifdef INBV_ROTBAR_IMPLEMENTED
1190             /* Workstation product, use appropriate status bar color */
1191             Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1192 #endif
1193         }
1194         else
1195         {
1196             /* Display correct branding based on server suite */
1197             if (ExVerifySuite(StorageServer))
1198             {
1199                 /* Storage Server Edition */
1200                 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
1201             }
1202             else if (ExVerifySuite(ComputeServer))
1203             {
1204                 /* Compute Cluster Edition */
1205                 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
1206             }
1207             else
1208             {
1209                 /* Normal edition */
1210                 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
1211             }
1212 
1213 #ifdef INBV_ROTBAR_IMPLEMENTED
1214             /* Server product, use appropriate status bar color */
1215             Bar = InbvGetResourceAddress(IDB_BAR_DEFAULT);
1216 #endif
1217         }
1218 #else
1219         /* Use default status bar */
1220         Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1221 #endif
1222 
1223         /* Make sure we have a logo */
1224         if (BootLogo)
1225         {
1226             /* Save the main image palette for implementing the fade-in effect */
1227             PBITMAPINFOHEADER BitmapInfoHeader = BootLogo;
1228             LPRGBQUAD Palette = (LPRGBQUAD)((PUCHAR)BootLogo + BitmapInfoHeader->biSize);
1229             RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
1230 
1231             /* Draw the logo at the center of the screen */
1232             BitBltAligned(BootLogo,
1233                           TRUE,
1234                           AL_HORIZONTAL_CENTER,
1235                           AL_VERTICAL_CENTER,
1236                           0, 0, 0, 34);
1237 
1238 #ifdef INBV_ROTBAR_IMPLEMENTED
1239             /* Choose progress bar */
1240             TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
1241 #endif
1242 
1243             /* Set progress bar coordinates and display it */
1244             InbvSetProgressBarCoordinates(VID_PROGRESS_BAR_LEFT,
1245                                           VID_PROGRESS_BAR_TOP,
1246                                           VID_PROGRESS_BAR_WIDTH,
1247                                           VID_PROGRESS_BAR_HEIGHT);
1248 
1249 #ifdef REACTOS_SKUS
1250             /* Check for non-workstation products */
1251             if (SharedUserData->NtProductType != NtProductWinNt)
1252             {
1253                 /* Overwrite part of the logo for a server product */
1254                 InbvScreenToBufferBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
1255                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
1256                 InbvSolidColorFill(VID_SKU_AREA_LEFT, VID_SKU_AREA_TOP,
1257                                    VID_SKU_AREA_RIGHT, VID_SKU_AREA_BOTTOM, BV_COLOR_BLACK);
1258                 InbvBufferToScreenBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
1259                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
1260 
1261                 /* In setup mode, you haven't selected a SKU yet */
1262                 if (ExpInTextModeSetup) Text = NULL;
1263             }
1264 #endif
1265         }
1266 
1267         /* Load and draw progress bar bitmap */
1268         BootProgress = InbvGetResourceAddress(IDB_PROGRESS_BAR);
1269         BitBltAligned(BootProgress,
1270                       TRUE,
1271                       AL_HORIZONTAL_CENTER,
1272                       AL_VERTICAL_CENTER,
1273                       0, 118, 0, 0);
1274 
1275         /* Load and draw copyright text bitmap */
1276         BootCopy = InbvGetResourceAddress(IDB_COPYRIGHT);
1277         BitBltAligned(BootCopy,
1278                       TRUE,
1279                       AL_HORIZONTAL_LEFT,
1280                       AL_VERTICAL_BOTTOM,
1281                       22, 0, 0, 20);
1282 
1283 #ifdef REACTOS_SKUS
1284         /* Draw the SKU text if it exits */
1285         if (Text)
1286             BitBltPalette(Text, TRUE, VID_SKU_TEXT_LEFT, VID_SKU_TEXT_TOP);
1287 #endif
1288 
1289 #ifdef INBV_ROTBAR_IMPLEMENTED
1290         if ((TempRotBarSelection == RB_SQUARE_CELLS) && Bar)
1291         {
1292             /* Save previous screen pixels to buffer */
1293             InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
1294             /* Draw the progress bar bit */
1295             BitBltPalette(Bar, TRUE, 0, 0);
1296             /* Store it in global buffer */
1297             InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
1298             /* Restore screen pixels */
1299             InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
1300         }
1301 
1302         /*
1303          * Add a rotating bottom horizontal bar when using a progress bar,
1304          * to show that ReactOS can be still alive when the bar does not
1305          * appear to progress.
1306          */
1307         if (TempRotBarSelection == RB_PROGRESS_BAR)
1308         {
1309             LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
1310             if (LineBmp)
1311             {
1312                 /* Draw the line and store it in global buffer */
1313                 BitBltPalette(LineBmp, TRUE, 0, SCREEN_HEIGHT-6);
1314                 InbvScreenToBufferBlt(RotLineBuffer, 0, SCREEN_HEIGHT-6, SCREEN_WIDTH, 6, SCREEN_WIDTH);
1315             }
1316         }
1317         else
1318         {
1319             /* Hide the simple progress bar if not used */
1320             ShowProgressBar = FALSE;
1321         }
1322 #endif
1323 
1324         /* Restore the kernel resource section protection to be read-only */
1325         MmChangeKernelResourceSectionProtection(MM_READONLY);
1326 
1327         /* Display the boot logo and fade it in */
1328         BootLogoFadeIn();
1329 
1330 #ifdef INBV_ROTBAR_IMPLEMENTED
1331         if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
1332         {
1333             /* Start the animation thread */
1334             Status = PsCreateSystemThread(&ThreadHandle,
1335                                           0,
1336                                           NULL,
1337                                           NULL,
1338                                           NULL,
1339                                           InbvRotationThread,
1340                                           NULL);
1341             if (NT_SUCCESS(Status))
1342             {
1343                 /* The thread has started, close the handle as we don't need it */
1344                 RotBarThreadActive = TRUE;
1345                 ObCloseHandle(ThreadHandle, KernelMode);
1346             }
1347         }
1348 #endif
1349 
1350         /* Set filter which will draw text display if needed */
1351         InbvInstallDisplayStringFilter(DisplayFilter);
1352     }
1353 
1354 #ifdef INBV_ROTBAR_IMPLEMENTED
1355     /* Do we have the animation thread? */
1356     if (RotBarThreadActive)
1357     {
1358         /* We do, initialize the progress bar */
1359         InbvAcquireLock();
1360         RotBarSelection = TempRotBarSelection;
1361         InbvRotBarInit();
1362         InbvReleaseLock();
1363     }
1364 #endif
1365 }
1366 
1367 CODE_SEG("INIT")
1368 VOID
1369 NTAPI
1370 DisplayFilter(PCHAR *String)
1371 {
1372     /* Windows hack to skip first dots */
1373     static BOOLEAN DotHack = TRUE;
1374 
1375     /* If "." is given set *String to empty string */
1376     if (DotHack && strcmp(*String, ".") == 0)
1377         *String = "";
1378 
1379     if (**String)
1380     {
1381         /* Remove the filter */
1382         InbvInstallDisplayStringFilter(NULL);
1383 
1384         DotHack = FALSE;
1385 
1386         /* Draw text screen */
1387         DisplayBootBitmap(TRUE);
1388     }
1389 }
1390 
1391 CODE_SEG("INIT")
1392 VOID
1393 NTAPI
1394 FinalizeBootLogo(VOID)
1395 {
1396     /* Acquire lock and check the display state */
1397     InbvAcquireLock();
1398     if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
1399     {
1400         /* Clear the screen */
1401         VidSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_BLACK);
1402     }
1403 
1404     /* Reset progress bar and lock */
1405 #ifdef INBV_ROTBAR_IMPLEMENTED
1406     PltRotBarStatus = RBS_STOP_ANIMATE;
1407     RotBarThreadActive = FALSE;
1408 #endif
1409     InbvReleaseLock();
1410 }
1411