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