diff --git a/dll/win32/kernel32/client/vdm.c b/dll/win32/kernel32/client/vdm.c index 8b50319491f..3456dab587c 100644 --- a/dll/win32/kernel32/client/vdm.c +++ b/dll/win32/kernel32/client/vdm.c @@ -1640,26 +1640,101 @@ GetVDMCurrentDirectories(DWORD cchCurDirs, PCHAR lpszzCurDirs) /* - * @unimplemented + * @implemented (undocumented) */ BOOL WINAPI -RegisterConsoleVDM ( - DWORD Unknown0, - DWORD Unknown1, - DWORD Unknown2, - DWORD Unknown3, - DWORD Unknown4, - DWORD Unknown5, - DWORD Unknown6, - DWORD Unknown7, - DWORD Unknown8, - DWORD Unknown9, - DWORD Unknown10 - ) +RegisterConsoleVDM(IN DWORD dwRegisterFlags, + IN HANDLE hStartHardwareEvent, + IN HANDLE hEndHardwareEvent, + IN HANDLE hErrorHardwareEvent, + IN DWORD dwUnusedVar, + OUT LPDWORD lpVideoStateLength, + OUT PVOID* lpVideoState, // PVIDEO_HARDWARE_STATE_HEADER* + IN PVOID lpUnusedBuffer, + IN DWORD dwUnusedBufferLength, + IN COORD dwVDMBufferSize, + OUT PVOID* lpVDMBuffer) { - STUB; - return FALSE; + BOOL Success; + CONSOLE_API_MESSAGE ApiMessage; + PCONSOLE_REGISTERVDM RegisterVDMRequest = &ApiMessage.Data.RegisterVDMRequest; + PCSR_CAPTURE_BUFFER CaptureBuffer = NULL; + + /* Set up the data to send to the Console Server */ + RegisterVDMRequest->ConsoleHandle = NtCurrentPeb()->ProcessParameters->ConsoleHandle; + RegisterVDMRequest->RegisterFlags = dwRegisterFlags; + + if (dwRegisterFlags != 0) + { + RegisterVDMRequest->StartHardwareEvent = hStartHardwareEvent; + RegisterVDMRequest->EndHardwareEvent = hEndHardwareEvent; + RegisterVDMRequest->ErrorHardwareEvent = hErrorHardwareEvent; + + RegisterVDMRequest->VDMBufferSize = dwVDMBufferSize; + +#if 0 + RegisterVDMRequest->UnusedBufferLength = dwUnusedBufferLength; + + /* Allocate a Capture Buffer */ + CaptureBuffer = CsrAllocateCaptureBuffer(1, dwUnusedBufferLength); + if (CaptureBuffer == NULL) + { + DPRINT1("CsrAllocateCaptureBuffer failed!\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + /* Capture the buffer to write */ + CsrCaptureMessageBuffer(CaptureBuffer, + (PVOID)lpUnusedBuffer, + dwUnusedBufferLength, + (PVOID*)&RegisterVDMRequest->UnusedBuffer); +#endif + } + else + { + // CaptureBuffer = NULL; + } + + /* Call the server */ + CsrClientCallServer((PCSR_API_MESSAGE)&ApiMessage, + CaptureBuffer, + CSR_CREATE_API_NUMBER(CONSRV_SERVERDLL_INDEX, ConsolepRegisterVDM), + sizeof(*RegisterVDMRequest)); + + /* Check for success */ + Success = NT_SUCCESS(ApiMessage.Status); + + /* Release the capture buffer if needed */ + if (CaptureBuffer) CsrFreeCaptureBuffer(CaptureBuffer); + + /* Retrieve the results */ + if (Success) + { + if (dwRegisterFlags != 0) + { + _SEH2_TRY + { + *lpVideoStateLength = RegisterVDMRequest->VideoStateLength; + *lpVideoState = RegisterVDMRequest->VideoState; + *lpVDMBuffer = RegisterVDMRequest->VDMBuffer; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + SetLastError(ERROR_INVALID_ACCESS); + Success = FALSE; + } + _SEH2_END; + } + } + else + { + BaseSetLastNTError(ApiMessage.Status); + } + + /* Return success status */ + return Success; } @@ -1756,9 +1831,8 @@ VDMOperationStarted(IN ULONG Unknown0) { DPRINT1("VDMOperationStarted(%d)\n", Unknown0); - return - BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler, - NULL, - 0, - Unknown0); + return BaseUpdateVDMEntry(VdmEntryUpdateControlCHandler, + NULL, + 0, + Unknown0); } diff --git a/include/psdk/wincon.h b/include/psdk/wincon.h index c44e478dab0..2df5ef345a0 100644 --- a/include/psdk/wincon.h +++ b/include/psdk/wincon.h @@ -618,6 +618,12 @@ BOOL WINAPI CloseConsoleHandle(_In_ HANDLE); // BOOL WINAPI SetStdHandle(_In_ DWORD, _In_ HANDLE); /* Undocumented */ BOOL WINAPI VerifyConsoleIoHandle(_In_ HANDLE); +/* Undocumented */ +BOOL +WINAPI +RegisterConsoleVDM(_In_ DWORD, _In_ HANDLE, _In_ HANDLE, _In_ HANDLE, _In_ DWORD, + _Out_ LPDWORD, _Out_ PVOID*, _In_ PVOID, _In_ DWORD, _In_ COORD, + _Out_ PVOID*); BOOL WINAPI diff --git a/include/reactos/subsys/win/conmsg.h b/include/reactos/subsys/win/conmsg.h index 06853570269..1f047226d3d 100644 --- a/include/reactos/subsys/win/conmsg.h +++ b/include/reactos/subsys/win/conmsg.h @@ -806,6 +806,28 @@ typedef struct BOOL Ansi; } CONSOLE_GETKBDLAYOUTNAME, *PCONSOLE_GETKBDLAYOUTNAME; +typedef struct +{ + HANDLE ConsoleHandle; + ULONG RegisterFlags; + HANDLE StartHardwareEvent; + HANDLE EndHardwareEvent; + HANDLE ErrorHardwareEvent; + + /* Unused member */ + ULONG UnusedVar; + + ULONG VideoStateLength; + PVOID VideoState; // PVIDEO_HARDWARE_STATE_HEADER + + /* Unused members */ + PVOID UnusedBuffer; + ULONG UnusedBufferLength; + + COORD VDMBufferSize; + PVOID VDMBuffer; +} CONSOLE_REGISTERVDM, *PCONSOLE_REGISTERVDM; + typedef struct _CONSOLE_API_MESSAGE { PORT_MESSAGE Header; @@ -903,6 +925,9 @@ typedef struct _CONSOLE_API_MESSAGE CONSOLE_GETINPUTOUTPUTCP GetConsoleCPRequest; CONSOLE_SETINPUTOUTPUTCP SetConsoleCPRequest; CONSOLE_GETKBDLAYOUTNAME GetKbdLayoutNameRequest; + + /* Virtual DOS Machine */ + CONSOLE_REGISTERVDM RegisterVDMRequest; } Data; } CONSOLE_API_MESSAGE, *PCONSOLE_API_MESSAGE; diff --git a/win32ss/user/winsrv/consrv/condrv/conoutput.c b/win32ss/user/winsrv/consrv/condrv/conoutput.c index 42d5cbfdb36..8e159178fd1 100644 --- a/win32ss/user/winsrv/consrv/condrv/conoutput.c +++ b/win32ss/user/winsrv/consrv/condrv/conoutput.c @@ -207,6 +207,13 @@ ConDrvGetActiveScreenBuffer(IN PCONSOLE Console) /* PUBLIC DRIVER APIS *********************************************************/ +NTSTATUS NTAPI +ConDrvWriteConsoleOutputVDM(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCHAR_CELL CharInfo/*Buffer*/, + IN COORD CharInfoSize, + IN OUT PSMALL_RECT WriteRegion, + IN BOOLEAN DrawRegion); NTSTATUS NTAPI ConDrvInvalidateBitMapRect(IN PCONSOLE Console, IN PCONSOLE_SCREEN_BUFFER Buffer, @@ -218,6 +225,19 @@ ConDrvInvalidateBitMapRect(IN PCONSOLE Console, /* Validity check */ ASSERT(Console == Buffer->Header.Console); + /* In text-mode only, draw the VDM buffer if present */ + if (GetType(Buffer) == TEXTMODE_BUFFER) + { + PTEXTMODE_SCREEN_BUFFER TextBuffer = (PTEXTMODE_SCREEN_BUFFER)Buffer; + + /*Status =*/ ConDrvWriteConsoleOutputVDM(Buffer->Header.Console, + TextBuffer, + Console->VDMBuffer, + Console->VDMBufferSize, + Region, + FALSE); + } + /* If the output buffer is the current one, redraw the correct portion of the screen */ if (Buffer == Console->ActiveBuffer) TermDrawRegion(Console, Region); diff --git a/win32ss/user/winsrv/consrv/condrv/text.c b/win32ss/user/winsrv/consrv/condrv/text.c index 96e30035931..6b8dbdcbc8a 100644 --- a/win32ss/user/winsrv/consrv/condrv/text.c +++ b/win32ss/user/winsrv/consrv/condrv/text.c @@ -722,6 +722,68 @@ ConDrvWriteConsoleOutput(IN PCONSOLE Console, return STATUS_SUCCESS; } +/* + * NOTE: This function is strongly inspired by ConDrvWriteConsoleOutput... + */ +NTSTATUS NTAPI +ConDrvWriteConsoleOutputVDM(IN PCONSOLE Console, + IN PTEXTMODE_SCREEN_BUFFER Buffer, + IN PCHAR_CELL CharInfo/*Buffer*/, + IN COORD CharInfoSize, + IN OUT PSMALL_RECT WriteRegion, + IN BOOLEAN DrawRegion) +{ + SHORT X, Y; + SMALL_RECT ScreenBuffer; + PCHAR_CELL CurCharInfo; + SMALL_RECT CapturedWriteRegion; + PCHAR_INFO Ptr; + + if (Console == NULL || Buffer == NULL || CharInfo == NULL || WriteRegion == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Validity check */ + ASSERT(Console == Buffer->Header.Console); + + CapturedWriteRegion = *WriteRegion; + + /* Make sure WriteRegion is inside the screen buffer */ + ConioInitRect(&ScreenBuffer, 0, 0, + Buffer->ScreenBufferSize.Y - 1, Buffer->ScreenBufferSize.X - 1); + if (!ConioGetIntersection(&CapturedWriteRegion, &ScreenBuffer, &CapturedWriteRegion)) + { + /* + * It is okay to have a WriteRegion completely outside + * the screen buffer. No data is written then. + */ + return STATUS_SUCCESS; + } + + // CurCharInfo = CharInfo; + + for (Y = CapturedWriteRegion.Top; Y <= CapturedWriteRegion.Bottom; ++Y) + { + /**/CurCharInfo = CharInfo + Y * CharInfoSize.X + CapturedWriteRegion.Left;/**/ + + Ptr = ConioCoordToPointer(Buffer, CapturedWriteRegion.Left, Y); + for (X = CapturedWriteRegion.Left; X <= CapturedWriteRegion.Right; ++X) + { + ConsoleAnsiCharToUnicodeChar(Console, &Ptr->Char.UnicodeChar, &CurCharInfo->Char); + Ptr->Attributes = CurCharInfo->Attributes; + ++Ptr; + ++CurCharInfo; + } + } + + if (DrawRegion) TermDrawRegion(Console, &CapturedWriteRegion); + + *WriteRegion = CapturedWriteRegion; + + return STATUS_SUCCESS; +} + NTSTATUS NTAPI ConDrvWriteConsole(IN PCONSOLE Console, IN PTEXTMODE_SCREEN_BUFFER ScreenBuffer, diff --git a/win32ss/user/winsrv/consrv/include/conio.h b/win32ss/user/winsrv/consrv/include/conio.h index 90defe0ea42..955a28a3928 100644 --- a/win32ss/user/winsrv/consrv/include/conio.h +++ b/win32ss/user/winsrv/consrv/include/conio.h @@ -19,6 +19,15 @@ #define DEFAULT_POPUP_ATTRIB (FOREGROUND_BLUE | FOREGROUND_RED | \ BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY) +/* VGA character cell */ +typedef struct _CHAR_CELL +{ + CHAR Char; + BYTE Attributes; +} CHAR_CELL, *PCHAR_CELL; +C_ASSERT(sizeof(CHAR_CELL) == 2); + + /* Object type magic numbers */ typedef enum _CONSOLE_IO_OBJECT_TYPE { @@ -300,6 +309,17 @@ typedef struct _CONSOLE PCONSOLE_SCREEN_BUFFER ActiveBuffer; /* Pointer to currently active screen buffer */ UINT OutputCodePage; + /**** Per-console Virtual DOS Machine Text-mode Buffer ****/ + COORD VDMBufferSize; /* Real size of the VDM buffer, in units of ??? */ + HANDLE VDMBufferSection; /* Handle to the memory shared section for the VDM buffer */ + PVOID VDMBuffer; /* Our VDM buffer */ + PVOID ClientVDMBuffer; /* A copy of the client view of our VDM buffer */ + HANDLE VDMClientProcess; /* Handle to the client process who opened the buffer, to unmap the view */ + + HANDLE StartHardwareEvent; + HANDLE EndHardwareEvent; + HANDLE ErrorHardwareEvent; + /****************************** Other properties ******************************/ UNICODE_STRING OriginalTitle; /* Original title of console, the one defined when the console leader is launched; it never changes. Always NULL-terminated */ UNICODE_STRING Title; /* Title of console. Always NULL-terminated */ diff --git a/win32ss/user/winsrv/consrv/subsysreg.c b/win32ss/user/winsrv/consrv/subsysreg.c index 0ba10afce3a..40f974040c1 100644 --- a/win32ss/user/winsrv/consrv/subsysreg.c +++ b/win32ss/user/winsrv/consrv/subsysreg.c @@ -21,8 +21,132 @@ CSR_API(SrvRegisterConsoleVDM) { - DPRINT1("%s not yet implemented\n", __FUNCTION__); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS Status; + PCONSOLE_REGISTERVDM RegisterVDMRequest = &((PCONSOLE_API_MESSAGE)ApiMessage)->Data.RegisterVDMRequest; + PCONSOLE Console; + + DPRINT1("SrvRegisterConsoleVDM(%d)\n", RegisterVDMRequest->RegisterFlags); + + Status = ConSrvGetConsole(ConsoleGetPerProcessData(CsrGetClientThread()->Process), + &Console, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Can't get console\n"); + return Status; + } + + if (RegisterVDMRequest->RegisterFlags != 0) + { + LARGE_INTEGER SectionSize; + ULONG Size, ViewSize = 0; + HANDLE ProcessHandle; + + /* + * Remember the handle to the process so that we can close or unmap + * correctly the allocated resources when the client releases the + * screen buffer. + */ + ProcessHandle = CsrGetClientThread()->Process->ProcessHandle; + Console->VDMClientProcess = ProcessHandle; + + Console->VDMBufferSize = RegisterVDMRequest->VDMBufferSize; + + Size = Console->VDMBufferSize.X * Console->VDMBufferSize.Y + * sizeof(CHAR_CELL); + + /* + * Create a memory section for the VDM buffer, to share with the client. + */ + SectionSize.QuadPart = Size; + Status = NtCreateSection(&Console->VDMBufferSection, + SECTION_ALL_ACCESS, + NULL, + &SectionSize, + PAGE_READWRITE, + SEC_COMMIT, + NULL); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to create a shared section ; Status = %lu\n", Status); + goto Quit; + } + + /* + * Create a view for our needs. + */ + ViewSize = 0; + Console->VDMBuffer = NULL; + Status = NtMapViewOfSection(Console->VDMBufferSection, + NtCurrentProcess(), + (PVOID*)&Console->VDMBuffer, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status); + NtClose(Console->VDMBufferSection); + goto Quit; + } + + /* + * Create a view for the client. We must keep a trace of it so that + * we can unmap it when the client releases the VDM buffer. + */ + ViewSize = 0; + Console->ClientVDMBuffer = NULL; + Status = NtMapViewOfSection(Console->VDMBufferSection, + ProcessHandle, + (PVOID*)&Console->ClientVDMBuffer, + 0, + 0, + NULL, + &ViewSize, + ViewUnmap, + 0, + PAGE_READWRITE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Error: Impossible to map the shared section ; Status = %lu\n", Status); + NtUnmapViewOfSection(NtCurrentProcess(), Console->VDMBuffer); + NtClose(Console->VDMBufferSection); + goto Quit; + } + + // TODO: Duplicate the event handles. + + RegisterVDMRequest->VDMBuffer = Console->ClientVDMBuffer; + + Status = STATUS_SUCCESS; + } + else + { + /* RegisterFlags == 0 means we are unregistering the VDM */ + + // TODO: Close the duplicated handles. + + if (Console->VDMBuffer) + { + /* + * Uninitialize the graphics screen buffer + * in the reverse way we initialized it. + */ + NtUnmapViewOfSection(Console->VDMClientProcess, Console->ClientVDMBuffer); + NtUnmapViewOfSection(NtCurrentProcess(), Console->VDMBuffer); + NtClose(Console->VDMBufferSection); + } + Console->VDMBuffer = Console->ClientVDMBuffer = NULL; + + Console->VDMBufferSize.X = Console->VDMBufferSize.Y = 0; + } + +Quit: + ConSrvReleaseConsole(Console, TRUE); + return Status; } CSR_API(SrvVDMConsoleOperation)