The RegisterConsoleVDM API (undocumented) allows NTVDM to get a VGA-like text framebuffer hold by the console server. This text framebuffer is unique, per-console (so that changing the active text screen buffer doesn't change the text framebuffer) and exists even if you use a graphics screen buffer.
NTVDM can write in this framebuffer, and when a call to InvalidateConsoleDIBits is done, it is painted on the screen.

Since RegisterConsoleVDM is unimplemented on ReactOS, I start an implementation of this API *inside* NTVDM (that will then be moved to kernel32/winsrv when it will work correctly).

I adapt the code of the VGA emulation to support RegisterConsoleVDM.

svn path=/branches/ntvdm/; revision=62371
This commit is contained in:
Hermès Bélusca-Maïto
2014-03-01 20:58:42 +00:00
parent 129e5d0305
commit b152f5cf43
3 changed files with 332 additions and 41 deletions
+1 -1
View File
@@ -409,7 +409,7 @@ BOOLEAN EmulatorInitialize(HANDLE ConsoleInput, HANDLE ConsoleOutput)
VOID EmulatorCleanup(VOID)
{
// VgaCleanup();
VgaCleanup();
/* Close the input thread handle */
if (InputThread != NULL) CloseHandle(InputThread);
+329 -40
View File
@@ -170,16 +170,45 @@ static CONST COLORREF VgaDefaultPalette[VGA_MAX_COLORS] =
#endif
static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
/*
* Console interface -- VGA-mode-agnostic
*/
typedef struct _CHAR_CELL
{
CHAR Char;
BYTE Attributes;
} CHAR_CELL, *PCHAR_CELL;
C_ASSERT(sizeof(CHAR_CELL) == 2);
static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
static LPVOID ConsoleFramebuffer = NULL;
static LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to
// either TextFramebuffer or a valid
// graphics framebuffer.
static HPALETTE PaletteHandle = NULL;
static HANDLE StartEvent = NULL;
static HANDLE EndEvent = NULL;
static HANDLE AnotherEvent = 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.
*/
static CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
static COORD TextResolution = {0};
static PCHAR_CELL TextFramebuffer = NULL;
static HANDLE TextConsoleBuffer = NULL;
/* Graphics mode */
static HANDLE GraphicsConsoleBuffer = NULL;
static HANDLE ConsoleMutex = NULL;
static HPALETTE PaletteHandle = NULL;
static BOOLEAN DoubleVision = FALSE;
/*
* VGA Hardware
*/
static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE];
static BYTE VgaLatchRegisters[VGA_NUM_BANKS] = {0, 0, 0, 0};
static BYTE VgaMiscRegister;
@@ -210,7 +239,7 @@ static BOOLEAN InVerticalRetrace = FALSE;
static BOOLEAN InHorizontalRetrace = FALSE;
static BOOLEAN NeedsUpdate = FALSE;
static BOOLEAN ModeChanged = TRUE;
static BOOLEAN ModeChanged = FALSE;
static BOOLEAN CursorMoved = FALSE;
static BOOLEAN PaletteChanged = FALSE;
@@ -223,8 +252,246 @@ enum SCREEN_MODE
static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
/* RegisterConsoleVDM EMULATION ***********************************************/
#include <ntddvdeo.h>
typedef
BOOL
(WINAPI *pRegisterConsoleVDM)
(
BOOL IsDosVDM_flag,
HANDLE EventHandle_1,
HANDLE EventHandle_2,
HANDLE EventHandle_3,
int Unused1,
PVOID returned_val_1,
PVOID *returned_val_2,
PVOID lpUnknownBuffer,
DWORD theUnknownBufferLength,
COORD theVDMBufferSize,
PCHAR *lpVDMBuffer
);
#if 0
BOOL
WINAPI
RegisterConsoleVDM
(
BOOL IsDosVDM_flag,
HANDLE EventHandle_1,
HANDLE EventHandle_2,
HANDLE EventHandle_3,
int Unused1,
PVOID returned_val_1,
PVOID *returned_val_2,
PVOID lpUnknownBuffer,
DWORD theUnknownBufferLength,
COORD theVDMBufferSize,
PVOID *lpVDMBuffer
);
HMODULE hKernel32 = NULL;
pRegisterConsoleVDM RegisterConsoleVDM = NULL;
#endif
/*
* 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(BOOL IsDosVDM_flag,
HANDLE EventHandle_1,
HANDLE EventHandle_2,
HANDLE EventHandle_3,
int Unused1,
PVOID returned_val_1,
PVOID *returned_val_2,
PVOID lpUnknownBuffer,
DWORD theUnknownBufferLength,
COORD theVDMBufferSize,
PCHAR *lpVDMBuffer)
{
UNREFERENCED_PARAMETER(EventHandle_3);
UNREFERENCED_PARAMETER(Unused1);
UNREFERENCED_PARAMETER(returned_val_1);
UNREFERENCED_PARAMETER(returned_val_2);
UNREFERENCED_PARAMETER(lpUnknownBuffer);
UNREFERENCED_PARAMETER(theUnknownBufferLength);
SetLastError(0);
DPRINT1("__RegisterConsoleVDM(%d)\n", IsDosVDM_flag);
if (lpVDMBuffer == NULL) return FALSE;
if (IsDosVDM_flag)
{
// if (EventHandle_1 == NULL || EventHandle_2 == NULL) return FALSE;
if (VDMBuffer != NULL) return FALSE;
VDMBufferSize = theVDMBufferSize;
/* HACK: Cache -- to be removed in the real implementation */
CharBuff = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
theVDMBufferSize.X * theVDMBufferSize.Y
* sizeof(CHAR_INFO));
ASSERT(CharBuff);
VDMBuffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
theVDMBufferSize.X * theVDMBufferSize.Y
* sizeof(CHAR_CELL));
*lpVDMBuffer = (PCHAR)VDMBuffer;
return (VDMBuffer != NULL);
}
else
{
/* HACK: Cache -- to be removed in the real implementation */
if (CharBuff) HeapFree(GetProcessHeap(), 0, CharBuff);
CharBuff = NULL;
if (VDMBuffer) HeapFree(GetProcessHeap(), 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);
}
/* PRIVATE FUNCTIONS **********************************************************/
static BOOL VgaAttachToConsole(PCOORD Resolution)
{
BOOL Success = FALSE;
ULONG Length = 0;
PVIDEO_HARDWARE_STATE_HEADER State;
ASSERT(TextFramebuffer == NULL);
// ResetEvent(AnotherEvent);
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: )
* to check whether the current process is a VDM process, and fails otherwise with the
* error 0xC0000022 ().
*
* It is worth it to notice that also basesrv.dll does the same only for the
* BaseSrvIsFirstVDM API...
*/
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,
(PCHAR*)&TextFramebuffer);
if (!Success)
{
DisplayMessage(L"RegisterConsoleVDM failed with error %d\n", GetLastError());
VdmRunning = FALSE;
}
return Success;
}
static VOID VgaDetachFromConsole(VOID)
{
ULONG dummyLength;
PVOID dummyPtr;
COORD dummySize = {0};
__RegisterConsoleVDM(0,
NULL,
NULL,
NULL,
0,
&dummyLength,
&dummyPtr,
NULL,
0,
dummySize,
(PCHAR*)&dummyPtr);
TextFramebuffer = NULL;
}
static BOOL IsConsoleHandle(HANDLE hHandle)
{
DWORD dwMode;
/* Check whether the handle may be that of a console... */
if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
return FALSE;
/*
* It may be. Perform another test... The idea comes from the
* MSDN description of the WriteConsole API:
*
* "WriteConsole fails if it is used with a standard handle
* that is redirected to a file. If an application processes
* multilingual output that can be redirected, determine whether
* the output handle is a console handle (one method is to call
* the GetConsoleMode function and check whether it succeeds).
* If the handle is a console handle, call WriteConsole. If the
* handle is not a console handle, the output is redirected and
* you should call WriteFile to perform the I/O."
*/
return GetConsoleMode(hHandle, &dwMode);
}
static inline DWORD VgaGetAddressSize(VOID)
{
if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
@@ -648,18 +915,24 @@ static BOOL VgaEnterTextMode(PCOORD Resolution)
/* Update the saved console information */
GetConsoleScreenBufferInfo(TextConsoleBuffer, &ConsoleInfo);
/* Allocate a framebuffer */
ConsoleFramebuffer = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
Resolution->X * Resolution->Y
* sizeof(CHAR_INFO));
if (ConsoleFramebuffer == NULL)
/* Adjust the text framebuffer if we changed resolution */
if (TextResolution.X != Resolution->X ||
TextResolution.Y != Resolution->Y)
{
DisplayMessage(L"An unexpected error occurred!\n");
VdmRunning = FALSE;
return FALSE;
VgaDetachFromConsole();
/* VgaAttachToConsole sets TextResolution to the new resolution */
if (!VgaAttachToConsole(Resolution))
{
DisplayMessage(L"An unexpected error occurred!\n");
VdmRunning = FALSE;
return FALSE;
}
}
/* The active framebuffer is now the text framebuffer */
ConsoleFramebuffer = TextFramebuffer;
/*
* Set the text mode palette.
*
@@ -681,8 +954,7 @@ static BOOL VgaEnterTextMode(PCOORD Resolution)
static VOID VgaLeaveTextMode(VOID)
{
/* Free the old framebuffer */
HeapFree(GetProcessHeap(), 0, ConsoleFramebuffer);
/* Reset the active framebuffer */
ConsoleFramebuffer = NULL;
}
@@ -736,7 +1008,7 @@ static VOID VgaChangeMode(VOID)
static VOID VgaUpdateFramebuffer(VOID)
{
INT i, j, k;
SHORT i, j, k;
COORD Resolution = VgaGetDisplayResolution();
DWORD AddressSize = VgaGetAddressSize();
DWORD Address = MAKEWORD(VgaCrtcRegisters[VGA_CRTC_START_ADDR_LOW_REG],
@@ -927,8 +1199,8 @@ static VOID VgaUpdateFramebuffer(VOID)
{
/* Text mode */
DWORD CurrentAddr;
PCHAR_INFO CharBuffer = (PCHAR_INFO)ConsoleFramebuffer;
CHAR_INFO CharInfo;
PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
CHAR_CELL CharInfo;
/* Loop through the scanlines */
for (i = 0; i < Resolution.Y; i++)
@@ -939,13 +1211,13 @@ static VOID VgaUpdateFramebuffer(VOID)
CurrentAddr = LOWORD((Address + j) * AddressSize);
/* Plane 0 holds the character itself */
CharInfo.Char.AsciiChar = VgaMemory[CurrentAddr];
CharInfo.Char = VgaMemory[CurrentAddr];
/* Plane 1 holds the attribute */
CharInfo.Attributes = VgaMemory[CurrentAddr + VGA_BANK_SIZE];
/* Now check if the resulting character data has changed */
if ((CharBuffer[i * Resolution.X + j].Char.AsciiChar != CharInfo.Char.AsciiChar)
if ((CharBuffer[i * Resolution.X + j].Char != CharInfo.Char)
|| (CharBuffer[i * Resolution.X + j].Attributes != CharInfo.Attributes))
{
/* Yes, write the new value */
@@ -1361,32 +1633,24 @@ VOID VgaRefreshDisplay(VOID)
{
/* Graphics mode */
ConsoleBufferHandle = GraphicsConsoleBuffer;
/* In DoubleVision mode, scale the update rectangle */
if (DoubleVision)
{
UpdateRectangle.Left *= 2;
UpdateRectangle.Top *= 2;
UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
}
}
else
{
/* Text mode */
COORD Origin = { UpdateRectangle.Left, UpdateRectangle.Top };
ConsoleBufferHandle = TextConsoleBuffer;
/* Write the data to the console */
WriteConsoleOutputA(TextConsoleBuffer,
(PCHAR_INFO)ConsoleFramebuffer,
Resolution,
Origin,
&UpdateRectangle);
}
/* In DoubleVision mode, scale the update rectangle */
if (DoubleVision)
{
UpdateRectangle.Left *= 2;
UpdateRectangle.Top *= 2;
UpdateRectangle.Right = UpdateRectangle.Right * 2 + 1;
UpdateRectangle.Bottom = UpdateRectangle.Bottom * 2 + 1;
}
/* Redraw the screen */
InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
__InvalidateConsoleDIBits(ConsoleBufferHandle, &UpdateRectangle);
/* Clear the update flag */
NeedsUpdate = FALSE;
@@ -1491,7 +1755,7 @@ VOID VgaResetPalette(VOID)
BOOLEAN VgaInitialize(HANDLE TextHandle)
{
/* Save the default text-mode console output handle */
if (TextHandle == INVALID_HANDLE_VALUE) return FALSE;
if (!IsConsoleHandle(TextHandle)) return FALSE;
TextConsoleBuffer = TextHandle;
/* Save the console information */
@@ -1529,4 +1793,29 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
return TRUE;
}
VOID VgaCleanup(VOID)
{
if (ScreenMode == GRAPHICS_MODE)
{
/* Leave the current graphics mode */
VgaLeaveGraphicsMode();
}
else
{
/* Leave the current text mode */
VgaLeaveTextMode();
}
VgaDetachFromConsole();
CloseHandle(AnotherEvent);
CloseHandle(EndEvent);
CloseHandle(StartEvent);
#if 0
RegisterConsoleVDM = NULL;
FreeLibrary(hKernel32);
#endif
}
/* EOF */
+2
View File
@@ -258,7 +258,9 @@ VOID VgaHorizontalRetrace(VOID);
VOID VgaReadMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
VOID VgaWriteMemory(DWORD Address, LPBYTE Buffer, DWORD Size);
VOID VgaClearMemory(VOID);
BOOLEAN VgaInitialize(HANDLE TextHandle);
VOID VgaCleanup(VOID);
#endif // _VGA_H_