Implement a basic passthrough mode

* [ ] Doesn't immediately stop a frame when passthrough mode is entered.
  * [ ] Exiting passthrough will leave us in a torn state
  * [ ] Pretty sure that's the wrong place to put that flag

  Otherwise this was scary easy
This commit is contained in:
Mike Griese 2019-07-19 10:24:29 -05:00
parent 5074335392
commit f5939ebd61
11 changed files with 169 additions and 2 deletions

View file

@ -397,3 +397,14 @@ void VtIo::_ShutdownIfNeeded()
ServiceLocator::RundownAndExit(ERROR_BROKEN_PIPE);
}
}
void VtIo::SetPassthroughMode(const bool enable)
{
_pVtRenderEngine->SetPassthroughMode(enable);
}
void VtIo::PassthroughString(std::wstring_view view)
{
std::wstring wstr{ view };
// LOG_IF_FAILED(_pVtRenderEngine->WriteTerminalW(wstr));
_pVtRenderEngine->PassthroughString(wstr);
}

View file

@ -36,6 +36,9 @@ namespace Microsoft::Console::VirtualTerminal
void CloseInput() override;
void CloseOutput() override;
void SetPassthroughMode(const bool enable);
void PassthroughString(std::wstring_view view);
private:
// After CreateIoHandlers is called, these will be invalid.
wil::unique_hfile _hInput;

View file

@ -941,6 +941,16 @@ using Microsoft::Console::VirtualTerminal::StateMachine;
const DWORD dwFlags,
_Inout_opt_ PSHORT const psScrollY)
{
if (WI_IsFlagSet(screenInfo.OutputMode, ENABLE_PASSTHROUGH_MODE))
{
const size_t BufferSize = *pcb;
const size_t cch = BufferSize / sizeof(WCHAR);
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.GetVtIo()->PassthroughString({ pwchRealUnicode, cch });
*pcb += BufferSize;
return STATUS_SUCCESS;
}
if (!WI_IsFlagSet(screenInfo.OutputMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING) ||
!WI_IsFlagSet(screenInfo.OutputMode, ENABLE_PROCESSED_OUTPUT))
{

View file

@ -26,7 +26,7 @@
#define VALID_TEXT_ATTRIBUTES (FG_ATTRS | BG_ATTRS | META_ATTRS)
#define INPUT_MODES (ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT | ENABLE_ECHO_INPUT | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT)
#define OUTPUT_MODES (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN | ENABLE_LVB_GRID_WORLDWIDE)
#define OUTPUT_MODES (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN | ENABLE_LVB_GRID_WORLDWIDE | ENABLE_PASSTHROUGH_MODE)
#define PRIVATE_MODES (ENABLE_INSERT_MODE | ENABLE_QUICK_EDIT_MODE | ENABLE_AUTO_POSITION | ENABLE_EXTENDED_FLAGS)
using namespace Microsoft::Console::Types;
@ -394,7 +394,14 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept
SCREEN_INFORMATION& screenInfo = context.GetActiveBuffer();
const DWORD dwOldMode = screenInfo.OutputMode;
const DWORD dwNewMode = mode;
DWORD preprocessNewMode = mode;
if (!gci.IsInVtIoMode() &&
(WI_IsFlagSet(preprocessNewMode, ENABLE_PASSTHROUGH_MODE)))
{
WI_ClearFlag(preprocessNewMode, ENABLE_PASSTHROUGH_MODE);
}
const DWORD dwNewMode = preprocessNewMode;
screenInfo.OutputMode = dwNewMode;
@ -413,6 +420,22 @@ void ApiRoutines::GetNumberOfConsoleMouseButtonsImpl(ULONG& buttons) noexcept
screenInfo.SetDefaultVtTabStops();
}
if (gci.IsInVtIoMode())
{
// if we're moving from passthrough on->off
if (WI_IsFlagClear(dwNewMode, ENABLE_PASSTHROUGH_MODE) &&
WI_IsFlagSet(dwOldMode, ENABLE_PASSTHROUGH_MODE))
{
gci.GetVtIo()->SetPassthroughMode(false);
}
// if we're moving from passthrough off->on
else if (WI_IsFlagSet(dwNewMode, ENABLE_PASSTHROUGH_MODE) &&
WI_IsFlagClear(dwOldMode, ENABLE_PASSTHROUGH_MODE))
{
gci.GetVtIo()->SetPassthroughMode(true);
}
}
gci.SetVirtTermLevel(WI_IsFlagSet(dwNewMode, ENABLE_VIRTUAL_TERMINAL_PROCESSING) ? 1 : 0);
gci.SetAutomaticReturnOnNewline(WI_IsFlagSet(screenInfo.OutputMode, DISABLE_NEWLINE_AUTO_RETURN) ? false : true);
gci.SetGridRenderingAllowedWorldwide(WI_IsFlagSet(screenInfo.OutputMode, ENABLE_LVB_GRID_WORLDWIDE));

View file

@ -71,6 +71,9 @@ Revision History:
#define CONSOLE_SUSPENDED (CONSOLE_OUTPUT_SUSPENDED)
// clang-format on
// ENABLE_LVB_GRID_WORLDWIDE is 0x0010
#define ENABLE_PASSTHROUGH_MODE 0x0020
class COOKED_READ_DATA;
class CommandHistory;

View file

@ -24,6 +24,10 @@ using namespace Microsoft::Console::Types;
{
return S_FALSE;
}
if (_passthroughMode)
{
return S_FALSE;
}
// If there's nothing to do, quick return
bool somethingToDo = _fInvalidRectUsed ||

View file

@ -417,3 +417,14 @@ HRESULT VtEngine::RequestCursor() noexcept
RETURN_IF_FAILED(_Flush());
return S_OK;
}
void VtEngine::SetPassthroughMode(const bool enable)
{
_passthroughMode = enable;
}
void VtEngine::PassthroughString(const std::wstring& wstr)
{
LOG_IF_FAILED(WriteTerminalW(wstr));
LOG_IF_FAILED(_Flush());
}

View file

@ -95,6 +95,9 @@ namespace Microsoft::Console::Render
void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner);
void SetPassthroughMode(const bool enable);
void PassthroughString(const std::wstring& wstr);
protected:
wil::unique_hfile _hFile;
std::string _buffer;
@ -133,6 +136,8 @@ namespace Microsoft::Console::Render
Microsoft::Console::VirtualTerminal::RenderTracing _trace;
bool _passthroughMode{ false };
[[nodiscard]] HRESULT _Write(std::string_view const str) noexcept;
[[nodiscard]] HRESULT _WriteFormattedString(const std::string* const pFormat, ...) noexcept;
[[nodiscard]] HRESULT _Flush() noexcept;

View file

@ -2,9 +2,77 @@
// Licensed under the MIT license.
#include <windows.h>
#include <wil\Common.h>
#include <wil\result.h>
#include <wil\resource.h>
#include <wil\wistd_functional.h>
#include <wil\wistd_memory.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include <conio.h>
#include <deque>
#include <memory>
#include <vector>
#include <string>
#include <sstream>
#include <assert.h>
#define ENABLE_PASSTHROUGH_MODE 0x0020
std::string csi(std::string seq)
{
std::string fullSeq = "\x1b[";
fullSeq += seq;
return fullSeq;
}
std::string osc(std::string seq)
{
std::string fullSeq = "\x1b]";
fullSeq += seq;
fullSeq += "\x7";
return fullSeq;
}
// This wmain exists for help in writing scratch programs while debugging.
int __cdecl wmain(int /*argc*/, WCHAR* /*argv[]*/)
{
wprintf(L"Attempting to start passthrough mode...\n");
auto hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
THROW_LAST_ERROR_IF(!GetConsoleMode(hOut, &dwMode));
wprintf(L"Original Mode: 0x%x\n", dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
dwMode |= DISABLE_NEWLINE_AUTO_RETURN;
dwMode |= ENABLE_PASSTHROUGH_MODE;
wprintf(L"Requested Mode: 0x%x\n", dwMode);
THROW_LAST_ERROR_IF(!SetConsoleMode(hOut, dwMode));
DWORD roundtripMode = 0;
THROW_LAST_ERROR_IF(!GetConsoleMode(hOut, &roundtripMode));
wprintf(L"Rountripped Mode: 0x%x\n", dwMode);
if (roundtripMode != dwMode)
{
wprintf(L"Mode did not rountrip\n");
}
else
{
wprintf(L"Mode rountripped successfully\n");
}
wprintf(L"Press a key to continue\n");
_getch();
wprintf(L"We're going to write some VT straight to the terminal\n");
printf(csi("31m").c_str());
printf(osc("0;Title:foo").c_str());
wprintf(L"Press a key to continue\n");
_getch();
return 0;
}

5
tools/bzcon.cmd Normal file
View file

@ -0,0 +1,5 @@
@echo off
"%msbuild%" Openconsole.sln /t:Conhost\Host_EXE /m /p:Configuration=Debug /p:Platform=%ARCH%
:eof

24
tools/scratch.cmd Normal file
View file

@ -0,0 +1,24 @@
@echo off
rem openvt - launch the vtterm binary
rem Runs the VtPipeTerm.exe binary generated by the build in the debug directory.
rem Passes any args along.
setlocal
set _last_build=%OPENCON%\bin\%ARCH%\%_LAST_BUILD_CONF%
if not exist %_last_build%\scratch.exe (
echo Could not locate the scratch.exe in %_last_build%. Double check that it has been built and try again.
goto :eof
)
set _r=%random%
set copy_dir=OpenConsole\%_r%
rem Generate a unique name, so that we can debug multiple revisions of the binary at the same time if needed.
(xcopy /Y %_last_build%\OpenConsole.exe %TEMP%\%copy_dir%\conhost.exe*) > nul
(xcopy /Y %_last_build%\console.dll %TEMP%\%copy_dir%\console.dll*) > nul
(xcopy /Y %_last_build%\scratch.exe %TEMP%\%copy_dir%\scratch.exe*) > nul
echo Launching %TEMP%\%copy_dir%\scratch.exe...
%TEMP%\%copy_dir%\scratch.exe