1
2 /* INCLUDES *******************************************************************/
3
4 // #include "ntvdm.h"
5
6 // #define NDEBUG
7 // #include <debug.h>
8
9 // #include "emulator.h"
10 #include "resource.h"
11
12 /* VARIABLES ******************************************************************/
13
14 static HANDLE CurrentConsoleOutput = INVALID_HANDLE_VALUE;
15
16 static HANDLE ConsoleInput = INVALID_HANDLE_VALUE;
17 static HANDLE ConsoleOutput = INVALID_HANDLE_VALUE;
18 static DWORD OrgConsoleInputMode, OrgConsoleOutputMode;
19
20 HWND hConsoleWnd = NULL;
21 static HMENU hConsoleMenu = NULL;
22 static INT VdmMenuPos = -1;
23 static BOOL CaptureMouse = FALSE;
24
25 /*
26 * Those menu helpers were taken from the GUI frontend in winsrv.dll
27 */
28 typedef struct _VDM_MENUITEM
29 {
30 UINT uID;
31 const struct _VDM_MENUITEM *SubMenu;
32 UINT_PTR uCmdID;
33 } VDM_MENUITEM, *PVDM_MENUITEM;
34
35 static const VDM_MENUITEM VdmMenuItems[] =
36 {
37 { IDS_VDM_DUMPMEM_TXT, NULL, ID_VDM_DUMPMEM_TXT },
38 { IDS_VDM_DUMPMEM_BIN, NULL, ID_VDM_DUMPMEM_BIN },
39 { -1, NULL, 0 }, /* Separator */
40 // { IDS_VDM_MOUNT_FLOPPY, NULL, ID_VDM_DRIVES },
41 // { IDS_VDM_EJECT_FLOPPY, NULL, ID_VDM_DRIVES },
42 { -1, NULL, 0 }, /* Separator */
43 { IDS_VDM_QUIT , NULL, ID_VDM_QUIT },
44
45 { 0, NULL, 0 } /* End of list */
46 };
47
48 static const VDM_MENUITEM VdmMainMenuItems[] =
49 {
50 { -1, NULL, 0 }, /* Separator */
51 { IDS_CAPTURE_MOUSE, NULL, ID_CAPTURE_MOUSE }, /* "Capture mouse"; can be renamed to "Release mouse" */
52 { IDS_VDM_MENU , VdmMenuItems, 0 }, /* ReactOS VDM Menu */
53
54 { 0, NULL, 0 } /* End of list */
55 };
56
57 static VOID
AppendMenuItems(HMENU hMenu,const VDM_MENUITEM * Items)58 AppendMenuItems(HMENU hMenu,
59 const VDM_MENUITEM *Items)
60 {
61 UINT i = 0;
62 WCHAR szMenuString[256];
63 HMENU hSubMenu;
64
65 do
66 {
67 if (Items[i].uID != (UINT)-1)
68 {
69 if (LoadStringW(GetModuleHandle(NULL),
70 Items[i].uID,
71 szMenuString,
72 ARRAYSIZE(szMenuString)) > 0)
73 {
74 if (Items[i].SubMenu != NULL)
75 {
76 hSubMenu = CreatePopupMenu();
77 if (hSubMenu != NULL)
78 {
79 AppendMenuItems(hSubMenu, Items[i].SubMenu);
80
81 if (!AppendMenuW(hMenu,
82 MF_STRING | MF_POPUP,
83 (UINT_PTR)hSubMenu,
84 szMenuString))
85 {
86 DestroyMenu(hSubMenu);
87 }
88 }
89 }
90 else
91 {
92 AppendMenuW(hMenu,
93 MF_STRING,
94 Items[i].uCmdID,
95 szMenuString);
96 }
97 }
98 }
99 else
100 {
101 AppendMenuW(hMenu,
102 MF_SEPARATOR,
103 0,
104 NULL);
105 }
106 i++;
107 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
108 }
109
110 static BOOL
VdmMenuExists(HMENU hConsoleMenu)111 VdmMenuExists(HMENU hConsoleMenu)
112 {
113 INT MenuPos, i;
114 MenuPos = GetMenuItemCount(hConsoleMenu);
115
116 /* Check for the presence of one of the VDM menu items */
117 for (i = 0; i <= MenuPos; i++)
118 {
119 if (GetMenuItemID(hConsoleMenu, i) == ID_CAPTURE_MOUSE)
120 {
121 /* Set VdmMenuPos to the position of the existing menu */
122 VdmMenuPos = i - 1;
123 return TRUE;
124 }
125 }
126 return FALSE;
127 }
128
129 static VOID
UpdateVdmMenuMouse(VOID)130 UpdateVdmMenuMouse(VOID)
131 {
132 WCHAR szMenuString[256];
133
134 /* Update "Capture/Release mouse" menu item */
135 if (LoadStringW(GetModuleHandle(NULL),
136 (CaptureMouse ? IDS_RELEASE_MOUSE : IDS_CAPTURE_MOUSE),
137 szMenuString,
138 ARRAYSIZE(szMenuString)) > 0)
139 {
140 ModifyMenuW(hConsoleMenu, ID_CAPTURE_MOUSE,
141 MF_BYCOMMAND, ID_CAPTURE_MOUSE, szMenuString);
142 }
143 }
144
145 /*static*/ VOID
UpdateVdmMenuDisks(VOID)146 UpdateVdmMenuDisks(VOID)
147 {
148 UINT_PTR ItemID;
149 USHORT i;
150
151 WCHAR szNoMedia[100];
152 WCHAR szMenuString1[256], szMenuString2[256];
153
154 /* Update the disks menu items */
155
156 LoadStringW(GetModuleHandle(NULL),
157 IDS_NO_MEDIA,
158 szNoMedia,
159 ARRAYSIZE(szNoMedia));
160
161 LoadStringW(GetModuleHandle(NULL),
162 IDS_VDM_MOUNT_FLOPPY,
163 szMenuString1,
164 ARRAYSIZE(szMenuString1));
165
166 for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
167 {
168 ItemID = ID_VDM_DRIVES + (2 * i);
169
170 if (GlobalSettings.FloppyDisks[i].Length != 0 &&
171 GlobalSettings.FloppyDisks[i].Buffer &&
172 *GlobalSettings.FloppyDisks[i].Buffer != L'\0')
173 {
174 /* Update item text */
175 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, GlobalSettings.FloppyDisks[i].Buffer);
176 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
177 ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
178
179 /* Enable the eject item */
180 EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_ENABLED);
181 }
182 else
183 {
184 /* Update item text */
185 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
186 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
187 ModifyMenuW(hConsoleMenu, ItemID, MF_BYCOMMAND | MF_STRING, ItemID, szMenuString2);
188
189 /* Disable the eject item */
190 EnableMenuItem(hConsoleMenu, ItemID + 1, MF_BYCOMMAND | MF_GRAYED);
191 }
192 }
193 }
194
195 static VOID
UpdateVdmMenu(VOID)196 UpdateVdmMenu(VOID)
197 {
198 UpdateVdmMenuMouse();
199 UpdateVdmMenuDisks();
200 }
201
202 static VOID
CreateVdmMenu(HANDLE ConOutHandle)203 CreateVdmMenu(HANDLE ConOutHandle)
204 {
205 HMENU hVdmSubMenu;
206 UINT_PTR ItemID;
207 UINT Pos;
208 USHORT i;
209 WCHAR szNoMedia[100];
210 WCHAR szMenuString1[256], szMenuString2[256];
211
212 hConsoleMenu = ConsoleMenuControl(ConOutHandle,
213 ID_CAPTURE_MOUSE,
214 ID_VDM_DRIVES + (2 * ARRAYSIZE(GlobalSettings.FloppyDisks)));
215 if (hConsoleMenu == NULL) return;
216
217 /* Get the position where we are going to insert our menu items */
218 VdmMenuPos = GetMenuItemCount(hConsoleMenu);
219
220 /* Really add the menu if it doesn't already exist (in case eg. NTVDM crashed) */
221 if (!VdmMenuExists(hConsoleMenu))
222 {
223 /* Add all the menu entries */
224 AppendMenuItems(hConsoleMenu, VdmMainMenuItems);
225
226 /* Add the removable drives menu entries */
227 hVdmSubMenu = GetSubMenu(hConsoleMenu, VdmMenuPos + 2); // VdmMenuItems
228 Pos = 3; // After the 2 items and the separator in VdmMenuItems
229
230 LoadStringW(GetModuleHandle(NULL),
231 IDS_NO_MEDIA,
232 szNoMedia,
233 ARRAYSIZE(szNoMedia));
234
235 LoadStringW(GetModuleHandle(NULL),
236 IDS_VDM_MOUNT_FLOPPY,
237 szMenuString1,
238 ARRAYSIZE(szMenuString1));
239
240 /* Drive 'x' -- Mount */
241 for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
242 {
243 ItemID = ID_VDM_DRIVES + (2 * i);
244
245 /* Add the item */
246 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i, szNoMedia);
247 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
248 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID, szMenuString2);
249 }
250
251 LoadStringW(GetModuleHandle(NULL),
252 IDS_VDM_EJECT_FLOPPY,
253 szMenuString1,
254 ARRAYSIZE(szMenuString1));
255
256 /* Drive 'x' -- Eject */
257 for (i = 0; i < ARRAYSIZE(GlobalSettings.FloppyDisks); ++i)
258 {
259 ItemID = ID_VDM_DRIVES + (2 * i);
260
261 /* Add the item */
262 _snwprintf(szMenuString2, ARRAYSIZE(szMenuString2), szMenuString1, i);
263 szMenuString2[ARRAYSIZE(szMenuString2) - 1] = UNICODE_NULL;
264 InsertMenuW(hVdmSubMenu, Pos++, MF_STRING | MF_BYPOSITION, ItemID + 1, szMenuString2);
265 }
266
267 /* Refresh the menu state */
268 UpdateVdmMenu();
269 DrawMenuBar(hConsoleWnd);
270 }
271 }
272
273 static VOID
DestroyVdmMenu(VOID)274 DestroyVdmMenu(VOID)
275 {
276 UINT i = 0;
277 const VDM_MENUITEM *Items = VdmMainMenuItems;
278
279 do
280 {
281 DeleteMenu(hConsoleMenu, VdmMenuPos, MF_BYPOSITION);
282 i++;
283 } while (!(Items[i].uID == 0 && Items[i].SubMenu == NULL && Items[i].uCmdID == 0));
284
285 DrawMenuBar(hConsoleWnd);
286 }
287
CaptureMousePointer(HANDLE ConOutHandle,BOOLEAN Capture)288 static VOID CaptureMousePointer(HANDLE ConOutHandle, BOOLEAN Capture)
289 {
290 static BOOL IsClipped = FALSE; // For debugging purposes
291 UNREFERENCED_PARAMETER(IsClipped);
292
293 if (Capture)
294 {
295 RECT rcClip;
296
297 // if (IsClipped) return;
298
299 /* Be sure the cursor will be hidden */
300 while (ShowConsoleCursor(ConOutHandle, FALSE) >= 0) ;
301
302 GetClientRect(hConsoleWnd, &rcClip);
303 MapWindowPoints(hConsoleWnd, HWND_DESKTOP /*NULL*/, (LPPOINT)&rcClip, 2 /* Magic value when the LPPOINT parameter is a RECT */);
304 IsClipped = ClipCursor(&rcClip);
305 }
306 else
307 {
308 // if (!IsClipped) return;
309
310 ClipCursor(NULL);
311 IsClipped = FALSE;
312
313 /* Be sure the cursor will be shown */
314 while (ShowConsoleCursor(ConOutHandle, TRUE) < 0) ;
315 }
316 }
317
EnableExtraHardware(HANDLE ConsoleInput)318 static VOID EnableExtraHardware(HANDLE ConsoleInput)
319 {
320 DWORD ConInMode;
321
322 if (GetConsoleMode(ConsoleInput, &ConInMode))
323 {
324 #if 0
325 // GetNumberOfConsoleMouseButtons();
326 // GetSystemMetrics(SM_CMOUSEBUTTONS);
327 // GetSystemMetrics(SM_MOUSEPRESENT);
328 if (MousePresent)
329 {
330 #endif
331 /* Support mouse input events if there is a mouse on the system */
332 ConInMode |= ENABLE_MOUSE_INPUT;
333 #if 0
334 }
335 else
336 {
337 /* Do not support mouse input events if there is no mouse on the system */
338 ConInMode &= ~ENABLE_MOUSE_INPUT;
339 }
340 #endif
341
342 SetConsoleMode(ConsoleInput, ConInMode);
343 }
344 }
345
346
347
348
349
350
351
352 /* PUBLIC FUNCTIONS ***********************************************************/
353
354 /*static*/ VOID
355 VdmShutdown(BOOLEAN Immediate);
356
357 static BOOL
358 WINAPI
ConsoleCtrlHandler(DWORD ControlType)359 ConsoleCtrlHandler(DWORD ControlType)
360 {
361 switch (ControlType)
362 {
363 case CTRL_LAST_CLOSE_EVENT:
364 {
365 /* Delayed shutdown */
366 DPRINT1("NTVDM delayed killing in the CTRL_LAST_CLOSE_EVENT CtrlHandler!\n");
367 VdmShutdown(FALSE);
368 break;
369 }
370
371 default:
372 {
373 /* Stop the VDM if the user logs out or closes the console */
374 DPRINT1("Killing NTVDM in the 'default' CtrlHandler!\n");
375 VdmShutdown(TRUE);
376 }
377 }
378 return TRUE;
379 }
380
381 static VOID
ConsoleInitUI(VOID)382 ConsoleInitUI(VOID)
383 {
384 hConsoleWnd = GetConsoleWindow();
385 CreateVdmMenu(ConsoleOutput);
386 }
387
388 static VOID
ConsoleCleanupUI(VOID)389 ConsoleCleanupUI(VOID)
390 {
391 /* Display again properly the mouse pointer */
392 if (CaptureMouse) CaptureMousePointer(ConsoleOutput, !CaptureMouse);
393
394 DestroyVdmMenu();
395 }
396
397 BOOL
ConsoleAttach(VOID)398 ConsoleAttach(VOID)
399 {
400 /* Save the original input and output console modes */
401 if (!GetConsoleMode(ConsoleInput , &OrgConsoleInputMode ) ||
402 !GetConsoleMode(ConsoleOutput, &OrgConsoleOutputMode))
403 {
404 CloseHandle(ConsoleOutput);
405 CloseHandle(ConsoleInput);
406 wprintf(L"FATAL: Cannot save console in/out modes\n");
407 return FALSE;
408 }
409
410 /* Set the console input mode */
411 SetConsoleMode(ConsoleInput, ENABLE_WINDOW_INPUT);
412 EnableExtraHardware(ConsoleInput);
413
414 /* Set the console output mode */
415 // SetConsoleMode(ConsoleOutput, ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT);
416
417 /* Initialize the UI */
418 ConsoleInitUI();
419
420 return TRUE;
421 }
422
423 VOID
ConsoleDetach(VOID)424 ConsoleDetach(VOID)
425 {
426 /* Cleanup the UI */
427 ConsoleCleanupUI();
428
429 /* Restore the original input and output console modes */
430 SetConsoleMode(ConsoleOutput, OrgConsoleOutputMode);
431 SetConsoleMode(ConsoleInput , OrgConsoleInputMode );
432 }
433
434 VOID
ConsoleReattach(HANDLE ConOutHandle)435 ConsoleReattach(HANDLE ConOutHandle)
436 {
437 DestroyVdmMenu();
438 CurrentConsoleOutput = ConOutHandle;
439 CreateVdmMenu(ConOutHandle);
440
441 /* Synchronize mouse cursor display with console screenbuffer switches */
442 CaptureMousePointer(CurrentConsoleOutput, CaptureMouse);
443 }
444
445 static BOOL
ConsoleInit(VOID)446 ConsoleInit(VOID)
447 {
448 /* Set the handler routine */
449 SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
450
451 /* Enable the CTRL_LAST_CLOSE_EVENT */
452 SetLastConsoleEventActive();
453
454 /*
455 * NOTE: The CONIN$ and CONOUT$ "virtual" files
456 * always point to non-redirected console handles.
457 */
458
459 /* Get the input handle to the real console, and check for success */
460 ConsoleInput = CreateFileW(L"CONIN$",
461 GENERIC_READ | GENERIC_WRITE,
462 FILE_SHARE_READ | FILE_SHARE_WRITE,
463 NULL,
464 OPEN_EXISTING,
465 0,
466 NULL);
467 if (ConsoleInput == INVALID_HANDLE_VALUE)
468 {
469 wprintf(L"FATAL: Cannot retrieve a handle to the console input\n");
470 return FALSE;
471 }
472
473 /* Get the output handle to the real console, and check for success */
474 ConsoleOutput = CreateFileW(L"CONOUT$",
475 GENERIC_READ | GENERIC_WRITE,
476 FILE_SHARE_READ | FILE_SHARE_WRITE,
477 NULL,
478 OPEN_EXISTING,
479 0,
480 NULL);
481 if (ConsoleOutput == INVALID_HANDLE_VALUE)
482 {
483 CloseHandle(ConsoleInput);
484 wprintf(L"FATAL: Cannot retrieve a handle to the console output\n");
485 return FALSE;
486 }
487
488 /* Effectively attach to the console */
489 return ConsoleAttach();
490 }
491
492 static VOID
ConsoleCleanup(VOID)493 ConsoleCleanup(VOID)
494 {
495 /* Detach from the console */
496 ConsoleDetach();
497
498 /* Close the console handles */
499 if (ConsoleOutput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleOutput);
500 if (ConsoleInput != INVALID_HANDLE_VALUE) CloseHandle(ConsoleInput);
501 }
502
IsConsoleHandle(HANDLE hHandle)503 BOOL IsConsoleHandle(HANDLE hHandle)
504 {
505 DWORD dwMode;
506
507 /* Check whether the handle may be that of a console... */
508 if ((GetFileType(hHandle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR)
509 return FALSE;
510
511 /*
512 * It may be. Perform another test... The idea comes from the
513 * MSDN description of the WriteConsole API:
514 *
515 * "WriteConsole fails if it is used with a standard handle
516 * that is redirected to a file. If an application processes
517 * multilingual output that can be redirected, determine whether
518 * the output handle is a console handle (one method is to call
519 * the GetConsoleMode function and check whether it succeeds).
520 * If the handle is a console handle, call WriteConsole. If the
521 * handle is not a console handle, the output is redirected and
522 * you should call WriteFile to perform the I/O."
523 */
524 return GetConsoleMode(hHandle, &dwMode);
525 }
526
MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)527 VOID MenuEventHandler(PMENU_EVENT_RECORD MenuEvent)
528 {
529 switch (MenuEvent->dwCommandId)
530 {
531 /*
532 * System-defined menu commands
533 */
534
535 case WM_INITMENU:
536 case WM_MENUSELECT:
537 {
538 /*
539 * If the mouse is captured, release it or recapture it
540 * when the menu opens or closes, respectively.
541 */
542 if (!CaptureMouse) break;
543 CaptureMousePointer(CurrentConsoleOutput, MenuEvent->dwCommandId == WM_INITMENU ? FALSE : TRUE);
544 break;
545 }
546
547
548 /*
549 * User-defined menu commands
550 */
551
552 case ID_CAPTURE_MOUSE:
553 CaptureMouse = !CaptureMouse;
554 CaptureMousePointer(CurrentConsoleOutput, CaptureMouse);
555 UpdateVdmMenuMouse();
556 break;
557
558 case ID_VDM_DUMPMEM_TXT:
559 DumpMemory(TRUE);
560 break;
561
562 case ID_VDM_DUMPMEM_BIN:
563 DumpMemory(FALSE);
564 break;
565
566 /* Drive 0 -- Mount */
567 /* Drive 1 -- Mount */
568 case ID_VDM_DRIVES + 0:
569 case ID_VDM_DRIVES + 2:
570 {
571 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES) / 2;
572 MountFloppy(DiskNumber);
573 break;
574 }
575
576 /* Drive 0 -- Eject */
577 /* Drive 1 -- Eject */
578 case ID_VDM_DRIVES + 1:
579 case ID_VDM_DRIVES + 3:
580 {
581 ULONG DiskNumber = (MenuEvent->dwCommandId - ID_VDM_DRIVES - 1) / 2;
582 EjectFloppy(DiskNumber);
583 break;
584 }
585
586 case ID_VDM_QUIT:
587 /* Stop the VDM */
588 // EmulatorTerminate();
589
590 /* Nothing runs, so exit immediately */
591 DPRINT1("Killing NTVDM via console menu!\n");
592 VdmShutdown(TRUE);
593 break;
594
595 default:
596 break;
597 }
598 }
599
FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)600 VOID FocusEventHandler(PFOCUS_EVENT_RECORD FocusEvent)
601 {
602 /*
603 * If the mouse is captured, release it or recapture it
604 * when we lose or regain focus, respectively.
605 */
606 if (!CaptureMouse) return;
607 CaptureMousePointer(CurrentConsoleOutput, FocusEvent->bSetFocus);
608 }
609