[NTVDM]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sat, 7 Nov 2015 20:07:12 +0000 (20:07 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sat, 7 Nov 2015 20:07:12 +0000 (20:07 +0000)
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

reactos/subsystems/mvdm/ntvdm/bios/vidbios.c
reactos/subsystems/mvdm/ntvdm/console/console.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/console/video.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/console/video.h [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/mouse32.c
reactos/subsystems/mvdm/ntvdm/emulator.c
reactos/subsystems/mvdm/ntvdm/hardware/video/svga.c
reactos/subsystems/mvdm/ntvdm/hardware/video/svga.h
reactos/subsystems/mvdm/ntvdm/ntvdm.c

index e4cdc37..5030f80 100644 (file)
@@ -32,6 +32,9 @@
 
 #include "io.h"
 #include "hardware/video/svga.h"
+/**/
+#include "../console/video.h"
+/**/
 
 /* MACROS *********************************************************************/
 
diff --git a/reactos/subsystems/mvdm/ntvdm/console/console.c b/reactos/subsystems/mvdm/ntvdm/console/console.c
new file mode 100644 (file)
index 0000000..8c87a08
--- /dev/null
@@ -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");
+}
diff --git a/reactos/subsystems/mvdm/ntvdm/console/video.c b/reactos/subsystems/mvdm/ntvdm/console/video.c
new file mode 100644 (file)
index 0000000..18e7960
--- /dev/null
@@ -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);
+}
diff --git a/reactos/subsystems/mvdm/ntvdm/console/video.h b/reactos/subsystems/mvdm/ntvdm/console/video.h
new file mode 100644 (file)
index 0000000..ceee02b
--- /dev/null
@@ -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);
index f704ed2..f59702a 100644 (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"
index dd92213..3fc0f4f 100644 (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"
index 9bb67af..78176aa 100644 (file)
 #include "io.h"
 #include "clock.h"
 
+#include "../../console/video.h"
+
 /* PRIVATE VARIABLES **********************************************************/
 
 static CONST DWORD MemoryBase[] = { 0xA0000, 0xA0000, 0xB0000, 0xB8000 };
 static CONST DWORD MemorySize[] = { 0x20000, 0x10000, 0x08000, 0x08000 };
 
-/*
- * Activate this line if you want to use the real
- * RegisterConsoleVDM API of ReactOS/Windows.
- */
-// #define USE_REAL_REGISTERCONSOLEVDM
-
 #define USE_REACTOS_COLORS
 // #define USE_DOSBOX_COLORS
 
@@ -208,35 +204,13 @@ static const COLORREF ConsoleColors[16] =
     RGB(255, 255, 255)  // BLUE  | GREEN | RED | INTENSITY
 };
 
-/*
- * 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);
-
-static PVOID ConsoleFramebuffer = NULL; // Active framebuffer, points to
-                                        // either TextFramebuffer or a
-                                        // valid graphics framebuffer.
+/// ConsoleFramebuffer
+static PVOID ActiveFramebuffer = NULL; // Active framebuffer, points to
+                                       // either TextFramebuffer or a
+                                       // valid graphics framebuffer.
 static HPALETTE TextPaletteHandle = NULL;
 static HPALETTE PaletteHandle = NULL;
 
-static HANDLE StartEvent = NULL;
-static HANDLE EndEvent   = NULL;
-static HANDLE AnotherEvent = NULL;
-
-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
@@ -244,20 +218,21 @@ static PVOID  OldConsoleFramebuffer = NULL;
  * 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;
+static PBYTE GraphicsFramebuffer = NULL;
+
+
+// static HANDLE ConsoleMutex = NULL;
+// /* DoubleVision support */
+// static BOOLEAN DoubleWidth  = FALSE;
+// static BOOLEAN DoubleHeight = FALSE;
+
+
+
 
 
 /*
@@ -318,394 +293,24 @@ static COORD CurrResolution   = {0};
 
 static SMALL_RECT UpdateRectangle = { 0, 0, 0, 0 };
 
-/* RegisterConsoleVDM EMULATION ***********************************************/
-
-#include <ntddvdeo.h>
 
-#ifdef USE_REAL_REGISTERCONSOLEVDM
 
-#define __RegisterConsoleVDM        RegisterConsoleVDM
-#define __InvalidateConsoleDIBits   InvalidateConsoleDIBits
 
-#else
+/** HACK!! **/
+#include "../../console/video.c"
+/** HACK!! **/
 
-/*
- * 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 inline DWORD VgaGetAddressSize(VOID);
-static VOID VgaUpdateTextCursor(VOID);
-
 static inline DWORD VgaGetVideoBaseAddress(VOID)
 {
     return MemoryBase[(VgaGcRegisters[VGA_GC_MISC_REG] >> 2) & 0x03];
 }
 
-static VOID VgaUpdateCursorPosition(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 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 BOOL VgaAttachToConsoleInternal(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
-
-    VgaUpdateCursorPosition();
-
-    return TRUE;
-}
-
-static VOID VgaDetachFromConsoleInternal(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 inline DWORD VgaGetAddressSize(VOID)
 {
     if (VgaCrtcRegisters[VGA_CRTC_UNDERLINE_REG] & VGA_CRTC_UNDERLINE_DWORD)
@@ -835,25 +440,6 @@ static inline BYTE VgaTranslateByteForWriting(BYTE Data, BYTE Plane)
     return Data;
 }
 
-static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
-{
-    /* Check if this is the first time the rectangle is updated */
-    if (!NeedsUpdate)
-    {
-        UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
-        UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
-    }
-
-    /* Expand the rectangle to include the point */
-    UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
-    UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
-    UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
-    UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
-
-    /* Set the update request flag */
-    NeedsUpdate = TRUE;
-}
-
 static inline ULONG VgaGetClockFrequency(VOID)
 {
     BYTE Numerator, Denominator;
@@ -1026,158 +612,73 @@ static VOID VgaResetPalette(VOID)
     PaletteChanged = TRUE;
 }
 
-static VOID VgaSetActiveScreenBuffer(HANDLE ScreenBuffer)
+static BOOL VgaEnterNewMode(SCREEN_MODE NewScreenMode, PCOORD Resolution)
 {
-    ASSERT(ScreenBuffer);
+    /* Check if the new mode is alphanumeric */
+    if (NewScreenMode == TEXT_MODE)
+    {
+        /* Enter new text mode */
 
-    /* Set the active buffer and reattach the VDM UI to it */
-    SetConsoleActiveScreenBuffer(ScreenBuffer);
-    ConsoleReattach(ScreenBuffer);
-}
+        if (!VgaConsoleCreateTextScreen(&TextFramebuffer,
+                                        Resolution,
+                                        TextPaletteHandle))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
+            EmulatorTerminate();
+            return FALSE;
+        }
 
-static BOOL VgaEnterGraphicsMode(PCOORD Resolution)
-{
-    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 active buffer */
-    VgaSetActiveScreenBuffer(GraphicsConsoleBuffer);
-
-    /* The active framebuffer is now the graphics framebuffer */
-    ConsoleFramebuffer = GraphicsFramebuffer;
-
-    /* Set the graphics mode palette */
-    SetConsolePalette(GraphicsConsoleBuffer,
-                      PaletteHandle,
-                      SYSPAL_NOSTATIC256);
-
-    /* Set the screen mode flag */
-    ScreenMode = GRAPHICS_MODE;
+        /* The active framebuffer is now the text framebuffer */
+        ActiveFramebuffer = TextFramebuffer;
 
-    return TRUE;
-}
+        /* Set the screen mode flag */
+        ScreenMode = TEXT_MODE;
 
-static VOID VgaLeaveGraphicsMode(VOID)
-{
-    /* Release the console framebuffer mutex */
-    ReleaseMutex(ConsoleMutex);
+        return TRUE;
+    }
+    else
+    {
+        /* Enter graphics mode */
 
-    /* Switch back to the default console text buffer */
-    // VgaSetActiveScreenBuffer(TextConsoleBuffer);
+        if (!VgaConsoleCreateGraphicsScreen(&GraphicsFramebuffer,
+                                            Resolution,
+                                            PaletteHandle))
+        {
+            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
+            EmulatorTerminate();
+            return FALSE;
+        }
 
-    /* Cleanup the video data */
-    CloseHandle(ConsoleMutex);
-    ConsoleMutex = NULL;
-    GraphicsFramebuffer = NULL;
-    CloseHandle(GraphicsConsoleBuffer);
-    GraphicsConsoleBuffer = NULL;
+        /* The active framebuffer is now the graphics framebuffer */
+        ActiveFramebuffer = GraphicsFramebuffer;
 
-    /* Reset the active framebuffer */
-    ConsoleFramebuffer = NULL;
+        /* Set the screen mode flag */
+        ScreenMode = GRAPHICS_MODE;
 
-    DoubleWidth  = FALSE;
-    DoubleHeight = FALSE;
+        return TRUE;
+    }
 }
 
-static BOOL VgaEnterTextMode(PCOORD Resolution)
+static VOID VgaLeaveCurrentMode(VOID)
 {
-    /* Switch to the text buffer */
-    // FIXME: Wouldn't it be preferrable to switch to it AFTER we reset everything??
-    VgaSetActiveScreenBuffer(TextConsoleBuffer);
-
-    /* Adjust the text framebuffer if we changed the resolution */
-    if (TextResolution.X != Resolution->X ||
-        TextResolution.Y != Resolution->Y)
+    /* Leave the current video mode */
+    if (ScreenMode == GRAPHICS_MODE)
     {
-        VgaDetachFromConsoleInternal();
+        VgaConsoleDestroyGraphicsScreen();
 
-        /*
-         * VgaAttachToConsoleInternal sets TextResolution
-         * to the new resolution and updates ConsoleInfo.
-         */
-        if (!VgaAttachToConsoleInternal(Resolution))
-        {
-            DisplayMessage(L"An unexpected error occurred!\n");
-            EmulatorTerminate();
-            return FALSE;
-        }
+        /* Cleanup the video data */
+        GraphicsFramebuffer = NULL;
     }
     else
     {
-        VgaUpdateCursorPosition();
-    }
-
-    /* The active framebuffer is now the text framebuffer */
-    ConsoleFramebuffer = TextFramebuffer;
-
-    /*
-     * 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,
-                      TextPaletteHandle,
-                      SYSPAL_NOSTATIC256);
-
-    /* Set the screen mode flag */
-    ScreenMode = TEXT_MODE;
+        VgaConsoleDestroyTextScreen();
 
-    return TRUE;
-}
+        /* Cleanup the video data */
+        TextFramebuffer = NULL;
+    }
 
-static VOID VgaLeaveTextMode(VOID)
-{
     /* Reset the active framebuffer */
-    ConsoleFramebuffer = NULL;
+    ActiveFramebuffer = NULL;
 }
 
 static VOID VgaChangeMode(VOID)
@@ -1201,37 +702,14 @@ static VOID VgaChangeMode(VOID)
     // *ONLY* if we succeeded in setting the new mode??
 
     /* Leave the current video mode */
-    if (ScreenMode == GRAPHICS_MODE)
-        VgaLeaveGraphicsMode();
-    else
-        VgaLeaveTextMode();
+    VgaLeaveCurrentMode(); // ScreenMode
 
     /* Update the current resolution */
     CurrResolution = NewResolution;
 
-    /* The new screen mode will be updated via the VgaEnterText/GraphicsMode functions */
-
-    /* Check if the new mode is alphanumeric */
-    if (NewScreenMode == TEXT_MODE)
-    {
-        /* Enter new text mode */
-        if (!VgaEnterTextMode(&CurrResolution))
-        {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into text mode. Error: %u", GetLastError());
-            EmulatorTerminate();
-            return;
-        }
-    }
-    else
-    {
-        /* Enter graphics mode */
-        if (!VgaEnterGraphicsMode(&CurrResolution))
-        {
-            DisplayMessage(L"An unexpected VGA error occurred while switching into graphics mode. Error: %u", GetLastError());
-            EmulatorTerminate();
-            return;
-        }
-    }
+    /* Change the screen mode */
+    if (!VgaEnterNewMode(NewScreenMode, &CurrResolution))
+        return;
 
 Quit:
 
@@ -1246,6 +724,25 @@ Quit:
     ModeChanged = FALSE;
 }
 
+static inline VOID VgaMarkForUpdate(SHORT Row, SHORT Column)
+{
+    /* Check if this is the first time the rectangle is updated */
+    if (!NeedsUpdate)
+    {
+        UpdateRectangle.Left = UpdateRectangle.Top = MAXSHORT;
+        UpdateRectangle.Right = UpdateRectangle.Bottom = MINSHORT;
+    }
+
+    /* Expand the rectangle to include the point */
+    UpdateRectangle.Left = min(UpdateRectangle.Left, Column);
+    UpdateRectangle.Right = max(UpdateRectangle.Right, Column);
+    UpdateRectangle.Top = min(UpdateRectangle.Top, Row);
+    UpdateRectangle.Bottom = max(UpdateRectangle.Bottom, Row);
+
+    /* Set the update request flag */
+    NeedsUpdate = TRUE;
+}
+
 static VOID VgaUpdateFramebuffer(VOID)
 {
     SHORT i, j, k;
@@ -1265,13 +762,13 @@ static VOID VgaUpdateFramebuffer(VOID)
      * If the console framebuffer is NULL, that means something
      * went wrong earlier and this is the final display refresh.
      */
-    if (ConsoleFramebuffer == NULL) return;
+    if (ActiveFramebuffer == NULL) return;
 
     /* Check if we are in text or graphics mode */
     if (ScreenMode == GRAPHICS_MODE)
     {
         /* Graphics mode */
-        PBYTE GraphicsBuffer = (PBYTE)ConsoleFramebuffer;
+        PBYTE GraphicsBuffer = (PBYTE)ActiveFramebuffer;
         DWORD InterlaceHighBit = VGA_INTERLACE_HIGH_BIT;
         SHORT X;
 
@@ -1531,7 +1028,7 @@ static VOID VgaUpdateFramebuffer(VOID)
     {
         /* Text mode */
         DWORD CurrentAddr;
-        PCHAR_CELL CharBuffer = (PCHAR_CELL)ConsoleFramebuffer;
+        PCHAR_CELL CharBuffer = (PCHAR_CELL)ActiveFramebuffer;
         CHAR_CELL CharInfo;
 
         /*
@@ -1574,9 +1071,6 @@ static VOID VgaUpdateFramebuffer(VOID)
 
 static VOID VgaUpdateTextCursor(VOID)
 {
-    COORD Position;
-    CONSOLE_CURSOR_INFO CursorInfo;
-
     BOOL CursorVisible = !(VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x20);
     BYTE CursorStart   =   VgaCrtcRegisters[VGA_CRTC_CURSOR_START_REG] & 0x1F;
     BYTE CursorEnd     =   VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG]   & 0x1F;
@@ -1589,31 +1083,11 @@ static VOID VgaUpdateTextCursor(VOID)
     /* Just return if we are not in text mode */
     if (ScreenMode != TEXT_MODE) return;
 
-    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.
-    }
-
     /* Add the cursor skew to the location */
     Location += (VgaCrtcRegisters[VGA_CRTC_CURSOR_END_REG] >> 5) & 0x03;
 
-    /* Find the coordinates of the new position */
-    Position.X = (SHORT)(Location % ScanlineSize);
-    Position.Y = (SHORT)(Location / ScanlineSize);
-
-    DPRINT("VgaUpdateTextCursor: (X = %d ; Y = %d)\n", Position.X, Position.Y);
-
-    /* Update the physical cursor */
-    SetConsoleCursorInfo(TextConsoleBuffer, &CursorInfo);
-    SetConsoleCursorPosition(TextConsoleBuffer, Position);
+    VgaConsoleUpdateTextCursor(CursorVisible, CursorStart, CursorEnd,
+                               TextSize, ScanlineSize, Location);
 
     /* Reset the cursor changed flag */
     CursorChanged = FALSE;
@@ -2119,8 +1593,6 @@ static VOID WINAPI VgaWritePort(USHORT Port, BYTE Data)
 
 static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
 {
-    HANDLE ConsoleBufferHandle = NULL;
-
     UNREFERENCED_PARAMETER(ElapsedTime);
 
     /* Set the vertical retrace cycle */
@@ -2160,32 +1632,7 @@ static VOID FASTCALL VgaVerticalRetrace(ULONGLONG ElapsedTime)
            UpdateRectangle.Right,
            UpdateRectangle.Bottom);
 
-    /* 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);
+    VgaConsoleRepaintScreen(&UpdateRectangle);
 
     /* Clear the update flag */
     NeedsUpdate = FALSE;
@@ -2249,14 +1696,6 @@ COORD VgaGetDisplayResolution(VOID)
     return Resolution;
 }
 
-BOOLEAN VgaGetDoubleVisionState(PBOOLEAN Horizontal, PBOOLEAN Vertical)
-{
-    if (GraphicsConsoleBuffer == NULL) return FALSE;
-    if (Horizontal) *Horizontal = DoubleWidth;
-    if (Vertical)   *Vertical   = DoubleHeight;
-    return TRUE;
-}
-
 VOID VgaRefreshDisplay(VOID)
 {
     VgaVerticalRetrace(0);
@@ -2403,134 +1842,9 @@ VOID VgaWriteTextModeFont(UINT FontNumber, CONST UCHAR* FontData, UINT Height)
     }
 }
 
-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();
-}
-
-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;
-
-    // VgaDetachFromConsoleInternal();
-
-    /*
-     * VgaAttachToConsoleInternal sets TextResolution
-     * to the new resolution and updates ConsoleInfo.
-     */
-    if (!VgaAttachToConsoleInternal(&TextResolution))
-    {
-        DisplayMessage(L"An unexpected error occurred!\n");
-        EmulatorTerminate();
-        return FALSE;
-    }
-
-    /* Restore the original screen buffer */
-    VgaSetActiveScreenBuffer(ScreenBufferHandle);
-    ScreenBufferHandle = NULL;
-
-    /* Restore the screen state */
-    if (ScreenMode == TEXT_MODE)
-    {
-        /* The text mode framebuffer was recreated */
-        ConsoleFramebuffer = TextFramebuffer;
-    }
-    else
-    {
-        /* The graphics mode framebuffer is unchanged */
-        ConsoleFramebuffer = OldConsoleFramebuffer;
-    }
-    OldConsoleFramebuffer = NULL;
-
-    return TRUE;
-}
-
-VOID VgaDetachFromConsole(VOID)
-{
-    VgaDetachFromConsoleInternal();
-
-    /* Save the screen state */
-    if (ScreenMode == TEXT_MODE)
-        ScreenBufferHandle = TextConsoleBuffer;
-    else
-        ScreenBufferHandle = GraphicsConsoleBuffer;
-
-    /* Reset the active framebuffer */
-    OldConsoleFramebuffer = ConsoleFramebuffer;
-    ConsoleFramebuffer = 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 */
-    VgaSetActiveScreenBuffer(TextConsoleBuffer);
-}
-
 BOOLEAN VgaInitialize(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;
+    if (!VgaConsoleInitialize(TextHandle)) return FALSE;
 
     /* Clear the SEQ, GC, CRTC and AC registers */
     RtlZeroMemory(VgaSeqRegisters , sizeof(VgaSeqRegisters ));
@@ -2542,9 +1856,6 @@ BOOLEAN VgaInitialize(HANDLE TextHandle)
     if (!VgaInitializePalette()) return FALSE;
     /***/ VgaResetPalette(); /***/
 
-    /* Switch to the text buffer, but do not enter into a text mode */
-    VgaSetActiveScreenBuffer(TextConsoleBuffer);
-
     /* Reset the sequencer */
     VgaResetSequencer();
 
@@ -2586,17 +1897,11 @@ VOID VgaCleanup(VOID)
     DestroyHardwareTimer(HSyncTimer);
 
     /* Leave the current video mode */
-    if (ScreenMode == GRAPHICS_MODE)
-        VgaLeaveGraphicsMode();
-    else
-        VgaLeaveTextMode();
+    VgaLeaveCurrentMode(); // ScreenMode
 
-    VgaDetachFromConsole();
     MemRemoveFastMemoryHook((PVOID)0xA0000, 0x20000);
 
-    CloseHandle(AnotherEvent);
-    CloseHandle(EndEvent);
-    CloseHandle(StartEvent);
+    VgaConsoleCleanup();
 }
 
 /* EOF */
index 1135742..ba6933b 100644 (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);
index 7d9a825..d17bf3c 100644 (file)
 
 #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[])
 {