Start factoring out all code related to console-specific stuff (while leaving everything else in place). This is WIP (and the corresponding .c files are included where they are needed instead of being compiled, will be fixed later).

svn path=/trunk/; revision=69838
This commit is contained in:
Hermès Bélusca-Maïto 2015-11-07 20:07:12 +00:00
parent 21f40234d7
commit 394328e27d
9 changed files with 1605 additions and 1377 deletions

View file

@ -32,6 +32,9 @@
#include "io.h"
#include "hardware/video/svga.h"
/**/
#include "../console/video.h"
/**/
/* MACROS *********************************************************************/

View file

@ -0,0 +1,565 @@
/* INCLUDES *******************************************************************/
// #include "ntvdm.h"
// #define NDEBUG
// #include <debug.h>
// #include "emulator.h"
#include "resource.h"
/* VARIABLES ******************************************************************/
static HANDLE CurrentConsoleOutput = INVALID_HANDLE_VALUE;
static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
HWND hConsoleWnd = NULL;
static HMENU hConsoleMenu = NULL;
static INT VdmMenuPos = -1;
static BOOLEAN ShowPointer = TRUE;
/*
* Those menu helpers were taken from the GUI frontend in winsrv.dll
*/
typedef struct _VDM_MENUITEM
{
UINT uID;
const struct _VDM_MENUITEM *SubMenu;
UINT_PTR uCmdID;
} VDM_MENUITEM, *PVDM_MENUITEM;
static const VDM_MENUITEM VdmMenuItems[] =
{
{ IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
{ IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
{ -1, NULL, 0 }, /* Separator */
// { IDS_VDM_MOUNT_FLOPPY, NULL, ID_VDM_DRIVES },
// { IDS_VDM_EJECT_FLOPPY, NULL, ID_VDM_DRIVES },
{ -1, NULL, 0 }, /* Separator */
{ IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
{ 0, NULL, 0 } /* End of list */
};
static const VDM_MENUITEM VdmMainMenuItems[] =
{
{ -1, NULL, 0 }, /* Separator */
{ IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* "Hide mouse"; can be renamed to "Show mouse" */
{ IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
{ 0, NULL, 0 } /* End of list */
};
static VOID
AppendMenuItems(HMENU hMenu,
const VDM_MENUITEM *Items)
{
UINT i = 0;
WCHAR szMenuString[256];
HMENU hSubMenu;
do
{
if (Items[i].uID != (UINT)-1)
{
if (LoadStringW(GetModuleHandle(NULL),
Items[i].uID,
szMenuString,
ARRAYSIZE(szMenuString)) > 0)
{
if (Items[i].SubMenu != NULL)
{
hSubMenu = CreatePopupMenu();
if (hSubMenu != NULL)
{
AppendMenuItems(hSubMenu, Items[i].SubMenu);
if (!AppendMenuW(hMenu,
MF_STRING | MF_POPUP,
(UINT_PTR)hSubMenu,
szMenuString))
{
DestroyMenu(hSubMenu);
}
}
}
else
{
AppendMenuW(hMenu,
MF_STRING,
Items[i].uCmdID,
szMenuString);
}
}
}
else
{
AppendMenuW(hMenu,
MF_SEPARATOR,
0,
NULL);
}
i++;
} while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
}
static BOOL
VdmMenuExists(HMENU hConsoleMenu)
{
INT MenuPos, i;
MenuPos = GetMenuItemCount(hConsoleMenu);
/* Check for the presence of one of the VDM menu items */
for (i = 0; i <= MenuPos; i++)
{
if (GetMenuItemID(hConsoleMenu, i) == ID_SHOWHIDE_MOUSE)
{
/* Set VdmMenuPos to the position of the existing menu */
VdmMenuPos = i - 1;
return TRUE;
}
}
return FALSE;
}
static VOID
UpdateVdmMenuMouse(VOID)
{
WCHAR szMenuString[256];
/* Update "Hide/Show mouse" menu item */
if (LoadStringW(GetModuleHandle(NULL),
(!ShowPointer ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
szMenuString,
ARRAYSIZE(szMenuString)) > 0)
{
ModifyMenuW(hConsoleMenu, ID_SHOWHIDE_MOUSE,
MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
}
}
/*static*/ VOID
UpdateVdmMenuDisks(VOID)
{
UINT_PTR ItemID;
UNICODE_STRING ValueString;
USHORT i;
WCHAR szNoMedia[100];
WCHAR szMenuString1[256], szMenuString2[256];
/* Update the disks menu items */
LoadStringW(GetModuleHandle(NULL),
IDS_NO_MEDIA,
szNoMedia,
ARRAYSIZE(szNoMedia));
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_MOUNT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
if (GlobalSettings.FloppyDisks[i].Length != 0 &&
GlobalSettings.FloppyDisks[i].Buffer &&
GlobalSettings.FloppyDisks[i].Buffer != '\0')
{
/* Convert the ANSI string to UNICODE */
RtlAnsiStringToUnicodeString(&ValueString, &GlobalSettings.FloppyDisks[i], TRUE);
/* Update item text */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, ValueString.Buffer);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
RtlFreeUnicodeString(&ValueString);
/* Enable the eject item */
EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_ENABLED);
}
else
{
/* Update item text */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
/* Disable the eject item */
EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_GRAYED);
}
}
}
static VOID
UpdateVdmMenu(VOID)
{
UpdateVdmMenuMouse();
UpdateVdmMenuDisks();
}
static VOID
CreateVdmMenu(HANDLE ConOutHandle)
{
HMENU hVdmSubMenu;
UINT_PTR ItemID;
UINT Pos;
USHORT i;
WCHAR szNoMedia[100];
WCHAR szMenuString1[256], szMenuString2[256];
hConsoleMenu = ConsoleMenuControl(ConOutHandle,
ID_SHOWHIDE_MOUSE,
ID_VDM_DRIVES + (2 * ARRAYSIZE(GlobalSettings.FloppyDisks)));
if (hConsoleMenu == NULL) return;
/* Get the position where we are going to insert our menu items */
VdmMenuPos = GetMenuItemCount(hConsoleMenu);
/* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
if (!VdmMenuExists(hConsoleMenu))
{
/* Add all the menu entries */
AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
/* Add the removable drives menu entries */
hVdmSubMenu = GetSubMenu(hConsoleMenu, VdmMenuPos + 2); // VdmMenuItems
Pos = 3; // After the 2 items and the separator in VdmMenuItems
LoadStringW(GetModuleHandle(NULL),
IDS_NO_MEDIA,
szNoMedia,
ARRAYSIZE(szNoMedia));
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_MOUNT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
/* Drive 'x' -- Mount */
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
/* Add the item */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID, szMenuString2);
}
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_EJECT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
/* Drive 'x' -- Eject */
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
/* Add the item */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 1, szMenuString2);
}
/* Refresh the menu state */
UpdateVdmMenu();
DrawMenuBar(hConsoleWnd);
}
}
static VOID
DestroyVdmMenu(VOID)
{
UINT i = 0;
const VDM_MENUITEM *Items = VdmMainMenuItems;
do
{
DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
i++;
} while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
DrawMenuBar(hConsoleWnd);
}
static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
{
if (ShowPtr)
{
/* Be sure the cursor will be shown */
while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
}
else
{
/* Be sure the cursor will be hidden */
while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
}
}
static VOID EnableExtraHardware(HANDLE ConsoleInput)
{
DWORD ConInMode;
if (GetConsoleMode(ConsoleInput, &ConInMode))
{
#if 0
// GetNumberOfConsoleMouseButtons();
// GetSystemMetrics(SM_CMOUSEBUTTONS);
// GetSystemMetrics(SM_MOUSEPRESENT);
if (MousePresent)
{
#endif
/* Support mouse input events if there is a mouse on the system */
ConInMode |= ENABLE_MOUSE_INPUT;
#if 0
}
else
{
/* Do not support mouse input events if there is no mouse on the system */
ConInMode &= ~ENABLE_MOUSE_INPUT;
}
#endif
SetConsoleMode(ConsoleInput, ConInMode);
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
/*static*/ VOID
VdmShutdown(BOOLEAN Immediate);
static BOOL
WINAPI
ConsoleCtrlHandler(DWORD ControlType)
{
switch (ControlType)
{
case CTRL_LAST_CLOSE_EVENT:
{
/* Delayed shutdown */
DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
VdmShutdown(FALSE);
break;
}
default:
{
/* Stop the VDM if the user logs out or closes the console */
DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
VdmShutdown(TRUE);
}
}
return TRUE;
}
static VOID
ConsoleInitUI(VOID)
{
hConsoleWnd = GetConsoleWindow();
CreateVdmMenu(ConsoleOutput);
}
static VOID
ConsoleCleanupUI(VOID)
{
/* Display again properly the mouse pointer */
if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
DestroyVdmMenu();
}
BOOL
ConsoleAttach(VOID)
{
/* Save the original input and output console modes */
if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
!GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
{
CloseHandle(ConsoleOutput);
CloseHandle(ConsoleInput);
wprintf(L"FATAL: Cannot save console in/out modes\n");
return FALSE;
}
/* Set the console input mode */
SetConsoleMode(ConsoleInput, ENABLE_WINDOW_INPUT);
EnableExtraHardware(ConsoleInput);
/* Set the console output mode */
// SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
/* Initialize the UI */
ConsoleInitUI();
return TRUE;
}
VOID
ConsoleDetach(VOID)
{
/* Cleanup the UI */
ConsoleCleanupUI();
/* Restore the original input and output console modes */
SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
}
VOID
ConsoleReattach(HANDLE ConOutHandle)
{
DestroyVdmMenu();
CurrentConsoleOutput = ConOutHandle;
CreateVdmMenu(ConOutHandle);
/* Synchronize mouse cursor display with console screenbuffer switches */
ShowHideMousePointer(CurrentConsoleOutput, ShowPointer);
}
static BOOL
ConsoleInit(VOID)
{
/* Set the handler routine */
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
/* Enable the CTRL_LAST_CLOSE_EVENT */
SetLastConsoleEventActive();
/*
* NOTE: The CONIN$ and CONOUT$ "virtual" files
* always point to non-redirected console handles.
*/
/* Get the input handle to the real console, and check for success */
ConsoleInput = CreateFileW(L"CONIN$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (ConsoleInput == INVALID_HANDLE_VALUE)
{
wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
return FALSE;
}
/* Get the output handle to the real console, and check for success */
ConsoleOutput = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (ConsoleOutput == INVALID_HANDLE_VALUE)
{
CloseHandle(ConsoleInput);
wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
return FALSE;
}
/* Effectively attach to the console */
return ConsoleAttach();
}
static VOID
ConsoleCleanup(VOID)
{
/* Detach from the console */
ConsoleDetach();
/* Close the console handles */
if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
}
VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
{
switch (MenuEvent->dwCommandId)
{
/*
* System-defined menu commands
*/
case WM_INITMENU:
DPRINT1("WM_INITMENU\n");
break;
case WM_MENUSELECT:
DPRINT1("WM_MENUSELECT\n");
break;
/*
* User-defined menu commands
*/
case ID_SHOWHIDE_MOUSE:
ShowPointer = !ShowPointer;
ShowHideMousePointer(CurrentConsoleOutput, ShowPointer);
UpdateVdmMenuMouse();
break;
case ID_VDM_DUMPMEM_TXT:
DumpMemory(TRUE);
break;
case ID_VDM_DUMPMEM_BIN:
DumpMemory(FALSE);
break;
/* Drive 0 -- Mount */
/* Drive 1 -- Mount */
case ID_VDM_DRIVES + 0:
case ID_VDM_DRIVES + 2:
{
ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
MountFloppy(DiskNumber);
break;
}
/* Drive 0 -- Eject */
/* Drive 1 -- Eject */
case ID_VDM_DRIVES + 1:
case ID_VDM_DRIVES + 3:
{
ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
EjectFloppy(DiskNumber);
break;
}
case ID_VDM_QUIT:
/* Stop the VDM */
// EmulatorTerminate();
/* Nothing runs, so exit immediately */
DPRINT1("Killing NTVDM via console menu!\n");
VdmShutdown(TRUE);
break;
default:
break;
}
}
VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
{
DPRINT1("Focus events not handled\n");
}

View file

@ -0,0 +1,832 @@
/*
* 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] = CharBuff[i * TextResolution.X + j].Char.AsciiChar;
/* Store the attribute in plane 1 */
VgaMemory[CurrentAddr + VGA_BANK_SIZE] = (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 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 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;
ConsoleMutex = GraphicsBufferInfo.hMutex;
/* Clear the framebuffer */
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)
{
/* Save the default text-mode console output handle */
if (!IsConsoleHandle(TextHandle)) return FALSE;
TextConsoleBuffer = TextHandle;
/* Save the original cursor and console screen buffer information */
if (!GetConsoleCursorInfo(TextConsoleBuffer, &OrgConsoleCursorInfo) ||
!GetConsoleScreenBufferInfo(TextConsoleBuffer, &OrgConsoleBufferInfo))
{
return FALSE;
}
ConsoleInfo = OrgConsoleBufferInfo;
/* Switch to the text buffer, but do not enter into a text mode */
SetActiveScreenBuffer(TextConsoleBuffer);
return TRUE;
}
VOID VgaConsoleCleanup(VOID)
{
VgaDetachFromConsole();
CloseHandle(AnotherEvent);
CloseHandle(EndEvent);
CloseHandle(StartEvent);
}

View file

@ -0,0 +1,35 @@
/* FUNCTIONS ******************************************************************/
VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent);
BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical);
BOOL VgaAttachToConsole(VOID);
VOID VgaDetachFromConsole(VOID);
VOID
VgaConsoleUpdateTextCursor(BOOL CursorVisible,
BYTE CursorStart,
BYTE CursorEnd,
BYTE TextSize,
DWORD ScanlineSize,
WORD Location);
BOOL
VgaConsoleCreateGraphicsScreen(OUT PBYTE* GraphicsFramebuffer,
IN PCOORD Resolution,
IN HANDLE PaletteHandle);
VOID VgaConsoleDestroyGraphicsScreen(VOID);
BOOL
VgaConsoleCreateTextScreen(OUT PCHAR_CELL* TextFramebuffer,
IN PCOORD Resolution,
IN HANDLE PaletteHandle);
VOID VgaConsoleDestroyTextScreen(VOID);
VOID VgaConsoleRepaintScreen(PSMALL_RECT Rect);
BOOLEAN VgaConsoleInitialize(HANDLE TextHandle);
VOID VgaConsoleCleanup(VOID);

View file

@ -25,6 +25,9 @@
#include "hardware/ps2.h"
#include "hardware/pic.h"
#include "hardware/video/svga.h"
/**/
#include "../console/video.h"
/**/
#include "mouse32.h"
#include "bios/bios.h"

View file

@ -36,6 +36,9 @@
#include "hardware/ps2.h"
#include "hardware/sound/speaker.h"
#include "hardware/video/svga.h"
/**/
#include "./console/video.h"
/**/
#include "vddsup.h"
#include "io.h"

File diff suppressed because it is too large Load diff

View file

@ -368,11 +368,19 @@ typedef struct _SVGA_REGISTERS
UCHAR Attribute[VGA_AC_MAX_REG];
} SVGA_REGISTERS, *PSVGA_REGISTERS;
/* FUNCTIONS ******************************************************************/
VOID ScreenEventHandler(PWINDOW_BUFFER_SIZE_RECORD ScreenEvent);
BOOL VgaAttachToConsole(VOID);
VOID VgaDetachFromConsole(VOID);
/*
* Console interface -- VGA-mode-agnostic
*/
// WARNING! This structure *MUST BE* in sync with the one in consrv/include/conio_winsrv.h
typedef struct _CHAR_CELL
{
CHAR Char;
BYTE Attributes;
} CHAR_CELL, *PCHAR_CELL;
C_ASSERT(sizeof(CHAR_CELL) == 2);
/* FUNCTIONS ******************************************************************/
COORD VgaGetDisplayResolution(VOID);
VOID VgaRefreshDisplay(VOID);
@ -380,7 +388,6 @@ VOID FASTCALL VgaReadMemory(ULONG Address, PVOID Buffer, ULONG Size);
BOOLEAN FASTCALL VgaWriteMemory(ULONG Address, PVOID Buffer, ULONG Size);
VOID VgaWriteTextModeFont(UINT FontNumber, CONST UCHAR *FontData, UINT Height);
VOID VgaClearMemory(VOID);
BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical);
BOOLEAN VgaInitialize(HANDLE TextHandle);
VOID VgaCleanup(VOID);

View file

@ -20,338 +20,15 @@
#include "dos/dem.h"
#include "resource.h"
/* VARIABLES ******************************************************************/
static HANDLE CurrentConsoleOutput = INVALID_HANDLE_VALUE;
static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
NTVDM_SETTINGS GlobalSettings;
// Command line of NTVDM
INT NtVdmArgc;
WCHAR** NtVdmArgv;
HWND hConsoleWnd = NULL;
static HMENU hConsoleMenu = NULL;
static INT VdmMenuPos = -1;
static BOOLEAN ShowPointer = TRUE;
/*
* Those menu helpers were taken from the GUI frontend in winsrv.dll
*/
typedef struct _VDM_MENUITEM
{
UINT uID;
const struct _VDM_MENUITEM *SubMenu;
UINT_PTR uCmdID;
} VDM_MENUITEM, *PVDM_MENUITEM;
static const VDM_MENUITEM VdmMenuItems[] =
{
{ IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
{ IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
{ -1, NULL, 0 }, /* Separator */
// { IDS_VDM_MOUNT_FLOPPY, NULL, ID_VDM_DRIVES },
// { IDS_VDM_EJECT_FLOPPY, NULL, ID_VDM_DRIVES },
{ -1, NULL, 0 }, /* Separator */
{ IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
{ 0, NULL, 0 } /* End of list */
};
static const VDM_MENUITEM VdmMainMenuItems[] =
{
{ -1, NULL, 0 }, /* Separator */
{ IDS_HIDE_MOUSE, NULL, ID_SHOWHIDE_MOUSE }, /* "Hide mouse"; can be renamed to "Show mouse" */
{ IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
{ 0, NULL, 0 } /* End of list */
};
static VOID
AppendMenuItems(HMENU hMenu,
const VDM_MENUITEM *Items)
{
UINT i = 0;
WCHAR szMenuString[256];
HMENU hSubMenu;
do
{
if (Items[i].uID != (UINT)-1)
{
if (LoadStringW(GetModuleHandle(NULL),
Items[i].uID,
szMenuString,
ARRAYSIZE(szMenuString)) > 0)
{
if (Items[i].SubMenu != NULL)
{
hSubMenu = CreatePopupMenu();
if (hSubMenu != NULL)
{
AppendMenuItems(hSubMenu, Items[i].SubMenu);
if (!AppendMenuW(hMenu,
MF_STRING | MF_POPUP,
(UINT_PTR)hSubMenu,
szMenuString))
{
DestroyMenu(hSubMenu);
}
}
}
else
{
AppendMenuW(hMenu,
MF_STRING,
Items[i].uCmdID,
szMenuString);
}
}
}
else
{
AppendMenuW(hMenu,
MF_SEPARATOR,
0,
NULL);
}
i++;
} while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
}
BOOL
VdmMenuExists(HMENU hConsoleMenu)
{
INT MenuPos, i;
MenuPos = GetMenuItemCount(hConsoleMenu);
/* Check for the presence of one of the VDM menu items */
for (i = 0; i <= MenuPos; i++)
{
if (GetMenuItemID(hConsoleMenu, i) == ID_SHOWHIDE_MOUSE)
{
/* Set VdmMenuPos to the position of the existing menu */
VdmMenuPos = i - 1;
return TRUE;
}
}
return FALSE;
}
static VOID
UpdateVdmMenuMouse(VOID)
{
WCHAR szMenuString[256];
/* Update "Hide/Show mouse" menu item */
if (LoadStringW(GetModuleHandle(NULL),
(!ShowPointer ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
szMenuString,
ARRAYSIZE(szMenuString)) > 0)
{
ModifyMenuW(hConsoleMenu, ID_SHOWHIDE_MOUSE,
MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
}
}
/*static*/ VOID
UpdateVdmMenuDisks(VOID)
{
UINT_PTR ItemID;
UNICODE_STRING ValueString;
USHORT i;
WCHAR szNoMedia[100];
WCHAR szMenuString1[256], szMenuString2[256];
/* Update the disks menu items */
LoadStringW(GetModuleHandle(NULL),
IDS_NO_MEDIA,
szNoMedia,
ARRAYSIZE(szNoMedia));
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_MOUNT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
if (GlobalSettings.FloppyDisks[i].Length != 0 &&
GlobalSettings.FloppyDisks[i].Buffer &&
GlobalSettings.FloppyDisks[i].Buffer != '\0')
{
/* Convert the ANSI string to UNICODE */
RtlAnsiStringToUnicodeString(&ValueString, &GlobalSettings.FloppyDisks[i], TRUE);
/* Update item text */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, ValueString.Buffer);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
RtlFreeUnicodeString(&ValueString);
/* Enable the eject item */
EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_ENABLED);
}
else
{
/* Update item text */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
/* Disable the eject item */
EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_GRAYED);
}
}
}
static VOID
UpdateVdmMenu(VOID)
{
UpdateVdmMenuMouse();
UpdateVdmMenuDisks();
}
static VOID
CreateVdmMenu(HANDLE ConOutHandle)
{
HMENU hVdmSubMenu;
UINT_PTR ItemID;
UINT Pos;
USHORT i;
WCHAR szNoMedia[100];
WCHAR szMenuString1[256], szMenuString2[256];
hConsoleMenu = ConsoleMenuControl(ConOutHandle,
ID_SHOWHIDE_MOUSE,
ID_VDM_DRIVES + (2 * ARRAYSIZE(GlobalSettings.FloppyDisks)));
if (hConsoleMenu == NULL) return;
/* Get the position where we are going to insert our menu items */
VdmMenuPos = GetMenuItemCount(hConsoleMenu);
/* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
if (!VdmMenuExists(hConsoleMenu))
{
/* Add all the menu entries */
AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
/* Add the removable drives menu entries */
hVdmSubMenu = GetSubMenu(hConsoleMenu, VdmMenuPos + 2); // VdmMenuItems
Pos = 3; // After the 2 items and the separator in VdmMenuItems
LoadStringW(GetModuleHandle(NULL),
IDS_NO_MEDIA,
szNoMedia,
ARRAYSIZE(szNoMedia));
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_MOUNT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
/* Drive 'x' -- Mount */
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
/* Add the item */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID, szMenuString2);
}
LoadStringW(GetModuleHandle(NULL),
IDS_VDM_EJECT_FLOPPY,
szMenuString1,
ARRAYSIZE(szMenuString1));
/* Drive 'x' -- Eject */
for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
{
ItemID = ID_VDM_DRIVES + (2 * i);
/* Add the item */
_snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i);
szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 1, szMenuString2);
}
/* Refresh the menu state */
UpdateVdmMenu();
DrawMenuBar(hConsoleWnd);
}
}
static VOID
DestroyVdmMenu(VOID)
{
UINT i = 0;
const VDM_MENUITEM *Items = VdmMainMenuItems;
do
{
DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
i++;
} while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
DrawMenuBar(hConsoleWnd);
}
static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
{
if (ShowPtr)
{
/* Be sure the cursor will be shown */
while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
}
else
{
/* Be sure the cursor will be hidden */
while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
}
}
static VOID EnableExtraHardware(HANDLE ConsoleInput)
{
DWORD ConInMode;
if (GetConsoleMode(ConsoleInput, &ConInMode))
{
#if 0
// GetNumberOfConsoleMouseButtons();
// GetSystemMetrics(SM_CMOUSEBUTTONS);
// GetSystemMetrics(SM_MOUSEPRESENT);
if (MousePresent)
{
#endif
/* Support mouse input events if there is a mouse on the system */
ConInMode |= ENABLE_MOUSE_INPUT;
#if 0
}
else
{
/* Do not support mouse input events if there is no mouse on the system */
ConInMode &= ~ENABLE_MOUSE_INPUT;
}
#endif
SetConsoleMode(ConsoleInput, ConInMode);
}
}
/* PRIVATE FUNCTIONS **********************************************************/
static NTSTATUS
NTAPI
@ -624,6 +301,54 @@ FreeGlobalSettings(IN PNTVDM_SETTINGS Settings)
}
}
static VOID
ConsoleCleanup(VOID);
/** HACK!! **/
#include "./console/console.c"
/** HACK!! **/
/*static*/ VOID
VdmShutdown(BOOLEAN Immediate)
{
/*
* Immediate = TRUE: Immediate shutdown;
* FALSE: Delayed shutdown.
*/
static BOOLEAN MustShutdown = FALSE;
/* If a shutdown is ongoing, just return */
if (MustShutdown)
{
DPRINT1("Shutdown is ongoing...\n");
Sleep(INFINITE);
return;
}
/* First notify DOS to see whether we can shut down now */
MustShutdown = DosShutdown(Immediate);
/*
* In case we perform an immediate shutdown, or the DOS says
* we can shut down, do it now.
*/
MustShutdown = MustShutdown || Immediate;
if (MustShutdown)
{
EmulatorTerminate();
BiosCleanup();
EmulatorCleanup();
ConsoleCleanup();
FreeGlobalSettings(&GlobalSettings);
DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
/* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
ExitProcess(0);
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
VOID
@ -747,256 +472,6 @@ PrintMessageAnsi(IN CHAR_PRINT CharPrint,
#endif
}
static VOID
ConsoleCleanup(VOID);
static VOID
VdmShutdown(BOOLEAN Immediate)
{
/*
* Immediate = TRUE: Immediate shutdown;
* FALSE: Delayed shutdown.
*/
static BOOLEAN MustShutdown = FALSE;
/* If a shutdown is ongoing, just return */
if (MustShutdown)
{
DPRINT1("Shutdown is ongoing...\n");
Sleep(INFINITE);
return;
}
/* First notify DOS to see whether we can shut down now */
MustShutdown = DosShutdown(Immediate);
/*
* In case we perform an immediate shutdown, or the DOS says
* we can shut down, do it now.
*/
MustShutdown = MustShutdown || Immediate;
if (MustShutdown)
{
EmulatorTerminate();
BiosCleanup();
EmulatorCleanup();
ConsoleCleanup();
FreeGlobalSettings(&GlobalSettings);
DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
/* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
ExitProcess(0);
}
}
static BOOL
WINAPI
ConsoleCtrlHandler(DWORD ControlType)
{
switch (ControlType)
{
case CTRL_LAST_CLOSE_EVENT:
{
/* Delayed shutdown */
DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
VdmShutdown(FALSE);
break;
}
default:
{
/* Stop the VDM if the user logs out or closes the console */
DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
VdmShutdown(TRUE);
}
}
return TRUE;
}
static VOID
ConsoleInitUI(VOID)
{
hConsoleWnd = GetConsoleWindow();
CreateVdmMenu(ConsoleOutput);
}
static VOID
ConsoleCleanupUI(VOID)
{
/* Display again properly the mouse pointer */
if (ShowPointer) ShowHideMousePointer(ConsoleOutput, ShowPointer);
DestroyVdmMenu();
}
BOOL
ConsoleAttach(VOID)
{
/* Save the original input and output console modes */
if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
!GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
{
CloseHandle(ConsoleOutput);
CloseHandle(ConsoleInput);
wprintf(L"FATAL: Cannot save console in/out modes\n");
return FALSE;
}
/* Set the console input mode */
// FIXME: Activate ENABLE_WINDOW_INPUT when we will want to perform actions
// upon console window events (screen buffer resize, ...).
SetConsoleMode(ConsoleInput, 0 /* | ENABLE_WINDOW_INPUT */);
EnableExtraHardware(ConsoleInput);
/* Set the console output mode */
// SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
/* Initialize the UI */
ConsoleInitUI();
return TRUE;
}
VOID
ConsoleDetach(VOID)
{
/* Cleanup the UI */
ConsoleCleanupUI();
/* Restore the original input and output console modes */
SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
}
VOID
ConsoleReattach(HANDLE ConOutHandle)
{
DestroyVdmMenu();
CurrentConsoleOutput = ConOutHandle;
CreateVdmMenu(ConOutHandle);
/* Synchronize mouse cursor display with console screenbuffer switches */
ShowHideMousePointer(CurrentConsoleOutput, ShowPointer);
}
static BOOL
ConsoleInit(VOID)
{
/* Set the handler routine */
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
/* Enable the CTRL_LAST_CLOSE_EVENT */
SetLastConsoleEventActive();
/*
* NOTE: The CONIN$ and CONOUT$ "virtual" files
* always point to non-redirected console handles.
*/
/* Get the input handle to the real console, and check for success */
ConsoleInput = CreateFileW(L"CONIN$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (ConsoleInput == INVALID_HANDLE_VALUE)
{
wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
return FALSE;
}
/* Get the output handle to the real console, and check for success */
ConsoleOutput = CreateFileW(L"CONOUT$",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (ConsoleOutput == INVALID_HANDLE_VALUE)
{
CloseHandle(ConsoleInput);
wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
return FALSE;
}
/* Effectively attach to the console */
return ConsoleAttach();
}
static VOID
ConsoleCleanup(VOID)
{
/* Detach from the console */
ConsoleDetach();
/* Close the console handles */
if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
}
VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
{
switch (MenuEvent->dwCommandId)
{
case ID_SHOWHIDE_MOUSE:
ShowPointer = !ShowPointer;
ShowHideMousePointer(CurrentConsoleOutput, ShowPointer);
UpdateVdmMenuMouse();
break;
case ID_VDM_DUMPMEM_TXT:
DumpMemory(TRUE);
break;
case ID_VDM_DUMPMEM_BIN:
DumpMemory(FALSE);
break;
/* Drive 0 -- Mount */
/* Drive 1 -- Mount */
case ID_VDM_DRIVES + 0:
case ID_VDM_DRIVES + 2:
{
ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
MountFloppy(DiskNumber);
break;
}
/* Drive 0 -- Eject */
/* Drive 1 -- Eject */
case ID_VDM_DRIVES + 1:
case ID_VDM_DRIVES + 3:
{
ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
EjectFloppy(DiskNumber);
break;
}
case ID_VDM_QUIT:
/* Stop the VDM */
// EmulatorTerminate();
/* Nothing runs, so exit immediately */
DPRINT1("Killing NTVDM via console menu!\n");
VdmShutdown(TRUE);
break;
default:
break;
}
}
VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
{
DPRINT1("Focus events not handled\n");
}
INT
wmain(INT argc, WCHAR *argv[])
{