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 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 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 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 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 196 UpdateVdmMenu(VOID) 197 { 198 UpdateVdmMenuMouse(); 199 UpdateVdmMenuDisks(); 200 } 201 202 static VOID 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 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 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 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 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 382 ConsoleInitUI(VOID) 383 { 384 hConsoleWnd = GetConsoleWindow(); 385 CreateVdmMenu(ConsoleOutput); 386 } 387 388 static 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 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 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 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 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 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 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 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 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