xref: /reactos/subsystems/mvdm/ntvdm/console/video.c (revision d5b576b2)
1 /*
2  * COPYRIGHT:       GPL - See COPYING in the top level directory
3  * PROJECT:         ReactOS Virtual DOS Machine
4  * FILE:            subsystems/mvdm/ntvdm/hardware/video/console.c
5  * PURPOSE:         Console driver for the video subsystem
6  * PROGRAMMERS:     Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7  *                  Hermes Belusca-Maito (hermes.belusca@sfr.fr)
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 /** HACK!! **/
13 #if 0
14 
15 #include "ntvdm.h"
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 #include "emulator.h"
21 #include "svga.h"
22 
23 #include "console.h"
24 
25 #endif
26 /** HACK!! **/
27 
28 
29 /* PRIVATE VARIABLES **********************************************************/
30 
31 static CONSOLE_CURSOR_INFO         OrgConsoleCursorInfo;
32 static CONSOLE_SCREEN_BUFFER_INFO  OrgConsoleBufferInfo;
33 
34 
35 static HANDLE ScreenBufferHandle = NULL;
36 static PVOID  OldConsoleFramebuffer = NULL;
37 
38 
39 /*
40  * Text mode -- we always keep a valid text mode framebuffer
41  * even if we are in graphics mode. This is needed in order
42  * to keep a consistent VGA state. However, each time the VGA
43  * detaches from the console (and reattaches to it later on),
44  * this text mode framebuffer is recreated.
45  */
46 static HANDLE TextConsoleBuffer = NULL;
47 static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
48 static COORD  TextResolution = {0};
49 /// static PCHAR_CELL TextFramebuffer = NULL;
50 
51 /*
52  * Graphics mode
53  */
54 static HANDLE GraphicsConsoleBuffer = NULL;
55 /// static PVOID  GraphicsFramebuffer = NULL;
56 static HANDLE ConsoleMutex = NULL;
57 /* DoubleVision support */
58 static BOOLEAN DoubleWidth  = FALSE;
59 static BOOLEAN DoubleHeight = FALSE;
60 
61 
62 
63 /*
64  * Activate this line if you want to use the real
65  * RegisterConsoleVDM API of ReactOS/Windows.
66  */
67 // #define USE_REAL_REGISTERCONSOLEVDM
68 
69 static HANDLE StartEvent = NULL;
70 static HANDLE EndEvent   = NULL;
71 static HANDLE AnotherEvent = NULL;
72 
73 /* RegisterConsoleVDM EMULATION ***********************************************/
74 
75 #include <ntddvdeo.h>
76 
77 #ifdef USE_REAL_REGISTERCONSOLEVDM
78 
79 #define __RegisterConsoleVDM        RegisterConsoleVDM
80 #define __InvalidateConsoleDIBits   InvalidateConsoleDIBits
81 
82 #else
83 
84 /*
85  * This private buffer, per-console, is used by
86  * RegisterConsoleVDM and InvalidateConsoleDIBits.
87  */
88 static COORD VDMBufferSize  = {0};
89 static PCHAR_CELL VDMBuffer = NULL;
90 
91 static PCHAR_INFO CharBuff  = NULL; // This is a hack, which is unneeded
92                                     // for the real RegisterConsoleVDM and
93                                     // InvalidateConsoleDIBits
94 
95 BOOL
96 WINAPI
97 __RegisterConsoleVDM(IN DWORD dwRegisterFlags,
98                      IN HANDLE hStartHardwareEvent,
99                      IN HANDLE hEndHardwareEvent,
100                      IN HANDLE hErrorHardwareEvent,
101                      IN DWORD dwUnusedVar,
102                      OUT LPDWORD lpVideoStateLength,
103                      OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
104                      IN PVOID lpUnusedBuffer,
105                      IN DWORD dwUnusedBufferLength,
106                      IN COORD dwVDMBufferSize,
107                      OUT PVOID* lpVDMBuffer)
108 {
109     UNREFERENCED_PARAMETER(hErrorHardwareEvent);
110     UNREFERENCED_PARAMETER(dwUnusedVar);
111     UNREFERENCED_PARAMETER(lpVideoStateLength);
112     UNREFERENCED_PARAMETER(lpVideoState);
113     UNREFERENCED_PARAMETER(lpUnusedBuffer);
114     UNREFERENCED_PARAMETER(dwUnusedBufferLength);
115 
116     SetLastError(0);
117     DPRINT1("__RegisterConsoleVDM(%d)\n", dwRegisterFlags);
118 
119     if (lpVDMBuffer == NULL) return FALSE;
120 
121     if (dwRegisterFlags != 0)
122     {
123         // if (hStartHardwareEvent == NULL || hEndHardwareEvent == NULL) return FALSE;
124         if (VDMBuffer != NULL) return FALSE;
125 
126         VDMBufferSize = dwVDMBufferSize;
127 
128         /* HACK: Cache -- to be removed in the real implementation */
129         CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
130                                    HEAP_ZERO_MEMORY,
131                                    VDMBufferSize.X * VDMBufferSize.Y
132                                                    * sizeof(*CharBuff));
133         ASSERT(CharBuff);
134 
135         VDMBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
136                                     HEAP_ZERO_MEMORY,
137                                     VDMBufferSize.X * VDMBufferSize.Y
138                                                     * sizeof(*VDMBuffer));
139         *lpVDMBuffer = VDMBuffer;
140         return (VDMBuffer != NULL);
141     }
142     else
143     {
144         /* HACK: Cache -- to be removed in the real implementation */
145         if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
146         CharBuff = NULL;
147 
148         if (VDMBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, VDMBuffer);
149         VDMBuffer = NULL;
150 
151         VDMBufferSize.X = VDMBufferSize.Y = 0;
152 
153         return TRUE;
154     }
155 }
156 
157 BOOL
158 __InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
159                           IN PSMALL_RECT lpRect)
160 {
161     if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL))
162     {
163         /* HACK: Write the cached data to the console */
164 
165         COORD Origin = { lpRect->Left, lpRect->Top };
166         SHORT i, j;
167 
168         ASSERT(CharBuff);
169 
170         for (i = 0; i < VDMBufferSize.Y; i++)
171         {
172             for (j = 0; j < VDMBufferSize.X; j++)
173             {
174                 CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char;
175                 CharBuff[i * VDMBufferSize.X + j].Attributes     = VDMBuffer[i * VDMBufferSize.X + j].Attributes;
176             }
177         }
178 
179         WriteConsoleOutputA(hConsoleOutput,
180                             CharBuff,
181                             VDMBufferSize,
182                             Origin,
183                             lpRect);
184     }
185 
186     return InvalidateConsoleDIBits(hConsoleOutput, lpRect);
187 }
188 
189 #endif
190 
191 
192 /* PRIVATE FUNCTIONS **********************************************************/
193 
194 
195 /*********/
196 static VOID VgaUpdateTextCursor(VOID);
197 static inline DWORD VgaGetAddressSize(VOID);
198 /*********/
199 
200 
201 
202 
203 static VOID ResizeTextConsole(PCOORD Resolution, PSMALL_RECT WindowSize OPTIONAL)
204 {
205     BOOL Success;
206     SMALL_RECT ConRect;
207     SHORT oldWidth, oldHeight;
208 
209     /*
210      * Use this trick to effectively resize the console buffer and window,
211      * because:
212      * - SetConsoleScreenBufferSize fails if the new console screen buffer size
213      *   is smaller than the current console window size, and:
214      * - SetConsoleWindowInfo fails if the new console window size is larger
215      *   than the current console screen buffer size.
216      */
217 
218 
219     /* Retrieve the latest console information */
220     GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
221 
222     oldWidth  = ConsoleInfo.srWindow.Right  - ConsoleInfo.srWindow.Left + 1;
223     oldHeight = ConsoleInfo.srWindow.Bottom - ConsoleInfo.srWindow.Top  + 1;
224 
225     /*
226      * If the current console window is too large to hold the full contents
227      * of the new screen buffer, resize it first.
228      */
229     if (oldWidth > Resolution->X || oldHeight > Resolution->Y)
230     {
231         //
232         // NOTE: This is not a problem if we move the window back to (0,0)
233         // because when we resize the screen buffer, the window will move back
234         // to where the cursor is. Or, if the screen buffer is not resized,
235         // when we readjust again the window, we will move back to a correct
236         // position. This is what we wanted after all...
237         //
238 
239         ConRect.Left   = ConRect.Top = 0;
240         ConRect.Right  = ConRect.Left + min(oldWidth , Resolution->X) - 1;
241         ConRect.Bottom = ConRect.Top  + min(oldHeight, Resolution->Y) - 1;
242 
243         Success = SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
244         if (!Success) DPRINT1("(resize) SetConsoleWindowInfo(1) failed with error %d\n", GetLastError());
245     }
246 
247     /* Resize the screen buffer if needed */
248     if (Resolution->X != ConsoleInfo.dwSize.X || Resolution->Y != ConsoleInfo.dwSize.Y)
249     {
250         /*
251          * SetConsoleScreenBufferSize automatically takes into account the current
252          * cursor position when it computes starting which row it should copy text
253          * when resizing the sceenbuffer, and scrolls the console window such that
254          * the cursor is placed in it again. We therefore do not need to care about
255          * the cursor position and do the maths ourselves.
256          */
257         Success = SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
258         if (!Success) DPRINT1("(resize) SetConsoleScreenBufferSize failed with error %d\n", GetLastError());
259 
260         /*
261          * Setting a new screen buffer size can change other information,
262          * so update the saved console information.
263          */
264         GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
265     }
266 
267     if (!WindowSize)
268     {
269         ConRect.Left   = 0;
270         ConRect.Right  = ConRect.Left + Resolution->X - 1;
271         ConRect.Bottom = max(ConsoleInfo.dwCursorPosition.Y, Resolution->Y - 1);
272         ConRect.Top    = ConRect.Bottom - Resolution->Y + 1;
273 
274         // NOTE: We may take ConsoleInfo.dwMaximumWindowSize into account
275     }
276     else
277     {
278         ConRect.Left   = ConRect.Top = 0;
279         ConRect.Right  = ConRect.Left + WindowSize->Right  - WindowSize->Left;
280         ConRect.Bottom = ConRect.Top  + WindowSize->Bottom - WindowSize->Top ;
281     }
282 
283     Success = SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
284     if (!Success) DPRINT1("(resize) SetConsoleWindowInfo(2) failed with error %d\n", GetLastError());
285 
286     /* Update the saved console information */
287     GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
288 }
289 
290 static VOID UpdateCursorPosition(VOID)
291 {
292     /*
293      * Update the cursor position in the VGA registers.
294      */
295     WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X +
296                   ConsoleInfo.dwCursorPosition.X;
297 
298     VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG]  = LOBYTE(Offset);
299     VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset);
300 
301     VgaUpdateTextCursor();
302 }
303 
304 static BOOL AttachToConsoleInternal(PCOORD Resolution)
305 {
306     BOOL Success;
307     ULONG Length = 0;
308     PVIDEO_HARDWARE_STATE_HEADER State;
309 
310 #ifdef USE_REAL_REGISTERCONSOLEVDM
311     PCHAR_INFO CharBuff = NULL;
312 #endif
313     SHORT i, j;
314     DWORD AddressSize, ScanlineSize;
315     DWORD Address = 0;
316     DWORD CurrentAddr;
317     SMALL_RECT ConRect;
318     COORD Origin = { 0, 0 };
319 
320     ASSERT(TextFramebuffer == NULL);
321 
322     TextResolution = *Resolution;
323 
324     /*
325      * Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle);
326      * in the two following APIs:
327      * SrvRegisterConsoleVDM  (corresponding Win32 API: RegisterConsoleVDM)
328      * SrvVDMConsoleOperation (corresponding Win32 API: VDMConsoleOperation)
329      * to check whether the current process is a VDM process, and fails otherwise
330      * with the error 0xC0000022 (STATUS_ACCESS_DENIED).
331      *
332      * It is worth it to notice that also basesrv.dll does the same only for the
333      * BaseSrvIsFirstVDM API...
334      */
335 
336     /* Register with the console server */
337     Success =
338     __RegisterConsoleVDM(1,
339                          StartEvent,
340                          EndEvent,
341                          AnotherEvent, // NULL,
342                          0,
343                          &Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS"
344                          (PVOID*)&State, // NULL,
345                          NULL,
346                          0,
347                          TextResolution,
348                          (PVOID*)&TextFramebuffer);
349     if (!Success)
350     {
351         DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
352         EmulatorTerminate();
353         return FALSE;
354     }
355 
356 #ifdef USE_REAL_REGISTERCONSOLEVDM
357     CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
358                                HEAP_ZERO_MEMORY,
359                                TextResolution.X * TextResolution.Y
360                                                 * sizeof(*CharBuff));
361     ASSERT(CharBuff);
362 #endif
363 
364     /* Resize the console */
365     ResizeTextConsole(Resolution, NULL);
366 
367     /* Update the saved console information */
368     GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
369 
370     /*
371      * Copy console data into VGA memory
372      */
373 
374     /* Read the data from the console into the framebuffer... */
375     ConRect.Left   = ConRect.Top = 0;
376     ConRect.Right  = TextResolution.X;
377     ConRect.Bottom = TextResolution.Y;
378 
379     ReadConsoleOutputA(TextConsoleBuffer,
380                        CharBuff,
381                        TextResolution,
382                        Origin,
383                        &ConRect);
384 
385     /* ... and copy the framebuffer into the VGA memory */
386     AddressSize  = VgaGetAddressSize();
387     ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
388 
389     /* Loop through the scanlines */
390     for (i = 0; i < TextResolution.Y; i++)
391     {
392         /* Loop through the characters */
393         for (j = 0; j < TextResolution.X; j++)
394         {
395             CurrentAddr = LOWORD((Address + j) * AddressSize);
396 
397             /* Store the character in plane 0 */
398             VgaMemory[CurrentAddr * VGA_NUM_BANKS] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
399 
400             /* Store the attribute in plane 1 */
401             VgaMemory[CurrentAddr * VGA_NUM_BANKS + 1] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes;
402         }
403 
404         /* Move to the next scanline */
405         Address += ScanlineSize;
406     }
407 
408 #ifdef USE_REAL_REGISTERCONSOLEVDM
409     if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
410 #endif
411 
412     UpdateCursorPosition();
413 
414     return TRUE;
415 }
416 
417 static VOID DetachFromConsoleInternal(VOID)
418 {
419     ULONG dummyLength;
420     PVOID dummyPtr;
421     COORD dummySize = {0};
422 
423     /* Deregister with the console server */
424     __RegisterConsoleVDM(0,
425                          NULL,
426                          NULL,
427                          NULL,
428                          0,
429                          &dummyLength,
430                          &dummyPtr,
431                          NULL,
432                          0,
433                          dummySize,
434                          &dummyPtr);
435 
436     TextFramebuffer = NULL;
437 }
438 
439 static VOID SetActiveScreenBuffer(HANDLE ScreenBuffer)
440 {
441     ASSERT(ScreenBuffer);
442 
443     /* Set the active buffer and reattach the VDM UI to it */
444     SetConsoleActiveScreenBuffer(ScreenBuffer);
445     ConsoleReattach(ScreenBuffer);
446 }
447 
448 VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)
449 {
450     /*
451      * This function monitors and allows console resizings only if they are triggered by us.
452      * User-driven resizings via the console properties, or programmatical console resizings
453      * made by explicit calls to SetConsoleScreenBufferSize by external applications, are forbidden.
454      * In that case only a console window resize is done in case the size is reduced.
455      * This protection is enabled in CONSRV side when NTVDM registers as a VDM to CONSRV,
456      * but we also implement it there in case we are running in STANDALONE mode without
457      * CONSRV registration.
458      *
459      * The only potential problem we have is that, when this handler is called,
460      * the console is already resized. In case this corresponds to a forbidden resize,
461      * we resize the console back to its original size from inside the handler.
462      * This will trigger a recursive call to the handler, that should be detected.
463      */
464 
465     if (CurrResolution.X == ScreenEvent->dwSize.X &&
466         CurrResolution.Y == ScreenEvent->dwSize.Y)
467     {
468         /* Allowed resize, we are OK */
469         return;
470     }
471 
472     DPRINT1("ScreenEventHandler - Detected forbidden resize! Reset console screenbuffer size back to (X = %d ; Y = %d)\n", CurrResolution.X, CurrResolution.Y);
473 
474     // FIXME: If we're detaching, then stop monitoring for changes!!
475 
476     /* Restore the original console size */
477     ResizeTextConsole(&CurrResolution, NULL);
478 
479     /* Force refresh of all the screen */
480     NeedsUpdate = TRUE;
481     UpdateRectangle.Left = 0;
482     UpdateRectangle.Top  = 0;
483     UpdateRectangle.Right  = CurrResolution.X;
484     UpdateRectangle.Bottom = CurrResolution.Y;
485     VgaRefreshDisplay();
486 }
487 
488 BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
489 {
490     if (GraphicsConsoleBuffer == NULL) return FALSE;
491     if (Horizontal) *Horizontal = DoubleWidth;
492     if (Vertical)   *Vertical   = DoubleHeight;
493     return TRUE;
494 }
495 
496 BOOL VgaAttachToConsole(VOID)
497 {
498     if (TextResolution.X == 0 || TextResolution.Y == 0)
499         DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n");
500 
501     if (TextResolution.X == 0) TextResolution.X = 80;
502     if (TextResolution.Y == 0) TextResolution.Y = 25;
503 
504     // DetachFromConsoleInternal();
505 
506     /*
507      * AttachToConsoleInternal sets TextResolution
508      * to the new resolution and updates ConsoleInfo.
509      */
510     if (!AttachToConsoleInternal(&TextResolution))
511     {
512         DisplayMessage(L"An unexpected error occurred!\n");
513         EmulatorTerminate();
514         return FALSE;
515     }
516 
517     /* Restore the original screen buffer */
518     SetActiveScreenBuffer(ScreenBufferHandle);
519     ScreenBufferHandle = NULL;
520 
521     /* Restore the screen state */
522     if (ScreenMode == TEXT_MODE)
523     {
524         /* The text mode framebuffer was recreated */
525         ActiveFramebuffer = TextFramebuffer;
526     }
527     else
528     {
529         /* The graphics mode framebuffer is unchanged */
530         ActiveFramebuffer = OldConsoleFramebuffer;
531     }
532     OldConsoleFramebuffer = NULL;
533 
534     return TRUE;
535 }
536 
537 VOID VgaDetachFromConsole(VOID)
538 {
539     DetachFromConsoleInternal();
540 
541     /* Save the screen state */
542     if (ScreenMode == TEXT_MODE)
543         ScreenBufferHandle = TextConsoleBuffer;
544     else
545         ScreenBufferHandle = GraphicsConsoleBuffer;
546 
547     /* Reset the active framebuffer */
548     OldConsoleFramebuffer = ActiveFramebuffer;
549     ActiveFramebuffer = NULL;
550 
551     /* Restore the original console size */
552     ResizeTextConsole(&OrgConsoleBufferInfo.dwSize, &OrgConsoleBufferInfo.srWindow);
553 
554     /* Restore the original cursor shape */
555     SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo);
556 
557     // FIXME: Should we copy back the screen data to the screen buffer??
558     // WriteConsoleOutputA(...);
559 
560     // FIXME: Should we change cursor POSITION??
561     // VgaUpdateTextCursor();
562 
563     ///* Update the physical cursor */
564     //SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
565     //SetConsoleCursorPosition(TextConsoleBuffer, Position /*OrgConsoleBufferInfo.dwCursorPosition*/);
566 
567     /* Restore the old text-mode screen buffer */
568     SetActiveScreenBuffer(TextConsoleBuffer);
569 }
570 
571 
572 
573 
574 VOID
575 VgaConsoleUpdateTextCursor(BOOL CursorVisible,
576                            BYTE CursorStart,
577                            BYTE CursorEnd,
578                            BYTE TextSize,
579                            DWORD ScanlineSize,
580                            WORD Location)
581 {
582     COORD Position;
583     CONSOLE_CURSOR_INFO CursorInfo;
584 
585     if (CursorStart < CursorEnd)
586     {
587         /* Visible cursor */
588         CursorInfo.bVisible = CursorVisible;
589         CursorInfo.dwSize   = (100 * (CursorEnd - CursorStart)) / TextSize;
590     }
591     else
592     {
593         /* Hidden cursor */
594         CursorInfo.bVisible = FALSE;
595         CursorInfo.dwSize   = 1; // The size needs to be non-zero for SetConsoleCursorInfo to succeed.
596     }
597 
598     /* Find the coordinates of the new position */
599     Position.X = (SHORT)(Location % ScanlineSize);
600     Position.Y = (SHORT)(Location / ScanlineSize);
601 
602     DPRINT("VgaConsoleUpdateTextCursor: (X = %d ; Y = %d)\n", Position.X, Position.Y);
603 
604     /* Update the physical cursor */
605     SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
606     SetConsoleCursorPosition(TextConsoleBuffer, Position);
607 }
608 
609 BOOL
610 VgaConsoleCreateGraphicsScreen(// OUT PBYTE* GraphicsFramebuffer,
611                                IN PCOORD Resolution,
612                                IN HANDLE PaletteHandle)
613 {
614     DWORD i;
615     CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
616     BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
617     LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
618     LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
619 
620     LONG Width  = Resolution->X;
621     LONG Height = Resolution->Y;
622 
623     /* Use DoubleVision mode if the resolution is too small */
624     DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
625     if (DoubleWidth) Width *= 2;
626     DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
627     if (DoubleHeight) Height *= 2;
628 
629     /* Fill the bitmap info header */
630     RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
631     BitmapInfo->bmiHeader.biSize   = sizeof(BitmapInfo->bmiHeader);
632     BitmapInfo->bmiHeader.biWidth  = Width;
633     BitmapInfo->bmiHeader.biHeight = Height;
634     BitmapInfo->bmiHeader.biBitCount = 8;
635     BitmapInfo->bmiHeader.biPlanes   = 1;
636     BitmapInfo->bmiHeader.biCompression = BI_RGB;
637     BitmapInfo->bmiHeader.biSizeImage   = Width * Height /* * 1 == biBitCount / 8 */;
638 
639     /* Fill the palette data */
640     for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
641 
642     /* Fill the console graphics buffer info */
643     GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
644     GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
645     GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
646 
647     /* Create the buffer */
648     GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
649                                                       FILE_SHARE_READ | FILE_SHARE_WRITE,
650                                                       NULL,
651                                                       CONSOLE_GRAPHICS_BUFFER,
652                                                       &GraphicsBufferInfo);
653     if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
654 
655     /* Save the framebuffer address and mutex */
656     // *GraphicsFramebuffer = GraphicsBufferInfo.lpBitMap;
657     GraphicsFramebuffer = GraphicsBufferInfo.lpBitMap;
658     ConsoleMutex = GraphicsBufferInfo.hMutex;
659 
660     /* Clear the framebuffer */
661     // RtlZeroMemory(*GraphicsFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
662     RtlZeroMemory(GraphicsFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
663 
664     /* Set the graphics mode palette */
665     SetConsolePalette(GraphicsConsoleBuffer,
666                       PaletteHandle,
667                       SYSPAL_NOSTATIC256);
668 
669     /* Set the active buffer */
670     SetActiveScreenBuffer(GraphicsConsoleBuffer);
671 
672     return TRUE;
673 }
674 
675 VOID VgaConsoleDestroyGraphicsScreen(VOID)
676 {
677     /* Release the console framebuffer mutex */
678     ReleaseMutex(ConsoleMutex);
679 
680     /* Switch back to the default console text buffer */
681     // SetActiveScreenBuffer(TextConsoleBuffer);
682 
683     /* Cleanup the video data */
684     CloseHandle(ConsoleMutex);
685     ConsoleMutex = NULL;
686     // GraphicsFramebuffer = NULL;
687     CloseHandle(GraphicsConsoleBuffer);
688     GraphicsConsoleBuffer = NULL;
689 
690     // /* Reset the active framebuffer */
691     // ActiveFramebuffer = NULL;
692 
693     DoubleWidth  = FALSE;
694     DoubleHeight = FALSE;
695 }
696 
697 BOOL
698 VgaConsoleCreateTextScreen(// OUT PCHAR_CELL* TextFramebuffer,
699                            IN PCOORD Resolution,
700                            IN HANDLE PaletteHandle)
701 {
702     /* Switch to the text buffer */
703     // FIXME: Wouldn't it be preferrable to switch to it AFTER we reset everything??
704     SetActiveScreenBuffer(TextConsoleBuffer);
705 
706     /* Adjust the text framebuffer if we changed the resolution */
707     if (TextResolution.X != Resolution->X ||
708         TextResolution.Y != Resolution->Y)
709     {
710         DetachFromConsoleInternal();
711 
712         /*
713          * AttachToConsoleInternal sets TextResolution
714          * to the new resolution and updates ConsoleInfo.
715          */
716         if (!AttachToConsoleInternal(Resolution))
717         {
718             DisplayMessage(L"An unexpected error occurred!\n");
719             EmulatorTerminate();
720             return FALSE;
721         }
722     }
723     else
724     {
725         UpdateCursorPosition();
726     }
727 
728     /*
729      * Set the text mode palette.
730      *
731      * INFORMATION: This call should fail on Windows (and therefore
732      * we get the default palette and our external behaviour is
733      * just like Windows' one), but it should success on ReactOS
734      * (so that we get console palette changes even for text-mode
735      * screen buffers, which is a new feature on ReactOS).
736      */
737     SetConsolePalette(TextConsoleBuffer,
738                       PaletteHandle,
739                       SYSPAL_NOSTATIC256);
740 
741     return TRUE;
742 }
743 
744 VOID VgaConsoleDestroyTextScreen(VOID)
745 {
746 }
747 
748 
749 
750 VOID VgaConsoleRepaintScreen(PSMALL_RECT Rect)
751 {
752     HANDLE ConsoleBufferHandle = NULL;
753     SMALL_RECT UpdateRectangle = *Rect;
754 
755     /* Check if we are in text or graphics mode */
756     if (ScreenMode == GRAPHICS_MODE)
757     {
758         /* Graphics mode */
759         ConsoleBufferHandle = GraphicsConsoleBuffer;
760 
761         /* In DoubleVision mode, scale the update rectangle */
762         if (DoubleWidth)
763         {
764             UpdateRectangle.Left *= 2;
765             UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
766         }
767         if (DoubleHeight)
768         {
769             UpdateRectangle.Top *= 2;
770             UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
771         }
772     }
773     else
774     {
775         /* Text mode */
776         ConsoleBufferHandle = TextConsoleBuffer;
777     }
778 
779     /* Redraw the screen */
780     __InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
781 }
782 
783 BOOLEAN VgaConsoleInitialize(HANDLE TextHandle)
784 {
785     /*
786      * Initialize the console video by saving the default
787      * text-mode console output handle, if it is valid.
788      */
789     if (!IsConsoleHandle(TextHandle)) return FALSE;
790     TextConsoleBuffer = TextHandle;
791 
792     /* Save the original cursor and console screen buffer information */
793     if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
794         !GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
795     {
796         TextConsoleBuffer = NULL;
797         return FALSE;
798     }
799     ConsoleInfo = OrgConsoleBufferInfo;
800 
801     /* Switch to the text buffer, but do not enter into a text mode */
802     SetActiveScreenBuffer(TextConsoleBuffer);
803 
804     return TRUE;
805 }
806 
807 VOID VgaConsoleCleanup(VOID)
808 {
809     /* If the console video was not initialized, just return */
810     if (!TextConsoleBuffer)
811         return;
812 
813     VgaDetachFromConsole();
814 
815     // TODO: We need to initialize those events before using them!
816     CloseHandle(AnotherEvent);
817     CloseHandle(EndEvent);
818     CloseHandle(StartEvent);
819 }
820