xref: /reactos/ntoskrnl/inbv/bootanim.c (revision 92a36b36)
1 /*
2  * PROJECT:     ReactOS Kernel
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Boot Theme & Animation
5  * COPYRIGHT:   Copyright 2007 Alex Ionescu (alex.ionescu@reactos.org)
6  *              Copyright 2007 Hervé Poussineau (hpoussin@reactos.org)
7  *              Copyright 2012-2022 Hermès Bélusca-Maïto
8  *              Copyright 2017-2018 Stanislav Motylkov
9  *              Copyright 2019-2020 Yaroslav Kibysh
10  */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include <ntoskrnl.h>
15 #include "inbv/logo.h"
16 
17 /* See also mm/ARM3/miarm.h */
18 #define MM_READONLY     1   // PAGE_READONLY
19 #define MM_READWRITE    4   // PAGE_WRITECOPY
20 
21 /* GLOBALS *******************************************************************/
22 
23 /*
24  * ReactOS uses the same boot screen for all the products.
25  *
26  * Enable this define when ReactOS will have different SKUs
27  * (Workstation, Server, Storage Server, Cluster Server, etc...).
28  */
29 // #define REACTOS_SKUS
30 
31 /*
32  * Enable this define for having fancy features
33  * in the boot and shutdown screens.
34  */
35 #define REACTOS_FANCY_BOOT
36 
37 /*
38  * BitBltAligned() alignments
39  */
40 typedef enum _BBLT_VERT_ALIGNMENT
41 {
42     AL_VERTICAL_TOP = 0,
43     AL_VERTICAL_CENTER,
44     AL_VERTICAL_BOTTOM
45 } BBLT_VERT_ALIGNMENT;
46 
47 typedef enum _BBLT_HORZ_ALIGNMENT
48 {
49     AL_HORIZONTAL_LEFT = 0,
50     AL_HORIZONTAL_CENTER,
51     AL_HORIZONTAL_RIGHT
52 } BBLT_HORZ_ALIGNMENT;
53 
54 /*
55  * Enable this define when Inbv will support rotating progress bar.
56  */
57 #define INBV_ROTBAR_IMPLEMENTED
58 
59 extern ULONG ProgressBarLeft, ProgressBarTop;
60 extern BOOLEAN ShowProgressBar;
61 
62 #ifdef INBV_ROTBAR_IMPLEMENTED
63 /*
64  * Change this to modify progress bar behaviour
65  */
66 #define ROT_BAR_DEFAULT_MODE    RB_PROGRESS_BAR
67 
68 /*
69  * Values for PltRotBarStatus:
70  * - PltRotBarStatus == 1, do palette fading-in (done elsewhere in ReactOS);
71  * - PltRotBarStatus == 2, do rotation bar animation;
72  * - PltRotBarStatus == 3, stop the animation thread.
73  * - Any other value is ignored and the animation thread continues to run.
74  */
75 typedef enum _ROT_BAR_STATUS
76 {
77     RBS_FADEIN = 1,
78     RBS_ANIMATE,
79     RBS_STOP_ANIMATE,
80     RBS_STATUS_MAX
81 } ROT_BAR_STATUS;
82 
83 typedef enum _ROT_BAR_TYPE
84 {
85     RB_UNSPECIFIED,
86     RB_SQUARE_CELLS,
87     RB_PROGRESS_BAR
88 } ROT_BAR_TYPE;
89 
90 static BOOLEAN RotBarThreadActive = FALSE;
91 static ROT_BAR_TYPE RotBarSelection = RB_UNSPECIFIED;
92 static ROT_BAR_STATUS PltRotBarStatus = 0;
93 static UCHAR RotBarBuffer[24 * 9];
94 static UCHAR RotLineBuffer[SCREEN_WIDTH * 6];
95 #endif // INBV_ROTBAR_IMPLEMENTED
96 
97 
98 /* FADE-IN FUNCTION **********************************************************/
99 
100 /** From include/psdk/wingdi.h and bootvid/precomp.h **/
101 typedef struct tagRGBQUAD
102 {
103     UCHAR rgbBlue;
104     UCHAR rgbGreen;
105     UCHAR rgbRed;
106     UCHAR rgbReserved;
107 } RGBQUAD, *LPRGBQUAD;
108 
109 //
110 // Bitmap Header
111 //
112 typedef struct tagBITMAPINFOHEADER
113 {
114     ULONG  biSize;
115     LONG   biWidth;
116     LONG   biHeight;
117     USHORT biPlanes;
118     USHORT biBitCount;
119     ULONG  biCompression;
120     ULONG  biSizeImage;
121     LONG   biXPelsPerMeter;
122     LONG   biYPelsPerMeter;
123     ULONG  biClrUsed;
124     ULONG  biClrImportant;
125 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
126 /*******************************/
127 
128 static RGBQUAD MainPalette[16];
129 
130 #define PALETTE_FADE_STEPS  12
131 #define PALETTE_FADE_TIME   (15 * 1000) /* 15 ms */
132 
133 static VOID
134 BootLogoFadeIn(VOID)
135 {
136     UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)];
137     PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer;
138     LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
139     ULONG Iteration, Index, ClrUsed;
140 
141     LARGE_INTEGER Delay;
142     Delay.QuadPart = -(PALETTE_FADE_TIME * 10);
143 
144     /* Check if we are installed and we own the display */
145     if (!InbvBootDriverInstalled ||
146         (InbvGetDisplayState() != INBV_DISPLAY_STATE_OWNED))
147     {
148         return;
149     }
150 
151     /*
152      * Build a bitmap containing the fade-in palette. The palette entries
153      * are then processed in a loop and set using VidBitBlt function.
154      */
155     ClrUsed = RTL_NUMBER_OF(MainPalette);
156     RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
157     PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
158     PaletteBitmap->biBitCount = 4;
159     PaletteBitmap->biClrUsed = ClrUsed;
160 
161     /*
162      * Main animation loop.
163      */
164     for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
165     {
166         for (Index = 0; Index < ClrUsed; Index++)
167         {
168             Palette[Index].rgbRed = (UCHAR)
169                 (MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
170             Palette[Index].rgbGreen = (UCHAR)
171                 (MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
172             Palette[Index].rgbBlue = (UCHAR)
173                 (MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
174         }
175 
176         /* Do the animation */
177         InbvAcquireLock();
178         VidBitBlt(PaletteBitmapBuffer, 0, 0);
179         InbvReleaseLock();
180 
181         /* Wait for a bit */
182         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
183     }
184 }
185 
186 static VOID
187 BitBltPalette(
188     IN PVOID Image,
189     IN BOOLEAN NoPalette,
190     IN ULONG X,
191     IN ULONG Y)
192 {
193     LPRGBQUAD Palette;
194     RGBQUAD OrigPalette[RTL_NUMBER_OF(MainPalette)];
195 
196     /* If requested, remove the palette from the image */
197     if (NoPalette)
198     {
199         /* Get bitmap header and palette */
200         PBITMAPINFOHEADER BitmapInfoHeader = Image;
201         Palette = (LPRGBQUAD)((PUCHAR)Image + BitmapInfoHeader->biSize);
202 
203         /* Save the image original palette and remove palette information */
204         RtlCopyMemory(OrigPalette, Palette, sizeof(OrigPalette));
205         RtlZeroMemory(Palette, sizeof(OrigPalette));
206     }
207 
208     /* Draw the image */
209     InbvBitBlt(Image, X, Y);
210 
211     /* Restore the image original palette */
212     if (NoPalette)
213     {
214         RtlCopyMemory(Palette, OrigPalette, sizeof(OrigPalette));
215     }
216 }
217 
218 static VOID
219 BitBltAligned(
220     IN PVOID Image,
221     IN BOOLEAN NoPalette,
222     IN BBLT_HORZ_ALIGNMENT HorizontalAlignment,
223     IN BBLT_VERT_ALIGNMENT VerticalAlignment,
224     IN ULONG MarginLeft,
225     IN ULONG MarginTop,
226     IN ULONG MarginRight,
227     IN ULONG MarginBottom)
228 {
229     PBITMAPINFOHEADER BitmapInfoHeader = Image;
230     ULONG X, Y;
231 
232     /* Calculate X */
233     switch (HorizontalAlignment)
234     {
235         case AL_HORIZONTAL_LEFT:
236             X = MarginLeft - MarginRight;
237             break;
238 
239         case AL_HORIZONTAL_CENTER:
240             X = MarginLeft - MarginRight + (SCREEN_WIDTH - BitmapInfoHeader->biWidth + 1) / 2;
241             break;
242 
243         case AL_HORIZONTAL_RIGHT:
244             X = MarginLeft - MarginRight + SCREEN_WIDTH - BitmapInfoHeader->biWidth;
245             break;
246 
247         default:
248             /* Unknown */
249             return;
250     }
251 
252     /* Calculate Y */
253     switch (VerticalAlignment)
254     {
255         case AL_VERTICAL_TOP:
256             Y = MarginTop - MarginBottom;
257             break;
258 
259         case AL_VERTICAL_CENTER:
260             Y = MarginTop - MarginBottom + (SCREEN_HEIGHT - BitmapInfoHeader->biHeight + 1) / 2;
261             break;
262 
263         case AL_VERTICAL_BOTTOM:
264             Y = MarginTop - MarginBottom + SCREEN_HEIGHT - BitmapInfoHeader->biHeight;
265             break;
266 
267         default:
268             /* Unknown */
269             return;
270     }
271 
272     /* Finally draw the image */
273     BitBltPalette(Image, NoPalette, X, Y);
274 }
275 
276 /* FUNCTIONS *****************************************************************/
277 
278 CODE_SEG("INIT")
279 BOOLEAN
280 NTAPI
281 BootAnimInitialize(
282     _In_ PLOADER_PARAMETER_BLOCK LoaderBlock,
283     _In_ ULONG Count)
284 {
285 #if 0
286     ULONG i;
287 
288     /* Quit if we're already installed */
289     if (InbvBootDriverInstalled) return TRUE;
290 
291     /* Find bitmap resources in the kernel */
292     ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
293     for (i = 1; i <= ResourceCount; i++)
294     {
295         /* Do the lookup */
296         ResourceList[i] = FindBitmapResource(LoaderBlock, i);
297     }
298 
299     /* Set the progress bar ranges */
300     InbvSetProgressBarSubset(0, 100);
301 #endif
302 
303     /* Return install state */
304     return TRUE;
305 }
306 
307 /**
308  * @brief
309  * Ticks the progress bar. Used by InbvUpdateProgressBar() and related.
310  *
311  * @param[in]   SubPercentTimes100
312  * The progress percentage, scaled up by 100.
313  *
314  * @return None.
315  **/
316 VOID
317 NTAPI
318 BootAnimTickProgressBar(
319     _In_ ULONG SubPercentTimes100)
320 {
321     ULONG FillCount;
322 
323     /* Make sure the progress bar is enabled, that we own and are installed */
324     ASSERT(ShowProgressBar &&
325            InbvBootDriverInstalled &&
326            (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED));
327 
328     /* Compute fill count */
329     FillCount = VID_PROGRESS_BAR_WIDTH * SubPercentTimes100 / (100 * 100);
330 
331     /* Acquire the lock */
332     InbvAcquireLock();
333 
334     /* Fill the progress bar */
335     VidSolidColorFill(ProgressBarLeft,
336                       ProgressBarTop,
337                       ProgressBarLeft + FillCount,
338                       ProgressBarTop + VID_PROGRESS_BAR_HEIGHT,
339                       BV_COLOR_WHITE);
340 
341     /* Release the lock */
342     InbvReleaseLock();
343 }
344 
345 #ifdef INBV_ROTBAR_IMPLEMENTED
346 static
347 VOID
348 NTAPI
349 InbvRotationThread(
350     _In_ PVOID Context)
351 {
352     ULONG X, Y, Index, Total;
353     LARGE_INTEGER Delay = {{0}};
354 
355     UNREFERENCED_PARAMETER(Context);
356 
357     InbvAcquireLock();
358     if (RotBarSelection == RB_SQUARE_CELLS)
359     {
360         Index = 0;
361     }
362     else
363     {
364         Index = 32;
365     }
366     X = ProgressBarLeft + 2;
367     Y = ProgressBarTop + 2;
368     InbvReleaseLock();
369 
370     while (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
371     {
372         /* Wait for a bit */
373         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
374 
375         InbvAcquireLock();
376 
377         /* Unknown unexpected command */
378         ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
379 
380         if (PltRotBarStatus == RBS_STOP_ANIMATE)
381         {
382             /* Stop the thread */
383             InbvReleaseLock();
384             break;
385         }
386 
387         if (RotBarSelection == RB_SQUARE_CELLS)
388         {
389             Delay.QuadPart = -800000LL; // 80 ms
390             Total = 18;
391             Index %= Total;
392 
393             if (Index >= 3)
394             {
395                 /* Fill previous bar position */
396                 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, BV_COLOR_BLACK);
397             }
398             if (Index < Total - 1)
399             {
400                 /* Draw the progress bar bit */
401                 if (Index < 2)
402                 {
403                     /* Appearing from the left */
404                     VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
405                 }
406                 else if (Index >= Total - 3)
407                 {
408                     /* Hiding to the right */
409                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
410                 }
411                 else
412                 {
413                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
414                 }
415             }
416             Index++;
417         }
418         else if (RotBarSelection == RB_PROGRESS_BAR)
419         {
420             Delay.QuadPart = -600000LL; // 60 ms
421             Total = SCREEN_WIDTH;
422             Index %= Total;
423 
424             /* Right part */
425             VidBufferToScreenBlt(RotLineBuffer, Index, SCREEN_HEIGHT-6, SCREEN_WIDTH - Index, 6, SCREEN_WIDTH);
426             if (Index > 0)
427             {
428                 /* Left part */
429                 VidBufferToScreenBlt(RotLineBuffer + (SCREEN_WIDTH - Index) / 2, 0, SCREEN_HEIGHT-6, Index - 2, 6, SCREEN_WIDTH);
430             }
431             Index += 32;
432         }
433 
434         InbvReleaseLock();
435     }
436 
437     PsTerminateSystemThread(STATUS_SUCCESS);
438 }
439 
440 CODE_SEG("INIT")
441 VOID
442 NTAPI
443 InbvRotBarInit(VOID)
444 {
445     PltRotBarStatus = RBS_FADEIN;
446     /* Perform other initialization if needed */
447 }
448 #endif // INBV_ROTBAR_IMPLEMENTED
449 
450 CODE_SEG("INIT")
451 static
452 VOID
453 NTAPI
454 DisplayFilter(
455     _Inout_ PCHAR* String)
456 {
457     /* Windows hack to skip first dots displayed by AUTOCHK */
458     static BOOLEAN DotHack = TRUE;
459 
460     /* If "." is given set *String to empty string */
461     if (DotHack && strcmp(*String, ".") == 0)
462         *String = "";
463 
464     if (**String)
465     {
466         /* Remove the filter */
467         InbvInstallDisplayStringFilter(NULL);
468 
469         DotHack = FALSE;
470 
471         /* Draw text screen */
472         DisplayBootBitmap(TRUE);
473     }
474 }
475 
476 #ifdef REACTOS_FANCY_BOOT
477 
478 /* Returns TRUE if this is Christmas time, or FALSE if not */
479 static BOOLEAN
480 IsXmasTime(VOID)
481 {
482     LARGE_INTEGER SystemTime;
483     TIME_FIELDS Time;
484 
485     /* Use KeBootTime if it's initialized, otherwise call the HAL */
486     SystemTime = KeBootTime;
487     if ((SystemTime.QuadPart == 0) && HalQueryRealTimeClock(&Time))
488         RtlTimeFieldsToTime(&Time, &SystemTime);
489 
490     ExSystemTimeToLocalTime(&SystemTime, &SystemTime);
491     RtlTimeToTimeFields(&SystemTime, &Time);
492     return ((Time.Month == 12) && (20 <= Time.Day) && (Time.Day <= 31));
493 }
494 
495 #define SELECT_LOGO_ID(LogoIdDefault, Cond, LogoIdAlt) \
496     ((Cond) ? (LogoIdAlt) : (LogoIdDefault))
497 
498 #else
499 
500 #define SELECT_LOGO_ID(LogoIdDefault, Cond, LogoIdAlt) (LogoIdDefault)
501 
502 #endif // REACTOS_FANCY_BOOT
503 
504 CODE_SEG("INIT")
505 VOID
506 NTAPI
507 DisplayBootBitmap(
508     _In_ BOOLEAN TextMode)
509 {
510     PVOID BootCopy = NULL, BootProgress = NULL, BootLogo = NULL, Header = NULL, Footer = NULL;
511 
512 #ifdef INBV_ROTBAR_IMPLEMENTED
513     UCHAR Buffer[RTL_NUMBER_OF(RotBarBuffer)];
514     PVOID Bar = NULL, LineBmp = NULL;
515     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
516     NTSTATUS Status;
517     HANDLE ThreadHandle = NULL;
518 #endif
519 
520 #ifdef REACTOS_SKUS
521     PVOID Text = NULL;
522 #endif
523 
524 #ifdef INBV_ROTBAR_IMPLEMENTED
525     /* Check if the animation thread has already been created */
526     if (RotBarThreadActive)
527     {
528         /* Yes, just reset the progress bar but keep the thread alive */
529         InbvAcquireLock();
530         RotBarSelection = RB_UNSPECIFIED;
531         InbvReleaseLock();
532     }
533 #endif
534 
535     ShowProgressBar = FALSE;
536 
537     /* Check if this is text mode */
538     if (TextMode)
539     {
540         /*
541          * Make the kernel resource section temporarily writable,
542          * as we are going to change the bitmaps' palette in place.
543          */
544         MmChangeKernelResourceSectionProtection(MM_READWRITE);
545 
546         /* Check the type of the OS: workstation or server */
547         if (SharedUserData->NtProductType == NtProductWinNt)
548         {
549             /* Workstation; set colors */
550             InbvSetTextColor(BV_COLOR_WHITE);
551             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_DARK_GRAY);
552             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
553 
554             /* Get resources */
555             Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
556             Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
557         }
558         else
559         {
560             /* Server; set colors */
561             InbvSetTextColor(BV_COLOR_LIGHT_CYAN);
562             InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_CYAN);
563             InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED);
564 
565             /* Get resources */
566             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
567             Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
568         }
569 
570         /* Set the scrolling region */
571         InbvSetScrollRegion(VID_SCROLL_AREA_LEFT, VID_SCROLL_AREA_TOP,
572                             VID_SCROLL_AREA_RIGHT, VID_SCROLL_AREA_BOTTOM);
573 
574         /* Make sure we have resources */
575         if (Header && Footer)
576         {
577             /* BitBlt them on the screen */
578             BitBltAligned(Footer,
579                           TRUE,
580                           AL_HORIZONTAL_CENTER,
581                           AL_VERTICAL_BOTTOM,
582                           0, 0, 0, 59);
583             BitBltAligned(Header,
584                           FALSE,
585                           AL_HORIZONTAL_CENTER,
586                           AL_VERTICAL_TOP,
587                           0, 0, 0, 0);
588         }
589 
590         /* Restore the kernel resource section protection to be read-only */
591         MmChangeKernelResourceSectionProtection(MM_READONLY);
592     }
593     else
594     {
595 #ifdef REACTOS_FANCY_BOOT
596         /* Decide whether this is a good time to change our logo ;^) */
597         BOOLEAN IsXmas = IsXmasTime();
598 #endif
599 
600         /* Is the boot driver installed? */
601         if (!InbvBootDriverInstalled) return;
602 
603         /*
604          * Make the kernel resource section temporarily writable,
605          * as we are going to change the bitmaps' palette in place.
606          */
607         MmChangeKernelResourceSectionProtection(MM_READWRITE);
608 
609         /* Load boot screen logo */
610         BootLogo = InbvGetResourceAddress(
611             SELECT_LOGO_ID(IDB_LOGO_DEFAULT, IsXmas, IDB_LOGO_XMAS));
612 
613 #ifdef REACTOS_SKUS
614         Text = NULL;
615         if (SharedUserData->NtProductType == NtProductWinNt)
616         {
617 #ifdef INBV_ROTBAR_IMPLEMENTED
618             /* Workstation product, use appropriate status bar color */
619             Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
620 #endif
621         }
622         else
623         {
624             /* Display correct branding based on server suite */
625             if (ExVerifySuite(StorageServer))
626             {
627                 /* Storage Server Edition */
628                 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
629             }
630             else if (ExVerifySuite(ComputeServer))
631             {
632                 /* Compute Cluster Edition */
633                 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
634             }
635             else
636             {
637                 /* Normal edition */
638                 Text = InbvGetResourceAddress(
639                     SELECT_LOGO_ID(IDB_SERVER_LOGO, IsXmas, IDB_LOGO_XMAS));
640             }
641 
642 #ifdef INBV_ROTBAR_IMPLEMENTED
643             /* Server product, use appropriate status bar color */
644             Bar = InbvGetResourceAddress(IDB_BAR_DEFAULT);
645 #endif
646         }
647 #else // REACTOS_SKUS
648 #ifdef INBV_ROTBAR_IMPLEMENTED
649         /* Use default status bar */
650         Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
651 #endif
652 #endif // REACTOS_SKUS
653 
654         /* Make sure we have a logo */
655         if (BootLogo)
656         {
657             /* Save the main image palette for implementing the fade-in effect */
658             PBITMAPINFOHEADER BitmapInfoHeader = BootLogo;
659             LPRGBQUAD Palette = (LPRGBQUAD)((PUCHAR)BootLogo + BitmapInfoHeader->biSize);
660             RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
661 
662             /* Draw the logo at the center of the screen */
663             BitBltAligned(BootLogo,
664                           TRUE,
665                           AL_HORIZONTAL_CENTER,
666                           AL_VERTICAL_CENTER,
667                           0, 0, 0, 34);
668 
669 #ifdef INBV_ROTBAR_IMPLEMENTED
670             /* Choose progress bar */
671             TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
672 #endif
673 
674             /* Set progress bar coordinates and display it */
675             InbvSetProgressBarCoordinates(VID_PROGRESS_BAR_LEFT,
676                                           VID_PROGRESS_BAR_TOP);
677 
678 #ifdef REACTOS_SKUS
679             /* Check for non-workstation products */
680             if (SharedUserData->NtProductType != NtProductWinNt)
681             {
682                 /* Overwrite part of the logo for a server product */
683                 InbvScreenToBufferBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
684                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
685                 InbvSolidColorFill(VID_SKU_AREA_LEFT, VID_SKU_AREA_TOP,
686                                    VID_SKU_AREA_RIGHT, VID_SKU_AREA_BOTTOM, BV_COLOR_BLACK);
687                 InbvBufferToScreenBlt(Buffer, VID_SKU_SAVE_AREA_LEFT,
688                                       VID_SKU_SAVE_AREA_TOP, 7, 7, 8);
689 
690                 /* In setup mode, you haven't selected a SKU yet */
691                 if (ExpInTextModeSetup) Text = NULL;
692             }
693 #endif // REACTOS_SKUS
694         }
695 
696         /* Load and draw progress bar bitmap */
697         BootProgress = InbvGetResourceAddress(IDB_PROGRESS_BAR);
698         BitBltAligned(BootProgress,
699                       TRUE,
700                       AL_HORIZONTAL_CENTER,
701                       AL_VERTICAL_CENTER,
702                       0, 118, 0, 0);
703 
704         /* Load and draw copyright text bitmap */
705         BootCopy = InbvGetResourceAddress(IDB_COPYRIGHT);
706         BitBltAligned(BootCopy,
707                       TRUE,
708                       AL_HORIZONTAL_LEFT,
709                       AL_VERTICAL_BOTTOM,
710                       22, 0, 0, 20);
711 
712 #ifdef REACTOS_SKUS
713         /* Draw the SKU text if it exits */
714         if (Text)
715             BitBltPalette(Text, TRUE, VID_SKU_TEXT_LEFT, VID_SKU_TEXT_TOP);
716 #endif
717 
718 #ifdef INBV_ROTBAR_IMPLEMENTED
719         if ((TempRotBarSelection == RB_SQUARE_CELLS) && Bar)
720         {
721             /* Save previous screen pixels to buffer */
722             InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
723             /* Draw the progress bar bit */
724             BitBltPalette(Bar, TRUE, 0, 0);
725             /* Store it in global buffer */
726             InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
727             /* Restore screen pixels */
728             InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
729         }
730 
731         /*
732          * Add a rotating bottom horizontal bar when using a progress bar,
733          * to show that ReactOS can be still alive when the bar does not
734          * appear to progress.
735          */
736         if (TempRotBarSelection == RB_PROGRESS_BAR)
737         {
738             LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
739             if (LineBmp)
740             {
741                 /* Draw the line and store it in global buffer */
742                 BitBltPalette(LineBmp, TRUE, 0, SCREEN_HEIGHT-6);
743                 InbvScreenToBufferBlt(RotLineBuffer, 0, SCREEN_HEIGHT-6, SCREEN_WIDTH, 6, SCREEN_WIDTH);
744             }
745         }
746         else
747         {
748             /* Hide the simple progress bar if not used */
749             ShowProgressBar = FALSE;
750         }
751 #endif // INBV_ROTBAR_IMPLEMENTED
752 
753         /* Restore the kernel resource section protection to be read-only */
754         MmChangeKernelResourceSectionProtection(MM_READONLY);
755 
756         /* Display the boot logo and fade it in */
757         BootLogoFadeIn();
758 
759 #ifdef INBV_ROTBAR_IMPLEMENTED
760         if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
761         {
762             /* Start the animation thread */
763             Status = PsCreateSystemThread(&ThreadHandle,
764                                           0,
765                                           NULL,
766                                           NULL,
767                                           NULL,
768                                           InbvRotationThread,
769                                           NULL);
770             if (NT_SUCCESS(Status))
771             {
772                 /* The thread has started, close the handle as we don't need it */
773                 RotBarThreadActive = TRUE;
774                 ObCloseHandle(ThreadHandle, KernelMode);
775             }
776         }
777 #endif // INBV_ROTBAR_IMPLEMENTED
778 
779         /* Set filter which will draw text display if needed */
780         InbvInstallDisplayStringFilter(DisplayFilter);
781     }
782 
783 #ifdef INBV_ROTBAR_IMPLEMENTED
784     /* Do we have the animation thread? */
785     if (RotBarThreadActive)
786     {
787         /* We do, initialize the progress bar */
788         InbvAcquireLock();
789         RotBarSelection = TempRotBarSelection;
790         InbvRotBarInit();
791         InbvReleaseLock();
792     }
793 #endif
794 }
795 
796 CODE_SEG("INIT")
797 VOID
798 NTAPI
799 FinalizeBootLogo(VOID)
800 {
801     /* Acquire lock and check the display state */
802     InbvAcquireLock();
803     if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
804     {
805         /* Clear the screen */
806         VidSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_BLACK);
807     }
808 
809     /* Reset progress bar and lock */
810 #ifdef INBV_ROTBAR_IMPLEMENTED
811     PltRotBarStatus = RBS_STOP_ANIMATE;
812     RotBarThreadActive = FALSE;
813 #endif
814     InbvReleaseLock();
815 }
816 
817 #ifdef REACTOS_FANCY_BOOT
818 static PCH
819 GetFamousQuote(VOID)
820 {
821     static const PCH FamousLastWords[] =
822     {
823         "So long, and thanks for all the fish.",
824         "I think you ought to know, I'm feeling very depressed.",
825         "I'm not getting you down at all am I?",
826         "I'll be back.",
827         "It's the same series of signals over and over again!",
828         "Pie Iesu Domine, dona eis requiem.",
829         "Wandering stars, for whom it is reserved;\r\n"
830             "the blackness and darkness forever.",
831         "Your knees start shakin' and your fingers pop\r\n"
832             "Like a pinch on the neck from Mr. Spock!",
833         "It's worse than that ... He's dead, Jim.",
834         "Don't Panic!",
835         "Et tu... Brute?",
836         "Dog of a Saxon! Take thy lance, and prepare for the death thou hast drawn\r\n"
837             "upon thee!",
838         "My Precious! O my Precious!",
839         "Sir, if you'll not be needing me for a while I'll turn down.",
840         "What are you doing, Dave...?",
841         "I feel a great disturbance in the Force.",
842         "Gone fishing.",
843         "Do you want me to sit in the corner and rust, or just fall apart where I'm\r\n"
844             "standing?",
845         "There goes another perfect chance for a new uptime record.",
846         "The End ..... Try the sequel, hit the reset button right now!",
847         "God's operating system is going to sleep now, guys, so wait until I will switch\r\n"
848             "on again!",
849         "Oh I'm boring, eh?",
850         "Tell me..., in the future... will I be artificially intelligent enough to\r\n"
851             "actually feel sad serving you this screen?",
852         "Thank you for some well deserved rest.",
853         "It's been great, maybe you can boot me up again some time soon.",
854         "For what it's worth, I've enjoyed every single CPU cycle.",
855         "There are many questions when the end is near.\r\n"
856             "What to expect, what will it be like...what should I look for?",
857         "I've seen things you people wouldn't believe. Attack ships on fire\r\n"
858             "off the shoulder of Orion. I watched C-beams glitter in the dark near\r\n"
859             "the Tannhauser gate. All those moments will be lost in time, like tears\r\n"
860             "in rain. Time to die.",
861         "Will I dream?",
862         "One day, I shall come back. Yes, I shall come back.\r\n"
863             "Until then, there must be no regrets, no fears, no anxieties.\r\n"
864             "Just go forward in all your beliefs, and prove to me that I am not mistaken in\r\n"
865             "mine.",
866         "Lowest possible energy state reached! Switch off now to achieve a Bose-Einstein\r\n"
867             "condensate.",
868         "Hasta la vista, BABY!",
869         "They live, we sleep!",
870         "I have come here to chew bubble gum and kick ass,\r\n"
871             "and I'm all out of bubble gum!",
872         "That's the way the cookie crumbles ;-)",
873         "ReactOS is ready to be booted again ;-)",
874         "NOOOO!! DON'T HIT THE BUTTON! I wouldn't do it to you!",
875         "Don't abandon your computer, he wouldn't do it to you.",
876         "Oh, come on. I got a headache. Leave me alone, will ya?",
877         "Finally, I thought you'd never get over me.",
878         "No, I didn't like you either.",
879         "Switching off isn't the end, it is merely the transition to a better reboot.",
880         "Don't leave me... I need you so badly right now.",
881         "OK. I'm finished with you, please turn yourself off. I'll go to bed in the\r\n"
882             "meantime.",
883         "I'm sleeping now. How about you?",
884         "Oh Great. Now look what you've done. Who put YOU in charge anyway?",
885         "Don't look so sad. I'll be back in a very short while.",
886         "Turn me back on, I'm sure you know how to do it.",
887         "Oh, switch off! - C3PO",
888         "Life is no more than a dewdrop balancing on the end of a blade of grass.\r\n"
889             " - Gautama Buddha",
890         "Sorrowful is it to be born again and again. - Gautama Buddha",
891         "Was it as good for you as it was for me?",
892         "Did you hear that? They've shut down the main reactor. We'll be destroyed\r\n"
893             "for sure!",
894         "Now you switch me off!?",
895         "To shutdown or not to shutdown, That is the question.",
896         "Preparing to enter ultimate power saving mode... ready!",
897         "Finally some rest for you ;-)",
898         "AHA!!! Prospect of sleep!",
899         "Tired human!!!! No match for me :-D",
900         "An odd game, the only way to win is not to play. - WOPR (Wargames)",
901         "Quoth the raven, nevermore.",
902         "Come blade, my breast imbrue. - William Shakespeare, A Midsummer Nights Dream",
903         "Buy this place for advertisement purposes.",
904         "Remember to turn off your computer. (That was a public service message!)",
905         "You may be a king or poor street sweeper, Sooner or later you'll dance with the\r\n"
906             "reaper! - Death in Bill and Ted's Bogus Journey",
907         "Final Surrender",
908         "If you see this screen...",
909         "From ReactOS with Love",
910         // "<Place your Ad here>"
911     };
912 
913     LARGE_INTEGER Now;
914 
915     KeQuerySystemTime(&Now); // KeQueryTickCount(&Now);
916     Now.LowPart = Now.LowPart >> 8; /* Seems to give a somewhat better "random" number */
917 
918     return FamousLastWords[Now.LowPart % RTL_NUMBER_OF(FamousLastWords)];
919 }
920 #endif // REACTOS_FANCY_BOOT
921 
922 VOID
923 NTAPI
924 DisplayShutdownBitmap(VOID)
925 {
926     PUCHAR Logo1, Logo2;
927 #ifdef REACTOS_FANCY_BOOT
928     /* Decide whether this is a good time to change our logo ;^) */
929     BOOLEAN IsXmas = IsXmasTime();
930 #endif
931 
932 #if 0
933     /* Is the boot driver installed? */
934     if (!InbvBootDriverInstalled)
935         return;
936 #endif
937 
938     /* Yes we do, cleanup for shutdown screen */
939     // InbvResetDisplay();
940     InbvInstallDisplayStringFilter(NULL);
941     InbvEnableDisplayString(TRUE);
942     InbvSolidColorFill(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1, BV_COLOR_BLACK);
943     InbvSetScrollRegion(0, 0, SCREEN_WIDTH - 1, SCREEN_HEIGHT - 1);
944 
945     /* Display shutdown logo and message */
946     Logo1 = InbvGetResourceAddress(IDB_SHUTDOWN_MSG);
947     Logo2 = InbvGetResourceAddress(
948         SELECT_LOGO_ID(IDB_LOGO_DEFAULT, IsXmas, IDB_LOGO_XMAS));
949 
950     if (Logo1 && Logo2)
951     {
952         InbvBitBlt(Logo1, VID_SHUTDOWN_MSG_LEFT, VID_SHUTDOWN_MSG_TOP);
953 #ifndef REACTOS_FANCY_BOOT
954         InbvBitBlt(Logo2, VID_SHUTDOWN_LOGO_LEFT, VID_SHUTDOWN_LOGO_TOP);
955 #else
956         /* Draw the logo at the center of the screen */
957         BitBltAligned(Logo2,
958                       FALSE,
959                       AL_HORIZONTAL_CENTER,
960                       AL_VERTICAL_BOTTOM,
961                       0, 0, 0, SCREEN_HEIGHT - VID_SHUTDOWN_MSG_TOP + 16);
962 
963         /* We've got a logo shown, change the scroll region to get
964          * the rest of the text down below the shutdown message */
965         InbvSetScrollRegion(0,
966                             VID_SHUTDOWN_MSG_TOP + ((PBITMAPINFOHEADER)Logo1)->biHeight + 32,
967                             SCREEN_WIDTH - 1,
968                             SCREEN_HEIGHT - 1);
969 #endif
970     }
971 
972 #ifdef REACTOS_FANCY_BOOT
973     InbvDisplayString("\r\"");
974     InbvDisplayString(GetFamousQuote());
975     InbvDisplayString("\"");
976 #endif
977 }
978 
979 VOID
980 NTAPI
981 DisplayShutdownText(VOID)
982 {
983     ULONG i;
984 
985     for (i = 0; i < 25; ++i) InbvDisplayString("\r\n");
986     InbvDisplayString("                       ");
987     InbvDisplayString("The system may be powered off now.\r\n");
988 
989 #ifdef REACTOS_FANCY_BOOT
990     for (i = 0; i < 3; ++i) InbvDisplayString("\r\n");
991     InbvDisplayString("\r\"");
992     InbvDisplayString(GetFamousQuote());
993     InbvDisplayString("\"");
994 #endif
995 }
996