X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=subsystems%2Fntvdm%2Fhardware%2Fvga.c;h=7aa44f1256631c29f63e75c0933e879fed933a01;hp=ab4423d32f3a029345d86fed3077e5585893ca63;hb=b152f5cf439d28bf0aad8689e3457ad020608b87;hpb=129e5d0305dc7a5e3a03788a84b7ba882958744a;ds=sidebyside diff --git a/subsystems/ntvdm/hardware/vga.c b/subsystems/ntvdm/hardware/vga.c index ab4423d32f3..7aa44f12566 100644 --- a/subsystems/ntvdm/hardware/vga.c +++ b/subsystems/ntvdm/hardware/vga.c @@ -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 LPVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to + // either TextFramebuffer or a valid + // graphics framebuffer. +static HPALETTE PaletteHandle = NULL; -static BYTE VgaMemory[VGA_NUM_BANKS * VGA_BANK_SIZE]; -static LPVOID ConsoleFramebuffer = 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 + +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 */