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