[NTVDM][KERNEL32]
[reactos.git] / subsystems / ntvdm / ntvdm.c
index 6c7099d..3576bf4 100644 (file)
  * Activate this line if you want to run NTVDM in standalone mode with:
  * ntvdm.exe <program>
  */
-#define STANDALONE
+// #define STANDALONE
 
 /* VARIABLES ******************************************************************/
 
 static HANDLE ConsoleInput  = INVALID_HANDLE_VALUE;
 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
 static DWORD  OrgConsoleInputMode, OrgConsoleOutputMode;
-static CONSOLE_CURSOR_INFO         OrgConsoleCursorInfo;
-static CONSOLE_SCREEN_BUFFER_INFO  OrgConsoleBufferInfo;
+static BOOLEAN AcceptCommands = TRUE;
 static HANDLE CommandThread = NULL;
 
 static HMENU hConsoleMenu  = NULL;
 static INT   VdmMenuPos    = -1;
 static BOOLEAN ShowPointer = FALSE;
 
+ULONG SessionId = 0;
+HANDLE VdmTaskEvent = NULL;
+
 /*
  * Those menu helpers were taken from the GUI frontend in winsrv.dll
  */
@@ -199,7 +201,18 @@ BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType)
         }
         case CTRL_LAST_CLOSE_EVENT:
         {
-            if (CommandThread) TerminateThread(CommandThread, 0);
+            if (WaitForSingleObject(VdmTaskEvent, 0) == WAIT_TIMEOUT)
+            {
+                /* Exit immediately */
+                if (CommandThread) TerminateThread(CommandThread, 0);
+                EmulatorTerminate();
+            }
+            else
+            {
+                /* Stop accepting new commands */
+                AcceptCommands = FALSE;
+            }
+
             break;
         }
         default:
@@ -232,6 +245,9 @@ DWORD WINAPI PumpConsoleInput(LPVOID Parameter)
 
     while (VdmRunning)
     {
+        /* Make sure the task event is signaled */
+        WaitForSingleObject(VdmTaskEvent, INFINITE);
+
         /* Wait for an input record */
         if (!ReadConsoleInput(ConsoleInput, &InputRecord, 1, &Count))
         {
@@ -327,16 +343,6 @@ BOOL ConsoleInit(VOID)
         return FALSE;
     }
 
-    /* Save the original cursor and console screen buffer information */
-    if (!GetConsoleCursorInfo(ConsoleOutput, &OrgConsoleCursorInfo) ||
-        !GetConsoleScreenBufferInfo(ConsoleOutput, &OrgConsoleBufferInfo))
-    {
-        CloseHandle(ConsoleOutput);
-        CloseHandle(ConsoleInput);
-        wprintf(L"FATAL: Cannot save console cursor/screen-buffer info\n");
-        return FALSE;
-    }
-
     /* Initialize the UI */
     ConsoleInitUI();
 
@@ -345,26 +351,6 @@ BOOL ConsoleInit(VOID)
 
 VOID ConsoleCleanup(VOID)
 {
-    SMALL_RECT ConRect;
-
-    /* Restore the old screen buffer */
-    SetConsoleActiveScreenBuffer(ConsoleOutput);
-
-    /* Restore the original console size */
-    ConRect.Left   = 0;
-    ConRect.Top    = 0;
-    ConRect.Right  = ConRect.Left + OrgConsoleBufferInfo.srWindow.Right  - OrgConsoleBufferInfo.srWindow.Left;
-    ConRect.Bottom = ConRect.Top  + OrgConsoleBufferInfo.srWindow.Bottom - OrgConsoleBufferInfo.srWindow.Top ;
-    /*
-     * See the following trick explanation in vga.c:VgaEnterTextMode() .
-     */
-    SetConsoleScreenBufferSize(ConsoleOutput, OrgConsoleBufferInfo.dwSize);
-    SetConsoleWindowInfo(ConsoleOutput, TRUE, &ConRect);
-    SetConsoleScreenBufferSize(ConsoleOutput, OrgConsoleBufferInfo.dwSize);
-
-    /* Restore the original cursor shape */
-    SetConsoleCursorInfo(ConsoleOutput, &OrgConsoleCursorInfo);
-
     /* Restore the original input and output console modes */
     SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
     SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
@@ -379,6 +365,8 @@ VOID ConsoleCleanup(VOID)
 
 DWORD WINAPI CommandThreadProc(LPVOID Parameter)
 {
+    BOOLEAN First = TRUE;
+    DWORD Result;
     VDM_COMMAND_INFO CommandInfo;
     CHAR CmdLine[MAX_PATH];
     CHAR AppName[MAX_PATH];
@@ -389,13 +377,14 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
 
     UNREFERENCED_PARAMETER(Parameter);
 
-    while (TRUE)
+    while (AcceptCommands)
     {
         /* Clear the structure */
         ZeroMemory(&CommandInfo, sizeof(CommandInfo));
 
         /* Initialize the structure members */
-        CommandInfo.VDMState = VDM_NOT_LOADED;
+        CommandInfo.TaskId = SessionId;
+        CommandInfo.VDMState = VDM_FLAG_DOS;
         CommandInfo.CmdLine = CmdLine;
         CommandInfo.CmdLen = sizeof(CmdLine);
         CommandInfo.AppName = AppName;
@@ -409,27 +398,38 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
         CommandInfo.Env = Env;
         CommandInfo.EnvLen = sizeof(Env);
 
+        if (First) CommandInfo.VDMState |= VDM_FLAG_FIRST_TASK;
+
         /* Wait for the next available VDM */
         if (!GetNextVDMCommand(&CommandInfo)) break;
 
         /* Start the process from the command line */
         DPRINT1("Starting '%s'...\n", AppName);
-        if (DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
-                              AppName,
-                              CmdLine,
-                              Env,
-                              NULL,
-                              NULL) != ERROR_SUCCESS)
+
+        Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE, AppName, CmdLine, Env, NULL, NULL);
+        if (Result != ERROR_SUCCESS)
         {
-            DisplayMessage(L"Could not start '%S'", AppName);
+            DisplayMessage(L"Could not start '%S'. Error: %u", AppName, Result);
             break;
         }
 
+        /* Attach to the console */
+        if (!First) VgaAttachToConsole();
+
+        /* Perform a screen refresh */
+        VgaRefreshDisplay();
+
         /* Start simulation */
+        SetEvent(VdmTaskEvent);
         EmulatorSimulate();
 
         /* Perform another screen refresh */
         VgaRefreshDisplay();
+
+        /* Detach from the console */
+        VgaDetachFromConsole(FALSE);
+
+        First = FALSE;
     }
 
     return 0;
@@ -438,6 +438,8 @@ DWORD WINAPI CommandThreadProc(LPVOID Parameter)
 INT wmain(INT argc, WCHAR *argv[])
 {
 #ifdef STANDALONE
+
+    DWORD Result;
     CHAR ApplicationName[MAX_PATH];
     CHAR CommandLine[DOS_CMDLINE_LENGTH];
 
@@ -455,10 +457,28 @@ INT 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);
+        }
+    }
+
 #endif
 
     DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
 
+    /* Create the task event */
+    VdmTaskEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+    ASSERT(VdmTaskEvent != NULL);
+
     /* Initialize the console */
     if (!ConsoleInit())
     {
@@ -506,19 +526,22 @@ INT wmain(INT argc, WCHAR *argv[])
 #else
 
     /* Start the process from the command line */
-    DPRINT1("Starting '%s'...\n", CommandLine);
-    if (DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
-                          ApplicationName,
-                          CommandLine,
-                          GetEnvironmentStrings(),
-                          NULL,
-                          NULL) != ERROR_SUCCESS)
+    DPRINT1("Starting '%s'...\n", ApplicationName);
+
+    Result = DosLoadExecutable(DOS_LOAD_AND_EXECUTE,
+                               ApplicationName,
+                               CommandLine,
+                               GetEnvironmentStrings(),
+                               NULL,
+                               NULL);
+    if (Result != ERROR_SUCCESS)
     {
-        DisplayMessage(L"Could not start '%S'", ApplicationName);
+        DisplayMessage(L"Could not start '%S'. Error: %u", ApplicationName, Result);
         goto Cleanup;
     }
 
     /* Start simulation */
+    SetEvent(VdmTaskEvent);
     EmulatorSimulate();
 
     /* Perform another screen refresh */