75825d0aa23641a01be98f7e11e5c09e31f71483
[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 PVOID
216 NTAPI
217 INIT_FUNCTION
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 BOOLEAN
281 NTAPI
282 INIT_FUNCTION
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 VOID
361 NTAPI
362 INIT_FUNCTION
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 VOID
626 NTAPI
627 INIT_FUNCTION
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 VOID
742 NTAPI
743 INIT_FUNCTION
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 KPROCESSOR_MODE PreviousMode;
782
783 PAGED_CODE();
784
785 PreviousMode = ExGetPreviousMode();
786
787 /* We require the TCB privilege */
788 if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
789 return STATUS_PRIVILEGE_NOT_HELD;
790
791 /* Capture the string */
792 Status = ProbeAndCaptureUnicodeString(&CapturedString, PreviousMode, DisplayString);
793 if (!NT_SUCCESS(Status))
794 return Status;
795
796 /* Do not display the string if it is empty */
797 if (CapturedString.Length == 0 || CapturedString.Buffer == NULL)
798 {
799 Status = STATUS_SUCCESS;
800 goto Quit;
801 }
802
803 /*
804 * Convert the string since INBV understands only ANSI/OEM. Allocate the
805 * string buffer in non-paged pool because INBV passes it down to BOOTVID.
806 * We cannot perform the allocation using RtlUnicodeStringToOemString()
807 * since its allocator uses PagedPool.
808 */
809 RtlInitEmptyAnsiString((PANSI_STRING)&OemString, NULL,
810 RtlUnicodeStringToOemSize(&CapturedString));
811 OemString.Buffer = ExAllocatePoolWithTag(NonPagedPool,
812 OemString.MaximumLength,
813 TAG_OSTR);
814 if (OemString.Buffer == NULL)
815 {
816 Status = STATUS_NO_MEMORY;
817 goto Quit;
818 }
819 RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
820
821 /* Display the string */
822 InbvDisplayString(OemString.Buffer);
823
824 /* Free the string buffer */
825 ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
826
827 Status = STATUS_SUCCESS;
828
829 Quit:
830 /* Free the captured string */
831 ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
832
833 return Status;
834 }
835
836 #ifdef INBV_ROTBAR_IMPLEMENTED
837 static
838 VOID
839 NTAPI
840 InbvRotationThread(
841 _In_ PVOID Context)
842 {
843 ULONG X, Y, Index, Total;
844 LARGE_INTEGER Delay = {{0}};
845
846 InbvAcquireLock();
847 if (RotBarSelection == RB_SQUARE_CELLS)
848 {
849 Index = 0;
850 }
851 else
852 {
853 Index = 32;
854 }
855 X = ProgressBarLeft + 2;
856 Y = ProgressBarTop + 2;
857 InbvReleaseLock();
858
859 while (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
860 {
861 /* Wait for a bit */
862 KeDelayExecutionThread(KernelMode, FALSE, &Delay);
863
864 InbvAcquireLock();
865
866 /* Unknown unexpected command */
867 ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
868
869 if (PltRotBarStatus == RBS_STOP_ANIMATE)
870 {
871 /* Stop the thread */
872 InbvReleaseLock();
873 break;
874 }
875
876 if (RotBarSelection == RB_SQUARE_CELLS)
877 {
878 Delay.QuadPart = -800000; // 80 ms
879 Total = 18;
880 Index %= Total;
881
882 if (Index >= 3)
883 {
884 /* Fill previous bar position */
885 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, 0);
886 }
887 if (Index < Total - 1)
888 {
889 /* Draw the progress bar bit */
890 if (Index < 2)
891 {
892 /* Appearing from the left */
893 VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
894 }
895 else if (Index >= Total - 3)
896 {
897 /* Hiding to the right */
898 VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
899 }
900 else
901 {
902 VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
903 }
904 }
905 Index++;
906 }
907 else if (RotBarSelection == RB_PROGRESS_BAR)
908 {
909 Delay.QuadPart = -600000; // 60 ms
910 Total = 640;
911 Index %= Total;
912
913 /* Right part */
914 VidBufferToScreenBlt(RotLineBuffer, Index, 474, 640 - Index, 6, 640);
915 if (Index > 0)
916 {
917 /* Left part */
918 VidBufferToScreenBlt(RotLineBuffer + (640 - Index) / 2, 0, 474, Index - 2, 6, 640);
919 }
920 Index += 32;
921 }
922
923 InbvReleaseLock();
924 }
925
926 PsTerminateSystemThread(STATUS_SUCCESS);
927 }
928
929 VOID
930 NTAPI
931 INIT_FUNCTION
932 InbvRotBarInit(VOID)
933 {
934 PltRotBarStatus = RBS_FADEIN;
935 /* Perform other initialization if needed */
936 }
937 #endif
938
939 VOID
940 NTAPI
941 INIT_FUNCTION
942 DisplayBootBitmap(IN BOOLEAN TextMode)
943 {
944 PVOID Header = NULL, Footer = NULL, Screen = NULL;
945
946 #ifdef INBV_ROTBAR_IMPLEMENTED
947 UCHAR Buffer[24 * 9];
948 PVOID Bar = NULL, LineBmp = NULL;
949 ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
950 NTSTATUS Status;
951 HANDLE ThreadHandle = NULL;
952 #endif
953
954 #ifdef REACTOS_SKUS
955 PVOID Text = NULL;
956 #endif
957
958 #ifdef INBV_ROTBAR_IMPLEMENTED
959 /* Check if the animation thread has already been created */
960 if (RotBarThreadActive)
961 {
962 /* Yes, just reset the progress bar but keep the thread alive */
963 InbvAcquireLock();
964 RotBarSelection = RB_UNSPECIFIED;
965 InbvReleaseLock();
966 }
967 #endif
968
969 ShowProgressBar = FALSE;
970
971 /* Check if this is text mode */
972 if (TextMode)
973 {
974 /* Check the type of the OS: workstation or server */
975 if (SharedUserData->NtProductType == NtProductWinNt)
976 {
977 /* Workstation; set colors */
978 InbvSetTextColor(15);
979 InbvSolidColorFill(0, 0, 639, 479, 7);
980 InbvSolidColorFill(0, 421, 639, 479, 1);
981
982 /* Get resources */
983 Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
984 Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
985 }
986 else
987 {
988 /* Server; set colors */
989 InbvSetTextColor(14);
990 InbvSolidColorFill(0, 0, 639, 479, 6);
991 InbvSolidColorFill(0, 421, 639, 479, 1);
992
993 /* Get resources */
994 Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
995 Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
996 }
997
998 /* Set the scrolling region */
999 InbvSetScrollRegion(32, 80, 631, 400);
1000
1001 /* Make sure we have resources */
1002 if (Header && Footer)
1003 {
1004 /* BitBlt them on the screen */
1005 InbvBitBlt(Footer, 0, 419);
1006 InbvBitBlt(Header, 0, 0);
1007 }
1008 }
1009 else
1010 {
1011 /* Is the boot driver installed? */
1012 if (!InbvBootDriverInstalled) return;
1013
1014 /* Load the standard boot screen */
1015 Screen = InbvGetResourceAddress(IDB_BOOT_SCREEN);
1016
1017 #ifdef REACTOS_SKUS
1018 Text = NULL;
1019 if (SharedUserData->NtProductType == NtProductWinNt)
1020 {
1021 #ifdef INBV_ROTBAR_IMPLEMENTED
1022 /* Workstation product, use appropriate status bar color */
1023 Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1024 #endif
1025 }
1026 else
1027 {
1028 /* Display correct branding based on server suite */
1029 if (ExVerifySuite(StorageServer))
1030 {
1031 /* Storage Server Edition */
1032 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
1033 }
1034 else if (ExVerifySuite(ComputeServer))
1035 {
1036 /* Compute Cluster Edition */
1037 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
1038 }
1039 else
1040 {
1041 /* Normal edition */
1042 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
1043 }
1044
1045 #ifdef INBV_ROTBAR_IMPLEMENTED
1046 /* Server product, use appropriate status bar color */
1047 Bar = InbvGetResourceAddress(IDB_BAR_SERVER);
1048 #endif
1049 }
1050 #else
1051 /* Use default status bar */
1052 Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1053 #endif
1054
1055 /* Make sure we have a logo */
1056 if (Screen)
1057 {
1058 PBITMAPINFOHEADER BitmapInfoHeader;
1059 LPRGBQUAD Palette;
1060
1061 /*
1062 * Save the main image palette and replace it with black palette,
1063 * so that we can do fade-in effect later.
1064 */
1065 BitmapInfoHeader = (PBITMAPINFOHEADER)Screen;
1066 Palette = (LPRGBQUAD)((PUCHAR)Screen + BitmapInfoHeader->biSize);
1067 RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
1068 RtlZeroMemory(Palette, sizeof(MainPalette));
1069
1070 /* Blit the background */
1071 InbvBitBlt(Screen, 0, 0);
1072
1073 #ifdef INBV_ROTBAR_IMPLEMENTED
1074 /* Choose progress bar */
1075 TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
1076 #endif
1077
1078 /* Set progress bar coordinates and display it */
1079 InbvSetProgressBarCoordinates(259, 352);
1080
1081 #ifdef REACTOS_SKUS
1082 /* Check for non-workstation products */
1083 if (SharedUserData->NtProductType != NtProductWinNt)
1084 {
1085 /* Overwrite part of the logo for a server product */
1086 InbvScreenToBufferBlt(Buffer, 413, 237, 7, 7, 8);
1087 InbvSolidColorFill(418, 230, 454, 256, 0);
1088 InbvBufferToScreenBlt(Buffer, 413, 237, 7, 7, 8);
1089
1090 /* In setup mode, you haven't selected a SKU yet */
1091 if (ExpInTextModeSetup) Text = NULL;
1092 }
1093 #endif
1094 }
1095
1096 #ifdef REACTOS_SKUS
1097 /* Draw the SKU text if it exits */
1098 if (Text) InbvBitBlt(Text, 180, 121);
1099 #endif
1100
1101 #ifdef INBV_ROTBAR_IMPLEMENTED
1102 if (Bar)
1103 {
1104 /* Save previous screen pixels to buffer */
1105 InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
1106 /* Draw the progress bar bit */
1107 InbvBitBlt(Bar, 0, 0);
1108 /* Store it in global buffer */
1109 InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
1110 /* Restore screen pixels */
1111 InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
1112 }
1113
1114 /*
1115 * Add a rotating bottom horizontal bar when using a progress bar,
1116 * to show that ReactOS can be still alive when the bar does not
1117 * appear to progress.
1118 */
1119 if (TempRotBarSelection == RB_PROGRESS_BAR)
1120 {
1121 LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
1122 if (LineBmp)
1123 {
1124 /* Draw the line and store it in global buffer */
1125 InbvBitBlt(LineBmp, 0, 474);
1126 InbvScreenToBufferBlt(RotLineBuffer, 0, 474, 640, 6, 640);
1127 }
1128 }
1129 else
1130 {
1131 /* Hide the simple progress bar if not used */
1132 ShowProgressBar = FALSE;
1133 }
1134 #endif
1135
1136 /* Display the boot logo and fade it in */
1137 BootLogoFadeIn();
1138
1139 #ifdef INBV_ROTBAR_IMPLEMENTED
1140 if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
1141 {
1142 /* Start the animation thread */
1143 Status = PsCreateSystemThread(&ThreadHandle,
1144 0,
1145 NULL,
1146 NULL,
1147 NULL,
1148 InbvRotationThread,
1149 NULL);
1150 if (NT_SUCCESS(Status))
1151 {
1152 /* The thread has started, close the handle as we don't need it */
1153 RotBarThreadActive = TRUE;
1154 ObCloseHandle(ThreadHandle, KernelMode);
1155 }
1156 }
1157 #endif
1158
1159 /* Set filter which will draw text display if needed */
1160 InbvInstallDisplayStringFilter(DisplayFilter);
1161 }
1162
1163 #ifdef INBV_ROTBAR_IMPLEMENTED
1164 /* Do we have the animation thread? */
1165 if (RotBarThreadActive)
1166 {
1167 /* We do, initialize the progress bar */
1168 InbvAcquireLock();
1169 RotBarSelection = TempRotBarSelection;
1170 InbvRotBarInit();
1171 InbvReleaseLock();
1172
1173 // FIXME: This was added to allow animation start before the processor hangs
1174 if (TempRotBarSelection != RB_UNSPECIFIED)
1175 {
1176 LARGE_INTEGER Delay;
1177 Delay.QuadPart = -3000000; // 300 ms
1178 KeDelayExecutionThread(KernelMode, FALSE, &Delay);
1179 }
1180 }
1181 #endif
1182 }
1183
1184 VOID
1185 NTAPI
1186 INIT_FUNCTION
1187 DisplayFilter(PCHAR *String)
1188 {
1189 /* Windows hack to skip first dots */
1190 static BOOLEAN DotHack = TRUE;
1191
1192 /* If "." is given set *String to empty string */
1193 if(DotHack && strcmp(*String, ".") == 0)
1194 *String = "";
1195
1196 if(**String)
1197 {
1198 /* Remove the filter */
1199 InbvInstallDisplayStringFilter(NULL);
1200
1201 DotHack = FALSE;
1202
1203 /* Draw text screen */
1204 DisplayBootBitmap(TRUE);
1205 }
1206 }
1207
1208 VOID
1209 NTAPI
1210 INIT_FUNCTION
1211 FinalizeBootLogo(VOID)
1212 {
1213 /* Acquire lock and check the display state */
1214 InbvAcquireLock();
1215 if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
1216 {
1217 /* Clear the screen */
1218 VidSolidColorFill(0, 0, 639, 479, 0);
1219 }
1220
1221 /* Reset progress bar and lock */
1222 #ifdef INBV_ROTBAR_IMPLEMENTED
1223 PltRotBarStatus = RBS_STOP_ANIMATE;
1224 RotBarThreadActive = FALSE;
1225 #endif
1226 InbvReleaseLock();
1227 }