mirror of
https://github.com/reactos/reactos.git
synced 2024-08-28 22:28:36 +00:00
820 lines
26 KiB
C
820 lines
26 KiB
C
/*
|
|
* COPYRIGHT: GPL - See COPYING in the top level directory
|
|
* PROJECT: ReactOS Virtual DOS Machine
|
|
* FILE: subsystems/mvdm/ntvdm/hardware/video/console.c
|
|
* PURPOSE: Console driver for the video subsystem
|
|
* PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
* Hermes Belusca-Maito (hermes.belusca@sfr.fr)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
/** HACK!! **/
|
|
#if 0
|
|
|
|
#include "ntvdm.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include "emulator.h"
|
|
#include "svga.h"
|
|
|
|
#include "console.h"
|
|
|
|
#endif
|
|
/** HACK!! **/
|
|
|
|
|
|
/* PRIVATE VARIABLES **********************************************************/
|
|
|
|
static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo;
|
|
static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo;
|
|
|
|
|
|
static HANDLE ScreenBufferHandle = NULL;
|
|
static PVOID OldConsoleFramebuffer = NULL;
|
|
|
|
|
|
/*
|
|
* Text mode -- we always keep a valid text mode framebuffer
|
|
* even if we are in graphics mode. This is needed in order
|
|
* to keep a consistent VGA state. However, each time the VGA
|
|
* detaches from the console (and reattaches to it later on),
|
|
* this text mode framebuffer is recreated.
|
|
*/
|
|
static HANDLE TextConsoleBuffer = NULL;
|
|
static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
|
|
static COORD TextResolution = {0};
|
|
/// static PCHAR_CELL TextFramebuffer = NULL;
|
|
|
|
/*
|
|
* Graphics mode
|
|
*/
|
|
static HANDLE GraphicsConsoleBuffer = NULL;
|
|
/// static PVOID GraphicsFramebuffer = NULL;
|
|
static HANDLE ConsoleMutex = NULL;
|
|
/* DoubleVision support */
|
|
static BOOLEAN DoubleWidth = FALSE;
|
|
static BOOLEAN DoubleHeight = FALSE;
|
|
|
|
|
|
|
|
/*
|
|
* Activate this line if you want to use the real
|
|
* RegisterConsoleVDM API of ReactOS/Windows.
|
|
*/
|
|
// #define USE_REAL_REGISTERCONSOLEVDM
|
|
|
|
static HANDLE StartEvent = NULL;
|
|
static HANDLE EndEvent = NULL;
|
|
static HANDLE AnotherEvent = NULL;
|
|
|
|
/* RegisterConsoleVDM EMULATION ***********************************************/
|
|
|
|
#include <ntddvdeo.h>
|
|
|
|
#ifdef USE_REAL_REGISTERCONSOLEVDM
|
|
|
|
#define __RegisterConsoleVDM RegisterConsoleVDM
|
|
#define __InvalidateConsoleDIBits InvalidateConsoleDIBits
|
|
|
|
#else
|
|
|
|
/*
|
|
* This private buffer, per-console, is used by
|
|
* RegisterConsoleVDM and InvalidateConsoleDIBits.
|
|
*/
|
|
static COORD VDMBufferSize = {0};
|
|
static PCHAR_CELL VDMBuffer = NULL;
|
|
|
|
static PCHAR_INFO CharBuff = NULL; // This is a hack, which is unneeded
|
|
// for the real RegisterConsoleVDM and
|
|
// InvalidateConsoleDIBits
|
|
|
|
BOOL
|
|
WINAPI
|
|
__RegisterConsoleVDM(IN DWORD dwRegisterFlags,
|
|
IN HANDLE hStartHardwareEvent,
|
|
IN HANDLE hEndHardwareEvent,
|
|
IN HANDLE hErrorHardwareEvent,
|
|
IN DWORD dwUnusedVar,
|
|
OUT LPDWORD lpVideoStateLength,
|
|
OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER*
|
|
IN PVOID lpUnusedBuffer,
|
|
IN DWORD dwUnusedBufferLength,
|
|
IN COORD dwVDMBufferSize,
|
|
OUT PVOID* lpVDMBuffer)
|
|
{
|
|
UNREFERENCED_PARAMETER(hErrorHardwareEvent);
|
|
UNREFERENCED_PARAMETER(dwUnusedVar);
|
|
UNREFERENCED_PARAMETER(lpVideoStateLength);
|
|
UNREFERENCED_PARAMETER(lpVideoState);
|
|
UNREFERENCED_PARAMETER(lpUnusedBuffer);
|
|
UNREFERENCED_PARAMETER(dwUnusedBufferLength);
|
|
|
|
SetLastError(0);
|
|
DPRINT1("__RegisterConsoleVDM(%d)\n", dwRegisterFlags);
|
|
|
|
if (lpVDMBuffer == NULL) return FALSE;
|
|
|
|
if (dwRegisterFlags != 0)
|
|
{
|
|
// if (hStartHardwareEvent == NULL || hEndHardwareEvent == NULL) return FALSE;
|
|
if (VDMBuffer != NULL) return FALSE;
|
|
|
|
VDMBufferSize = dwVDMBufferSize;
|
|
|
|
/* HACK: Cache -- to be removed in the real implementation */
|
|
CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
VDMBufferSize.X * VDMBufferSize.Y
|
|
* sizeof(*CharBuff));
|
|
ASSERT(CharBuff);
|
|
|
|
VDMBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
VDMBufferSize.X * VDMBufferSize.Y
|
|
* sizeof(*VDMBuffer));
|
|
*lpVDMBuffer = VDMBuffer;
|
|
return (VDMBuffer != NULL);
|
|
}
|
|
else
|
|
{
|
|
/* HACK: Cache -- to be removed in the real implementation */
|
|
if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
|
|
CharBuff = NULL;
|
|
|
|
if (VDMBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, VDMBuffer);
|
|
VDMBuffer = NULL;
|
|
|
|
VDMBufferSize.X = VDMBufferSize.Y = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
__InvalidateConsoleDIBits(IN HANDLE hConsoleOutput,
|
|
IN PSMALL_RECT lpRect)
|
|
{
|
|
if ((hConsoleOutput == TextConsoleBuffer) && (VDMBuffer != NULL))
|
|
{
|
|
/* HACK: Write the cached data to the console */
|
|
|
|
COORD Origin = { lpRect->Left, lpRect->Top };
|
|
SHORT i, j;
|
|
|
|
ASSERT(CharBuff);
|
|
|
|
for (i = 0; i < VDMBufferSize.Y; i++)
|
|
{
|
|
for (j = 0; j < VDMBufferSize.X; j++)
|
|
{
|
|
CharBuff[i * VDMBufferSize.X + j].Char.AsciiChar = VDMBuffer[i * VDMBufferSize.X + j].Char;
|
|
CharBuff[i * VDMBufferSize.X + j].Attributes = VDMBuffer[i * VDMBufferSize.X + j].Attributes;
|
|
}
|
|
}
|
|
|
|
WriteConsoleOutputA(hConsoleOutput,
|
|
CharBuff,
|
|
VDMBufferSize,
|
|
Origin,
|
|
lpRect);
|
|
}
|
|
|
|
return InvalidateConsoleDIBits(hConsoleOutput, lpRect);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/* PRIVATE FUNCTIONS **********************************************************/
|
|
|
|
|
|
/*********/
|
|
static VOID VgaUpdateTextCursor(VOID);
|
|
static inline DWORD VgaGetAddressSize(VOID);
|
|
/*********/
|
|
|
|
|
|
|
|
|
|
static VOID ResizeTextConsole(PCOORD Resolution, PSMALL_RECT WindowSize OPTIONAL)
|
|
{
|
|
BOOL Success;
|
|
SMALL_RECT ConRect;
|
|
SHORT oldWidth, oldHeight;
|
|
|
|
/*
|
|
* Use this trick to effectively resize the console buffer and window,
|
|
* because:
|
|
* - SetConsoleScreenBufferSize fails if the new console screen buffer size
|
|
* is smaller than the current console window size, and:
|
|
* - SetConsoleWindowInfo fails if the new console window size is larger
|
|
* than the current console screen buffer size.
|
|
*/
|
|
|
|
|
|
/* Retrieve the latest console information */
|
|
GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
|
|
|
|
oldWidth = ConsoleInfo.srWindow.Right - ConsoleInfo.srWindow.Left + 1;
|
|
oldHeight = ConsoleInfo.srWindow.Bottom - ConsoleInfo.srWindow.Top + 1;
|
|
|
|
/*
|
|
* If the current console window is too large to hold the full contents
|
|
* of the new screen buffer, resize it first.
|
|
*/
|
|
if (oldWidth > Resolution->X || oldHeight > Resolution->Y)
|
|
{
|
|
//
|
|
// NOTE: This is not a problem if we move the window back to (0,0)
|
|
// because when we resize the screen buffer, the window will move back
|
|
// to where the cursor is. Or, if the screen buffer is not resized,
|
|
// when we readjust again the window, we will move back to a correct
|
|
// position. This is what we wanted after all...
|
|
//
|
|
|
|
ConRect.Left = ConRect.Top = 0;
|
|
ConRect.Right = ConRect.Left + min(oldWidth , Resolution->X) - 1;
|
|
ConRect.Bottom = ConRect.Top + min(oldHeight, Resolution->Y) - 1;
|
|
|
|
Success = SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
|
|
if (!Success) DPRINT1("(resize) SetConsoleWindowInfo(1) failed with error %d\n", GetLastError());
|
|
}
|
|
|
|
/* Resize the screen buffer if needed */
|
|
if (Resolution->X != ConsoleInfo.dwSize.X || Resolution->Y != ConsoleInfo.dwSize.Y)
|
|
{
|
|
/*
|
|
* SetConsoleScreenBufferSize automatically takes into account the current
|
|
* cursor position when it computes starting which row it should copy text
|
|
* when resizing the sceenbuffer, and scrolls the console window such that
|
|
* the cursor is placed in it again. We therefore do not need to care about
|
|
* the cursor position and do the maths ourselves.
|
|
*/
|
|
Success = SetConsoleScreenBufferSize(TextConsoleBuffer, *Resolution);
|
|
if (!Success) DPRINT1("(resize) SetConsoleScreenBufferSize failed with error %d\n", GetLastError());
|
|
|
|
/*
|
|
* Setting a new screen buffer size can change other information,
|
|
* so update the saved console information.
|
|
*/
|
|
GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
|
|
}
|
|
|
|
if (!WindowSize)
|
|
{
|
|
ConRect.Left = 0;
|
|
ConRect.Right = ConRect.Left + Resolution->X - 1;
|
|
ConRect.Bottom = max(ConsoleInfo.dwCursorPosition.Y, Resolution->Y - 1);
|
|
ConRect.Top = ConRect.Bottom - Resolution->Y + 1;
|
|
|
|
// NOTE: We may take ConsoleInfo.dwMaximumWindowSize into account
|
|
}
|
|
else
|
|
{
|
|
ConRect.Left = ConRect.Top = 0;
|
|
ConRect.Right = ConRect.Left + WindowSize->Right - WindowSize->Left;
|
|
ConRect.Bottom = ConRect.Top + WindowSize->Bottom - WindowSize->Top ;
|
|
}
|
|
|
|
Success = SetConsoleWindowInfo(TextConsoleBuffer, TRUE, &ConRect);
|
|
if (!Success) DPRINT1("(resize) SetConsoleWindowInfo(2) failed with error %d\n", GetLastError());
|
|
|
|
/* Update the saved console information */
|
|
GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
|
|
}
|
|
|
|
static VOID UpdateCursorPosition(VOID)
|
|
{
|
|
/*
|
|
* Update the cursor position in the VGA registers.
|
|
*/
|
|
WORD Offset = ConsoleInfo.dwCursorPosition.Y * TextResolution.X +
|
|
ConsoleInfo.dwCursorPosition.X;
|
|
|
|
VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_LOW_REG] = LOBYTE(Offset);
|
|
VgaCrtcRegisters[VGA_CRTC_CURSOR_LOC_HIGH_REG] = HIBYTE(Offset);
|
|
|
|
VgaUpdateTextCursor();
|
|
}
|
|
|
|
static BOOL AttachToConsoleInternal(PCOORD Resolution)
|
|
{
|
|
BOOL Success;
|
|
ULONG Length = 0;
|
|
PVIDEO_HARDWARE_STATE_HEADER State;
|
|
|
|
#ifdef USE_REAL_REGISTERCONSOLEVDM
|
|
PCHAR_INFO CharBuff = NULL;
|
|
#endif
|
|
SHORT i, j;
|
|
DWORD AddressSize, ScanlineSize;
|
|
DWORD Address = 0;
|
|
DWORD CurrentAddr;
|
|
SMALL_RECT ConRect;
|
|
COORD Origin = { 0, 0 };
|
|
|
|
ASSERT(TextFramebuffer == NULL);
|
|
|
|
TextResolution = *Resolution;
|
|
|
|
/*
|
|
* Windows 2k3 winsrv.dll calls NtVdmControl(VdmQueryVdmProcess == 14, &ConsoleHandle);
|
|
* in the two following APIs:
|
|
* SrvRegisterConsoleVDM (corresponding Win32 API: RegisterConsoleVDM)
|
|
* SrvVDMConsoleOperation (corresponding Win32 API: VDMConsoleOperation)
|
|
* to check whether the current process is a VDM process, and fails otherwise
|
|
* with the error 0xC0000022 (STATUS_ACCESS_DENIED).
|
|
*
|
|
* It is worth it to notice that also basesrv.dll does the same only for the
|
|
* BaseSrvIsFirstVDM API...
|
|
*/
|
|
|
|
/* Register with the console server */
|
|
Success =
|
|
__RegisterConsoleVDM(1,
|
|
StartEvent,
|
|
EndEvent,
|
|
AnotherEvent, // NULL,
|
|
0,
|
|
&Length, // NULL, <-- putting this (and null in the next var) makes the API returning error 12 "ERROR_INVALID_ACCESS"
|
|
(PVOID*)&State, // NULL,
|
|
NULL,
|
|
0,
|
|
TextResolution,
|
|
(PVOID*)&TextFramebuffer);
|
|
if (!Success)
|
|
{
|
|
DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
|
|
EmulatorTerminate();
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef USE_REAL_REGISTERCONSOLEVDM
|
|
CharBuff = RtlAllocateHeap(RtlGetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
TextResolution.X * TextResolution.Y
|
|
* sizeof(*CharBuff));
|
|
ASSERT(CharBuff);
|
|
#endif
|
|
|
|
/* Resize the console */
|
|
ResizeTextConsole(Resolution, NULL);
|
|
|
|
/* Update the saved console information */
|
|
GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
|
|
|
|
/*
|
|
* Copy console data into VGA memory
|
|
*/
|
|
|
|
/* Read the data from the console into the framebuffer... */
|
|
ConRect.Left = ConRect.Top = 0;
|
|
ConRect.Right = TextResolution.X;
|
|
ConRect.Bottom = TextResolution.Y;
|
|
|
|
ReadConsoleOutputA(TextConsoleBuffer,
|
|
CharBuff,
|
|
TextResolution,
|
|
Origin,
|
|
&ConRect);
|
|
|
|
/* ... and copy the framebuffer into the VGA memory */
|
|
AddressSize = VgaGetAddressSize();
|
|
ScanlineSize = (DWORD)VgaCrtcRegisters[VGA_CRTC_OFFSET_REG] * 2;
|
|
|
|
/* Loop through the scanlines */
|
|
for (i = 0; i < TextResolution.Y; i++)
|
|
{
|
|
/* Loop through the characters */
|
|
for (j = 0; j < TextResolution.X; j++)
|
|
{
|
|
CurrentAddr = LOWORD((Address + j) * AddressSize);
|
|
|
|
/* Store the character in plane 0 */
|
|
VgaMemory[CurrentAddr * VGA_NUM_BANKS] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
|
|
|
|
/* Store the attribute in plane 1 */
|
|
VgaMemory[CurrentAddr * VGA_NUM_BANKS + 1] = (BYTE)CharBuff[i * TextResolution.X + j].Attributes;
|
|
}
|
|
|
|
/* Move to the next scanline */
|
|
Address += ScanlineSize;
|
|
}
|
|
|
|
#ifdef USE_REAL_REGISTERCONSOLEVDM
|
|
if (CharBuff) RtlFreeHeap(RtlGetProcessHeap(), 0, CharBuff);
|
|
#endif
|
|
|
|
UpdateCursorPosition();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static VOID DetachFromConsoleInternal(VOID)
|
|
{
|
|
ULONG dummyLength;
|
|
PVOID dummyPtr;
|
|
COORD dummySize = {0};
|
|
|
|
/* Deregister with the console server */
|
|
__RegisterConsoleVDM(0,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&dummyLength,
|
|
&dummyPtr,
|
|
NULL,
|
|
0,
|
|
dummySize,
|
|
&dummyPtr);
|
|
|
|
TextFramebuffer = NULL;
|
|
}
|
|
|
|
static VOID SetActiveScreenBuffer(HANDLE ScreenBuffer)
|
|
{
|
|
ASSERT(ScreenBuffer);
|
|
|
|
/* Set the active buffer and reattach the VDM UI to it */
|
|
SetConsoleActiveScreenBuffer(ScreenBuffer);
|
|
ConsoleReattach(ScreenBuffer);
|
|
}
|
|
|
|
VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent)
|
|
{
|
|
/*
|
|
* This function monitors and allows console resizings only if they are triggered by us.
|
|
* User-driven resizings via the console properties, or programmatical console resizings
|
|
* made by explicit calls to SetConsoleScreenBufferSize by external applications, are forbidden.
|
|
* In that case only a console window resize is done in case the size is reduced.
|
|
* This protection is enabled in CONSRV side when NTVDM registers as a VDM to CONSRV,
|
|
* but we also implement it there in case we are running in STANDALONE mode without
|
|
* CONSRV registration.
|
|
*
|
|
* The only potential problem we have is that, when this handler is called,
|
|
* the console is already resized. In case this corresponds to a forbidden resize,
|
|
* we resize the console back to its original size from inside the handler.
|
|
* This will trigger a recursive call to the handler, that should be detected.
|
|
*/
|
|
|
|
if (CurrResolution.X == ScreenEvent->dwSize.X &&
|
|
CurrResolution.Y == ScreenEvent->dwSize.Y)
|
|
{
|
|
/* Allowed resize, we are OK */
|
|
return;
|
|
}
|
|
|
|
DPRINT1("ScreenEventHandler - Detected forbidden resize! Reset console screenbuffer size back to (X = %d ; Y = %d)\n", CurrResolution.X, CurrResolution.Y);
|
|
|
|
// FIXME: If we're detaching, then stop monitoring for changes!!
|
|
|
|
/* Restore the original console size */
|
|
ResizeTextConsole(&CurrResolution, NULL);
|
|
|
|
/* Force refresh of all the screen */
|
|
NeedsUpdate = TRUE;
|
|
UpdateRectangle.Left = 0;
|
|
UpdateRectangle.Top = 0;
|
|
UpdateRectangle.Right = CurrResolution.X;
|
|
UpdateRectangle.Bottom = CurrResolution.Y;
|
|
VgaRefreshDisplay();
|
|
}
|
|
|
|
BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
|
|
{
|
|
if (GraphicsConsoleBuffer == NULL) return FALSE;
|
|
if (Horizontal) *Horizontal = DoubleWidth;
|
|
if (Vertical) *Vertical = DoubleHeight;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL VgaAttachToConsole(VOID)
|
|
{
|
|
if (TextResolution.X == 0 || TextResolution.Y == 0)
|
|
DPRINT1("VgaAttachToConsole -- TextResolution uninitialized\n");
|
|
|
|
if (TextResolution.X == 0) TextResolution.X = 80;
|
|
if (TextResolution.Y == 0) TextResolution.Y = 25;
|
|
|
|
// DetachFromConsoleInternal();
|
|
|
|
/*
|
|
* AttachToConsoleInternal sets TextResolution
|
|
* to the new resolution and updates ConsoleInfo.
|
|
*/
|
|
if (!AttachToConsoleInternal(&TextResolution))
|
|
{
|
|
DisplayMessage(L"An unexpected error occurred!\n");
|
|
EmulatorTerminate();
|
|
return FALSE;
|
|
}
|
|
|
|
/* Restore the original screen buffer */
|
|
SetActiveScreenBuffer(ScreenBufferHandle);
|
|
ScreenBufferHandle = NULL;
|
|
|
|
/* Restore the screen state */
|
|
if (ScreenMode == TEXT_MODE)
|
|
{
|
|
/* The text mode framebuffer was recreated */
|
|
ActiveFramebuffer = TextFramebuffer;
|
|
}
|
|
else
|
|
{
|
|
/* The graphics mode framebuffer is unchanged */
|
|
ActiveFramebuffer = OldConsoleFramebuffer;
|
|
}
|
|
OldConsoleFramebuffer = NULL;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID VgaDetachFromConsole(VOID)
|
|
{
|
|
DetachFromConsoleInternal();
|
|
|
|
/* Save the screen state */
|
|
if (ScreenMode == TEXT_MODE)
|
|
ScreenBufferHandle = TextConsoleBuffer;
|
|
else
|
|
ScreenBufferHandle = GraphicsConsoleBuffer;
|
|
|
|
/* Reset the active framebuffer */
|
|
OldConsoleFramebuffer = ActiveFramebuffer;
|
|
ActiveFramebuffer = NULL;
|
|
|
|
/* Restore the original console size */
|
|
ResizeTextConsole(&OrgConsoleBufferInfo.dwSize, &OrgConsoleBufferInfo.srWindow);
|
|
|
|
/* Restore the original cursor shape */
|
|
SetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo);
|
|
|
|
// FIXME: Should we copy back the screen data to the screen buffer??
|
|
// WriteConsoleOutputA(...);
|
|
|
|
// FIXME: Should we change cursor POSITION??
|
|
// VgaUpdateTextCursor();
|
|
|
|
///* Update the physical cursor */
|
|
//SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
|
|
//SetConsoleCursorPosition(TextConsoleBuffer, Position /*OrgConsoleBufferInfo.dwCursorPosition*/);
|
|
|
|
/* Restore the old text-mode screen buffer */
|
|
SetActiveScreenBuffer(TextConsoleBuffer);
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
VgaConsoleUpdateTextCursor(BOOL CursorVisible,
|
|
BYTE CursorStart,
|
|
BYTE CursorEnd,
|
|
BYTE TextSize,
|
|
DWORD ScanlineSize,
|
|
WORD Location)
|
|
{
|
|
COORD Position;
|
|
CONSOLE_CURSOR_INFO CursorInfo;
|
|
|
|
if (CursorStart < CursorEnd)
|
|
{
|
|
/* Visible cursor */
|
|
CursorInfo.bVisible = CursorVisible;
|
|
CursorInfo.dwSize = (100 * (CursorEnd - CursorStart)) / TextSize;
|
|
}
|
|
else
|
|
{
|
|
/* Hidden cursor */
|
|
CursorInfo.bVisible = FALSE;
|
|
CursorInfo.dwSize = 1; // The size needs to be non-zero for SetConsoleCursorInfo to succeed.
|
|
}
|
|
|
|
/* Find the coordinates of the new position */
|
|
Position.X = (SHORT)(Location % ScanlineSize);
|
|
Position.Y = (SHORT)(Location / ScanlineSize);
|
|
|
|
DPRINT("VgaConsoleUpdateTextCursor: (X = %d ; Y = %d)\n", Position.X, Position.Y);
|
|
|
|
/* Update the physical cursor */
|
|
SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
|
|
SetConsoleCursorPosition(TextConsoleBuffer, Position);
|
|
}
|
|
|
|
BOOL
|
|
VgaConsoleCreateGraphicsScreen(// OUT PBYTE* GraphicsFramebuffer,
|
|
IN PCOORD Resolution,
|
|
IN HANDLE PaletteHandle)
|
|
{
|
|
DWORD i;
|
|
CONSOLE_GRAPHICS_BUFFER_INFO GraphicsBufferInfo;
|
|
BYTE BitmapInfoBuffer[VGA_BITMAP_INFO_SIZE];
|
|
LPBITMAPINFO BitmapInfo = (LPBITMAPINFO)BitmapInfoBuffer;
|
|
LPWORD PaletteIndex = (LPWORD)(BitmapInfo->bmiColors);
|
|
|
|
LONG Width = Resolution->X;
|
|
LONG Height = Resolution->Y;
|
|
|
|
/* Use DoubleVision mode if the resolution is too small */
|
|
DoubleWidth = (Width < VGA_MINIMUM_WIDTH);
|
|
if (DoubleWidth) Width *= 2;
|
|
DoubleHeight = (Height < VGA_MINIMUM_HEIGHT);
|
|
if (DoubleHeight) Height *= 2;
|
|
|
|
/* Fill the bitmap info header */
|
|
RtlZeroMemory(&BitmapInfo->bmiHeader, sizeof(BitmapInfo->bmiHeader));
|
|
BitmapInfo->bmiHeader.biSize = sizeof(BitmapInfo->bmiHeader);
|
|
BitmapInfo->bmiHeader.biWidth = Width;
|
|
BitmapInfo->bmiHeader.biHeight = Height;
|
|
BitmapInfo->bmiHeader.biBitCount = 8;
|
|
BitmapInfo->bmiHeader.biPlanes = 1;
|
|
BitmapInfo->bmiHeader.biCompression = BI_RGB;
|
|
BitmapInfo->bmiHeader.biSizeImage = Width * Height /* * 1 == biBitCount / 8 */;
|
|
|
|
/* Fill the palette data */
|
|
for (i = 0; i < (VGA_PALETTE_SIZE / 3); i++) PaletteIndex[i] = (WORD)i;
|
|
|
|
/* Fill the console graphics buffer info */
|
|
GraphicsBufferInfo.dwBitMapInfoLength = VGA_BITMAP_INFO_SIZE;
|
|
GraphicsBufferInfo.lpBitMapInfo = BitmapInfo;
|
|
GraphicsBufferInfo.dwUsage = DIB_PAL_COLORS;
|
|
|
|
/* Create the buffer */
|
|
GraphicsConsoleBuffer = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
CONSOLE_GRAPHICS_BUFFER,
|
|
&GraphicsBufferInfo);
|
|
if (GraphicsConsoleBuffer == INVALID_HANDLE_VALUE) return FALSE;
|
|
|
|
/* Save the framebuffer address and mutex */
|
|
// *GraphicsFramebuffer = GraphicsBufferInfo.lpBitMap;
|
|
GraphicsFramebuffer = GraphicsBufferInfo.lpBitMap;
|
|
ConsoleMutex = GraphicsBufferInfo.hMutex;
|
|
|
|
/* Clear the framebuffer */
|
|
// RtlZeroMemory(*GraphicsFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
|
|
RtlZeroMemory(GraphicsFramebuffer, BitmapInfo->bmiHeader.biSizeImage);
|
|
|
|
/* Set the graphics mode palette */
|
|
SetConsolePalette(GraphicsConsoleBuffer,
|
|
PaletteHandle,
|
|
SYSPAL_NOSTATIC256);
|
|
|
|
/* Set the active buffer */
|
|
SetActiveScreenBuffer(GraphicsConsoleBuffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID VgaConsoleDestroyGraphicsScreen(VOID)
|
|
{
|
|
/* Release the console framebuffer mutex */
|
|
ReleaseMutex(ConsoleMutex);
|
|
|
|
/* Switch back to the default console text buffer */
|
|
// SetActiveScreenBuffer(TextConsoleBuffer);
|
|
|
|
/* Cleanup the video data */
|
|
CloseHandle(ConsoleMutex);
|
|
ConsoleMutex = NULL;
|
|
// GraphicsFramebuffer = NULL;
|
|
CloseHandle(GraphicsConsoleBuffer);
|
|
GraphicsConsoleBuffer = NULL;
|
|
|
|
// /* Reset the active framebuffer */
|
|
// ActiveFramebuffer = NULL;
|
|
|
|
DoubleWidth = FALSE;
|
|
DoubleHeight = FALSE;
|
|
}
|
|
|
|
BOOL
|
|
VgaConsoleCreateTextScreen(// OUT PCHAR_CELL* TextFramebuffer,
|
|
IN PCOORD Resolution,
|
|
IN HANDLE PaletteHandle)
|
|
{
|
|
/* Switch to the text buffer */
|
|
// FIXME: Wouldn't it be preferrable to switch to it AFTER we reset everything??
|
|
SetActiveScreenBuffer(TextConsoleBuffer);
|
|
|
|
/* Adjust the text framebuffer if we changed the resolution */
|
|
if (TextResolution.X != Resolution->X ||
|
|
TextResolution.Y != Resolution->Y)
|
|
{
|
|
DetachFromConsoleInternal();
|
|
|
|
/*
|
|
* AttachToConsoleInternal sets TextResolution
|
|
* to the new resolution and updates ConsoleInfo.
|
|
*/
|
|
if (!AttachToConsoleInternal(Resolution))
|
|
{
|
|
DisplayMessage(L"An unexpected error occurred!\n");
|
|
EmulatorTerminate();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UpdateCursorPosition();
|
|
}
|
|
|
|
/*
|
|
* Set the text mode palette.
|
|
*
|
|
* INFORMATION: This call should fail on Windows (and therefore
|
|
* we get the default palette and our external behaviour is
|
|
* just like Windows' one), but it should success on ReactOS
|
|
* (so that we get console palette changes even for text-mode
|
|
* screen buffers, which is a new feature on ReactOS).
|
|
*/
|
|
SetConsolePalette(TextConsoleBuffer,
|
|
PaletteHandle,
|
|
SYSPAL_NOSTATIC256);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID VgaConsoleDestroyTextScreen(VOID)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
VOID VgaConsoleRepaintScreen(PSMALL_RECT Rect)
|
|
{
|
|
HANDLE ConsoleBufferHandle = NULL;
|
|
SMALL_RECT UpdateRectangle = *Rect;
|
|
|
|
/* Check if we are in text or graphics mode */
|
|
if (ScreenMode == GRAPHICS_MODE)
|
|
{
|
|
/* Graphics mode */
|
|
ConsoleBufferHandle = GraphicsConsoleBuffer;
|
|
|
|
/* In DoubleVision mode, scale the update rectangle */
|
|
if (DoubleWidth)
|
|
{
|
|
UpdateRectangle.Left *= 2;
|
|
UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
|
|
}
|
|
if (DoubleHeight)
|
|
{
|
|
UpdateRectangle.Top *= 2;
|
|
UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Text mode */
|
|
ConsoleBufferHandle = TextConsoleBuffer;
|
|
}
|
|
|
|
/* Redraw the screen */
|
|
__InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
|
|
}
|
|
|
|
BOOLEAN VgaConsoleInitialize(HANDLE TextHandle)
|
|
{
|
|
/*
|
|
* Initialize the console video by saving the default
|
|
* text-mode console output handle, if it is valid.
|
|
*/
|
|
if (!IsConsoleHandle(TextHandle)) return FALSE;
|
|
TextConsoleBuffer = TextHandle;
|
|
|
|
/* Save the original cursor and console screen buffer information */
|
|
if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
|
|
!GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
|
|
{
|
|
TextConsoleBuffer = NULL;
|
|
return FALSE;
|
|
}
|
|
ConsoleInfo = OrgConsoleBufferInfo;
|
|
|
|
/* Switch to the text buffer, but do not enter into a text mode */
|
|
SetActiveScreenBuffer(TextConsoleBuffer);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID VgaConsoleCleanup(VOID)
|
|
{
|
|
/* If the console video was not initialized, just return */
|
|
if (!TextConsoleBuffer)
|
|
return;
|
|
|
|
VgaDetachFromConsole();
|
|
|
|
// TODO: We need to initialize those events before using them!
|
|
CloseHandle(AnotherEvent);
|
|
CloseHandle(EndEvent);
|
|
CloseHandle(StartEvent);
|
|
}
|