xref: /reactos/subsystems/mvdm/ntvdm/console/video.c (revision 97168148)
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
__RegisterConsoleVDM(IN DWORD dwRegisterFlags,IN HANDLE hStartHardwareEvent,IN HANDLE hEndHardwareEvent,IN HANDLE hErrorHardwareEvent,IN DWORD dwUnusedVar,OUT LPDWORD lpVideoStateLength,OUT PVOID * lpVideoState,IN PVOID lpUnusedBuffer,IN DWORD dwUnusedBufferLength,IN COORD dwVDMBufferSize,OUT PVOID * lpVDMBuffer)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
__InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,IN PSMALL_RECT lpRect)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 
ResizeTextConsole(PCOORD Resolution,PSMALL_RECT WindowSize OPTIONAL)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 
UpdateCursorPosition(VOID)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 
AttachToConsoleInternal(PCOORD Resolution)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 
DetachFromConsoleInternal(VOID)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 
SetActiveScreenBuffer(HANDLE ScreenBuffer)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 
ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)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 
VgaGetDoubleVisionState(PBOOLEAN Horizontal,PBOOLEAN Vertical)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 
VgaAttachToConsole(VOID)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 
VgaDetachFromConsole(VOID)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
VgaConsoleUpdateTextCursor(BOOL CursorVisible,BYTE CursorStart,BYTE CursorEnd,BYTE TextSize,DWORD ScanlineSize,WORD Location)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
VgaConsoleCreateGraphicsScreen(IN PCOORD Resolution,IN HANDLE PaletteHandle)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 
VgaConsoleDestroyGraphicsScreen(VOID)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
VgaConsoleCreateTextScreen(IN PCOORD Resolution,IN HANDLE PaletteHandle)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 
VgaConsoleDestroyTextScreen(VOID)744 VOID VgaConsoleDestroyTextScreen(VOID)
745 {
746 }
747 
748 
749 
VgaConsoleRepaintScreen(PSMALL_RECT Rect)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 
VgaConsoleInitialize(HANDLE TextHandle)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 
VgaConsoleCleanup(VOID)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