[NTVDM]: When ntvdm crashes and we restart it in the same console, do not add fresh...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / ntvdm.c
index 54b868a..a6ac026 100644 (file)
@@ -12,9 +12,9 @@
 
 #include "ntvdm.h"
 #include "emulator.h"
-#include "cpu/cpu.h"
 
 #include "bios/bios.h"
+#include "cpu/cpu.h"
 
 #include "resource.h"
 
@@ -24,13 +24,6 @@ static HANDLE ConsoleInput  = INVALID_HANDLE_VALUE;
 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
 static DWORD  OrgConsoleInputMode, OrgConsoleOutputMode;
 
-// For DOS
-#ifndef STANDALONE
-BOOLEAN AcceptCommands = TRUE;
-HANDLE CommandThread = NULL;
-ULONG SessionId = 0;
-#endif
-
 HANDLE VdmTaskEvent = NULL;
 
 // Command line of NTVDM
@@ -85,7 +78,7 @@ AppendMenuItems(HMENU hMenu,
             if (LoadStringW(GetModuleHandle(NULL),
                             Items[i].uID,
                             szMenuString,
-                            sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
+                            ARRAYSIZE(szMenuString)) > 0)
             {
                 if (Items[i].SubMenu != NULL)
                 {
@@ -123,6 +116,21 @@ AppendMenuItems(HMENU hMenu,
     } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].wCmdID == 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)
+            return TRUE;
+    }
+    return FALSE;
+}
+
 /*static*/ VOID
 CreateVdmMenu(HANDLE ConOutHandle)
 {
@@ -131,9 +139,17 @@ CreateVdmMenu(HANDLE ConOutHandle)
                                       ID_VDM_QUIT);
     if (hConsoleMenu == NULL) return;
 
+    /* Get the position where we are going to insert our menu items */
     VdmMenuPos = GetMenuItemCount(hConsoleMenu);
-    AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
-    DrawMenuBar(GetConsoleWindow());
+    // FIXME: What happens if the menu already exist?
+    // VdmMenuPos points *after* the already existing menu!
+
+    /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
+    if (!VdmMenuExists(hConsoleMenu))
+    {
+        AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
+        DrawMenuBar(GetConsoleWindow());
+    }
 }
 
 /*static*/ VOID
@@ -169,13 +185,41 @@ static VOID ShowHideMousePointer(HANDLE ConOutHandle, BOOLEAN ShowPtr)
     if (LoadStringW(GetModuleHandle(NULL),
                     (!ShowPtr ? IDS_SHOW_MOUSE : IDS_HIDE_MOUSE),
                     szMenuString,
-                    sizeof(szMenuString) / sizeof(szMenuString[0])) > 0)
+                    ARRAYSIZE(szMenuString)) > 0)
     {
         ModifyMenu(hConsoleMenu, ID_SHOWHIDE_MOUSE,
                    MF_BYCOMMAND, ID_SHOWHIDE_MOUSE, szMenuString);
     }
 }
 
+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 ***********************************************************/
 
 VOID
@@ -198,9 +242,9 @@ DisplayMessage(LPCWSTR Format, ...)
      * an auxiliary buffer; otherwise use the static buffer.
      */
     MsgLen = _vscwprintf(Format, Parameters) + 1; // NULL-terminated
-    if (MsgLen > sizeof(StaticBuffer)/sizeof(StaticBuffer[0]))
+    if (MsgLen > ARRAYSIZE(StaticBuffer))
     {
-        Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, MsgLen * sizeof(WCHAR));
+        Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, MsgLen * sizeof(WCHAR));
         if (Buffer == NULL)
         {
             /* Allocation failed, use the static buffer and display a suitable error message */
@@ -210,7 +254,7 @@ DisplayMessage(LPCWSTR Format, ...)
         }
     }
 #else
-    MsgLen = sizeof(Buffer)/sizeof(Buffer[0]);
+    MsgLen = ARRAYSIZE(Buffer);
 #endif
 
     /* Display the message */
@@ -220,7 +264,7 @@ DisplayMessage(LPCWSTR Format, ...)
 
 #ifndef WIN2K_COMPLIANT
     /* Free the buffer if needed */
-    if (Buffer != StaticBuffer) HeapFree(GetProcessHeap(), 0, Buffer);
+    if (Buffer != StaticBuffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
 #endif
 
     va_end(Parameters);
@@ -230,21 +274,19 @@ static BOOL
 WINAPI
 ConsoleCtrlHandler(DWORD ControlType)
 {
+// HACK: Should be removed!
+#ifndef STANDALONE
+extern BOOLEAN AcceptCommands;
+extern HANDLE CommandThread;
+#endif
+
     switch (ControlType)
     {
-        case CTRL_C_EVENT:
-        case CTRL_BREAK_EVENT:
-        {
-            /* HACK: Stop the VDM */
-            DPRINT1("Ctrl-C/Break: Stop the VDM\n");
-            EmulatorTerminate();
-            break;
-        }
         case CTRL_LAST_CLOSE_EVENT:
         {
             if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
             {
-                /* Exit immediately */
+                /* Nothing runs, so exit immediately */
 #ifndef STANDALONE
                 if (CommandThread) TerminateThread(CommandThread, 0);
 #endif
@@ -253,13 +295,14 @@ ConsoleCtrlHandler(DWORD ControlType)
 #ifndef STANDALONE
             else
             {
-                /* Stop accepting new commands */
+                /* A command is running, let it run, but stop accepting new commands */
                 AcceptCommands = FALSE;
             }
 #endif
 
             break;
         }
+
         default:
         {
             /* Stop the VDM if the user logs out or closes the console */
@@ -284,7 +327,7 @@ ConsoleCleanupUI(VOID)
     DestroyVdmMenu();
 }
 
-static BOOL
+BOOL
 ConsoleAttach(VOID)
 {
     /* Save the original input and output console modes */
@@ -297,13 +340,22 @@ ConsoleAttach(VOID)
         // 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;
 }
 
-static VOID
+VOID
 ConsoleDetach(VOID)
 {
     /* Restore the original input and output console modes */
@@ -404,9 +456,60 @@ VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
     DPRINT1("Focus events not handled\n");
 }
 
+static BOOL
+LoadGlobalSettings(VOID)
+{
+// FIXME: These strings should be localized.
+#define ERROR_MEMORYVDD L"Insufficient memory to load installable Virtual Device Drivers."
+#define ERROR_REGVDD    L"Virtual Device Driver format in the registry is invalid."
+#define ERROR_LOADVDD   L"An installable Virtual Device Driver failed Dll initialization."
+
+    BOOL  Success = TRUE;
+    LONG  Error   = 0;
+
+    HKEY    hNTVDMKey;
+    LPCWSTR NTVDMKeyName   = L"SYSTEM\\CurrentControlSet\\Control\\NTVDM";
+
+    /* Try to open the NTVDM registry key */
+    Error = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                          NTVDMKeyName,
+                          0,
+                          KEY_QUERY_VALUE,
+                          &hNTVDMKey);
+    if (Error == ERROR_FILE_NOT_FOUND)
+    {
+        /* If the key just doesn't exist, don't do anything else */
+        return TRUE;
+    }
+    else if (Error != ERROR_SUCCESS)
+    {
+        /* The key exists but there was an access error: display an error and quit */
+        DisplayMessage(ERROR_REGVDD);
+        return FALSE;
+    }
+
+    /*
+     * Now we can do:
+     * - CPU core choice
+     * - Video choice
+     * - Sound choice
+     * - Mem?
+     * - ...
+     * - Standalone mode?
+     * - Debug settings
+     */
+
+// Quit:
+    RegCloseKey(hNTVDMKey);
+    return Success;
+}
+
 INT
 wmain(INT argc, WCHAR *argv[])
 {
+    NtVdmArgc = argc;
+    NtVdmArgv = argv;
+
 #ifdef STANDALONE
 
     if (argc < 2)
@@ -416,28 +519,10 @@ wmain(INT argc, WCHAR *argv[])
         return 0;
     }
 
-#else
-
-    INT i;
-    WCHAR *endptr;
-
-    /* Parse the command line arguments */
-    for (i = 1; i < argc; i++)
-    {
-        if (wcsncmp(argv[i], L"-i", 2) == 0)
-        {
-            /* This is the session ID */
-            SessionId = wcstoul(argv[i] + 2, &endptr, 10);
-
-            /* The VDM hasn't been started from a console, so quit when the task is done */
-            AcceptCommands = FALSE;
-        }
-    }
-
 #endif
 
-    NtVdmArgc = argc;
-    NtVdmArgv = argv;
+    /* Load global VDM settings */
+    LoadGlobalSettings();
 
     DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
 
@@ -480,7 +565,8 @@ Cleanup:
 
     /* Quit the VDM */
     DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");
-
+    /* Some VDDs rely on the fact that NTVDM calls ExitProcess on Windows */
+    ExitProcess(0);
     return 0;
 }