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