2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
5 * PURPOSE: Virtual DOS Machine
6 * PROGRAMMERS: Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
9 /* INCLUDES *******************************************************************/
17 #include "hardware/ps2.h"
18 #include "hardware/vga.h"
19 #include "bios/bios.h"
25 * Activate this line if you want to run NTVDM in standalone mode with:
30 /* VARIABLES ******************************************************************/
32 static HANDLE ConsoleInput
= INVALID_HANDLE_VALUE
;
33 static HANDLE ConsoleOutput
= INVALID_HANDLE_VALUE
;
34 static DWORD OrgConsoleInputMode
, OrgConsoleOutputMode
;
35 static CONSOLE_CURSOR_INFO OrgConsoleCursorInfo
;
36 static CONSOLE_SCREEN_BUFFER_INFO OrgConsoleBufferInfo
;
37 static HANDLE CommandThread
= NULL
;
39 static HMENU hConsoleMenu
= NULL
;
40 static INT VdmMenuPos
= -1;
41 static BOOLEAN ShowPointer
= FALSE
;
44 * Those menu helpers were taken from the GUI frontend in winsrv.dll
46 typedef struct _VDM_MENUITEM
49 const struct _VDM_MENUITEM
*SubMenu
;
51 } VDM_MENUITEM
, *PVDM_MENUITEM
;
53 static const VDM_MENUITEM VdmMenuItems
[] =
55 { IDS_VDM_QUIT
, NULL
, ID_VDM_QUIT
},
57 { 0, NULL
, 0 } /* End of list */
60 static const VDM_MENUITEM VdmMainMenuItems
[] =
62 { -1, NULL
, 0 }, /* Separator */
63 { IDS_HIDE_MOUSE
, NULL
, ID_SHOWHIDE_MOUSE
}, /* Hide mouse; can be renamed to Show mouse */
64 { IDS_VDM_MENU
, VdmMenuItems
, 0 }, /* ReactOS VDM Menu */
66 { 0, NULL
, 0 } /* End of list */
70 AppendMenuItems(HMENU hMenu
,
71 const VDM_MENUITEM
*Items
)
74 WCHAR szMenuString
[255];
79 if (Items
[i
].uID
!= (UINT
)-1)
81 if (LoadStringW(GetModuleHandle(NULL
),
84 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
86 if (Items
[i
].SubMenu
!= NULL
)
88 hSubMenu
= CreatePopupMenu();
91 AppendMenuItems(hSubMenu
, Items
[i
].SubMenu
);
93 if (!AppendMenuW(hMenu
,
98 DestroyMenu(hSubMenu
);
119 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
123 CreateVdmMenu(HANDLE ConOutHandle
)
125 hConsoleMenu
= ConsoleMenuControl(ConsoleOutput
,
128 if (hConsoleMenu
== NULL
) return;
130 VdmMenuPos
= GetMenuItemCount(hConsoleMenu
);
131 AppendMenuItems(hConsoleMenu
, VdmMainMenuItems
);
132 DrawMenuBar(GetConsoleWindow());
139 const VDM_MENUITEM
*Items
= VdmMainMenuItems
;
143 DeleteMenu(hConsoleMenu
, VdmMenuPos
, MF_BYPOSITION
);
145 } while (!(Items
[i
].uID
== 0 && Items
[i
].SubMenu
== NULL
&& Items
[i
].wCmdID
== 0));
147 DrawMenuBar(GetConsoleWindow());
150 static VOID
ShowHideMousePointer(HANDLE ConOutHandle
, BOOLEAN ShowPtr
)
152 WCHAR szMenuString
[255] = L
"";
156 /* Be sure the cursor will be shown */
157 while (ShowConsoleCursor(ConOutHandle
, TRUE
) < 0) ;
161 /* Be sure the cursor will be hidden */
162 while (ShowConsoleCursor(ConOutHandle
, FALSE
) >= 0) ;
165 if (LoadStringW(GetModuleHandle(NULL
),
166 (!ShowPtr
? IDS_SHOW_MOUSE
: IDS_HIDE_MOUSE
),
168 sizeof(szMenuString
) / sizeof(szMenuString
[0])) > 0)
170 ModifyMenu(hConsoleMenu
, ID_SHOWHIDE_MOUSE
,
171 MF_BYCOMMAND
, ID_SHOWHIDE_MOUSE
, szMenuString
);
175 /* PUBLIC FUNCTIONS ***********************************************************/
177 VOID
DisplayMessage(LPCWSTR Format
, ...)
182 va_start(Parameters
, Format
);
183 _vsnwprintf(Buffer
, 256, Format
, Parameters
);
184 DPRINT1("\n\nNTVDM Subsystem\n%S\n\n", Buffer
);
185 MessageBoxW(NULL
, Buffer
, L
"NTVDM Subsystem", MB_OK
);
189 BOOL WINAPI
ConsoleCtrlHandler(DWORD ControlType
)
194 case CTRL_BREAK_EVENT
:
197 EmulatorInterrupt(0x23);
200 case CTRL_LAST_CLOSE_EVENT
:
202 if (CommandThread
) TerminateThread(CommandThread
, 0);
207 /* Stop the VDM if the user logs out or closes the console */
214 VOID
ConsoleInitUI(VOID
)
216 CreateVdmMenu(ConsoleOutput
);
219 VOID
ConsoleCleanupUI(VOID
)
221 /* Display again properly the mouse pointer */
222 if (ShowPointer
) ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
227 DWORD WINAPI
PumpConsoleInput(LPVOID Parameter
)
229 HANDLE ConsoleInput
= (HANDLE
)Parameter
;
230 INPUT_RECORD InputRecord
;
235 /* Wait for an input record */
236 if (!ReadConsoleInput(ConsoleInput
, &InputRecord
, 1, &Count
))
238 DWORD LastError
= GetLastError();
239 DPRINT1("Error reading console input (0x%p, %lu) - Error %lu\n", ConsoleInput
, Count
, LastError
);
245 /* Check the event type */
246 switch (InputRecord
.EventType
)
250 /* Send it to the PS/2 controller */
251 PS2Dispatch(&InputRecord
);
256 switch (InputRecord
.Event
.MenuEvent
.dwCommandId
)
258 case ID_SHOWHIDE_MOUSE
:
259 ShowHideMousePointer(ConsoleOutput
, ShowPointer
);
260 ShowPointer
= !ShowPointer
;
283 BOOL
ConsoleInit(VOID
)
285 /* Set the handler routine */
286 SetConsoleCtrlHandler(ConsoleCtrlHandler
, TRUE
);
288 /* Enable the CTRL_LAST_CLOSE_EVENT */
289 SetLastConsoleEventActive();
291 /* Get the input handle to the real console, and check for success */
292 ConsoleInput
= CreateFileW(L
"CONIN$",
293 GENERIC_READ
| GENERIC_WRITE
,
294 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
299 if (ConsoleInput
== INVALID_HANDLE_VALUE
)
301 wprintf(L
"FATAL: Cannot retrieve a handle to the console input\n");
305 /* Get the output handle to the real console, and check for success */
306 ConsoleOutput
= CreateFileW(L
"CONOUT$",
307 GENERIC_READ
| GENERIC_WRITE
,
308 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
313 if (ConsoleOutput
== INVALID_HANDLE_VALUE
)
315 CloseHandle(ConsoleInput
);
316 wprintf(L
"FATAL: Cannot retrieve a handle to the console output\n");
320 /* Save the original input and output console modes */
321 if (!GetConsoleMode(ConsoleInput
, &OrgConsoleInputMode
) ||
322 !GetConsoleMode(ConsoleOutput
, &OrgConsoleOutputMode
))
324 CloseHandle(ConsoleOutput
);
325 CloseHandle(ConsoleInput
);
326 wprintf(L
"FATAL: Cannot save console in/out modes\n");
330 /* Save the original cursor and console screen buffer information */
331 if (!GetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
) ||
332 !GetConsoleScreenBufferInfo(ConsoleOutput
, &OrgConsoleBufferInfo
))
334 CloseHandle(ConsoleOutput
);
335 CloseHandle(ConsoleInput
);
336 wprintf(L
"FATAL: Cannot save console cursor/screen-buffer info\n");
340 /* Initialize the UI */
346 VOID
ConsoleCleanup(VOID
)
350 /* Restore the old screen buffer */
351 SetConsoleActiveScreenBuffer(ConsoleOutput
);
353 /* Restore the original console size */
356 ConRect
.Right
= ConRect
.Left
+ OrgConsoleBufferInfo
.srWindow
.Right
- OrgConsoleBufferInfo
.srWindow
.Left
;
357 ConRect
.Bottom
= ConRect
.Top
+ OrgConsoleBufferInfo
.srWindow
.Bottom
- OrgConsoleBufferInfo
.srWindow
.Top
;
359 * See the following trick explanation in vga.c:VgaEnterTextMode() .
361 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
362 SetConsoleWindowInfo(ConsoleOutput
, TRUE
, &ConRect
);
363 SetConsoleScreenBufferSize(ConsoleOutput
, OrgConsoleBufferInfo
.dwSize
);
365 /* Restore the original cursor shape */
366 SetConsoleCursorInfo(ConsoleOutput
, &OrgConsoleCursorInfo
);
368 /* Restore the original input and output console modes */
369 SetConsoleMode(ConsoleOutput
, OrgConsoleOutputMode
);
370 SetConsoleMode(ConsoleInput
, OrgConsoleInputMode
);
375 /* Close the console handles */
376 if (ConsoleOutput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleOutput
);
377 if (ConsoleInput
!= INVALID_HANDLE_VALUE
) CloseHandle(ConsoleInput
);
380 DWORD WINAPI
CommandThreadProc(LPVOID Parameter
)
382 VDM_COMMAND_INFO CommandInfo
;
383 CHAR CmdLine
[MAX_PATH
];
384 CHAR AppName
[MAX_PATH
];
385 CHAR PifFile
[MAX_PATH
];
386 CHAR Desktop
[MAX_PATH
];
387 CHAR Title
[MAX_PATH
];
389 UNREFERENCED_PARAMETER(Parameter
);
393 /* Clear the structure */
394 ZeroMemory(&CommandInfo
, sizeof(CommandInfo
));
396 /* Initialize the structure members */
397 CommandInfo
.VDMState
= VDM_NOT_LOADED
;
398 CommandInfo
.CmdLine
= CmdLine
;
399 CommandInfo
.CmdLen
= sizeof(CmdLine
);
400 CommandInfo
.AppName
= AppName
;
401 CommandInfo
.AppLen
= sizeof(AppName
);
402 CommandInfo
.PifFile
= PifFile
;
403 CommandInfo
.PifLen
= sizeof(PifFile
);
404 CommandInfo
.Desktop
= Desktop
;
405 CommandInfo
.DesktopLen
= sizeof(Desktop
);
406 CommandInfo
.Title
= Title
;
407 CommandInfo
.TitleLen
= sizeof(Title
);
409 /* Wait for the next available VDM */
410 if (!GetNextVDMCommand(&CommandInfo
)) break;
412 /* Start the process from the command line */
413 DPRINT1("Starting '%s'...\n", AppName
);
414 if (!DosCreateProcess(AppName
, 0))
416 DisplayMessage(L
"Could not start '%S'", AppName
);
420 /* Start simulation */
423 /* Perform another screen refresh */
430 INT
wmain(INT argc
, WCHAR
*argv
[])
434 CHAR CommandLine
[DOS_CMDLINE_LENGTH
];
436 if (argc
== 2 && argv
[1] != NULL
)
438 WideCharToMultiByte(CP_ACP
, 0, argv
[1], -1, CommandLine
, sizeof(CommandLine
), NULL
, NULL
);
442 wprintf(L
"\nReactOS Virtual DOS Machine\n\n"
443 L
"Usage: NTVDM <executable>\n");
449 DPRINT1("\n\n\nNTVDM - Starting...\n\n\n");
451 /* Initialize the console */
454 wprintf(L
"FATAL: A problem occurred when trying to initialize the console\n");
458 /* Initialize the emulator */
459 if (!EmulatorInitialize(ConsoleInput
, ConsoleOutput
))
461 wprintf(L
"FATAL: Failed to initialize the emulator\n");
465 /* Initialize the system BIOS */
466 if (!BiosInitialize(NULL
))
468 wprintf(L
"FATAL: Failed to initialize the VDM BIOS.\n");
472 /* Initialize the VDM DOS kernel */
473 if (!DosInitialize(NULL
))
475 wprintf(L
"FATAL: Failed to initialize the VDM DOS kernel.\n");
481 /* Create the GetNextVDMCommand thread */
482 CommandThread
= CreateThread(NULL
, 0, &CommandThreadProc
, NULL
, 0, NULL
);
483 if (CommandThread
== NULL
)
485 wprintf(L
"FATAL: Failed to create the command processing thread: %d\n", GetLastError());
489 /* Wait for the command thread to exit */
490 WaitForSingleObject(CommandThread
, INFINITE
);
492 /* Close the thread handle */
493 CloseHandle(CommandThread
);
497 /* Start the process from the command line */
498 DPRINT1("Starting '%s'...\n", CommandLine
);
499 if (!DosCreateProcess(CommandLine
, 0))
501 DisplayMessage(L
"Could not start '%S'", CommandLine
);
505 /* Start simulation */
508 /* Perform another screen refresh */
523 DPRINT1("\n\n\nNTVDM - Exiting...\n\n\n");