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