mirror of
https://github.com/microsoft/terminal.git
synced 2025-12-19 18:11:39 -05:00
The idea is that we can translate Console API calls directly to VT at least as well as the current VtEngine setup can. For instance, a call to `SetConsoleCursorPosition` clearly translates directly to a `CUP` escape sequence. Effectively, instead of translating output asynchronously in the renderer thread, we'll do it synchronously right during the Console API call. Most importantly, the this means that any VT output that an application generates will now be given to the terminal unmodified. Aside from reducing our project's complexity quite a bit and opening the path towards various interesting work like sixels, Device Control Strings, buffer snapshotting, synchronized updates, and more, it also improves performance for mixed text output like enwik8.txt in conhost to 1.3-2x and in Windows Terminal via ConPTY to roughly 20x. This adds support for overlapped IO, because now that output cannot be "skipped" anymore (VtEngine worked like a renderer after all) it's become crucial to block conhost as little as possible. ⚠️ Intentionally unresolved changes/quirks: * To force a delayed EOL wrap to wrap, `WriteCharsLegacy` emits a `\r\n` if necessary. This breaks text reflow on window resize. We cannot emit ` \r` the way readline does it, because this would overwrite the first column in the next row with a whitespace. The alternative is to read back the affected cell from the buffer and emit that character and its attributes followed by a `\r`. I chose to not do that, because buffer read-back is lossy (= UCS2). Unless the window is resized, the difference is unnoticeable and historically, conhost had no support for buffer reflow anyway. * If `ENABLE_VIRTUAL_TERMINAL_PROCESSING` is set while `DISABLE_NEWLINE_AUTO_RETURN` is reset, we'll blindly replace all LF with CRLF. This may hypothetically break DCS sequences, but it's the only way to do this without parsing the given VT string and thus the only way we can achieve passthrough mode in the future. * `ENABLE_WRAP_AT_EOL_OUTPUT` is translated to `DECAWM`. Between Windows XP and Windows 11 21H2, `ENABLE_WRAP_AT_EOL_OUTPUT` being reset would cause the cursor position to reset to wherever a write started, _if_ the write, including expanded control chars, was less than 100 characters long. If it was longer than that, the cursor position would end up in an effectively random position. After lengthy research I believe that this is a bug introduced in Windows XP and that the original intention was for this mode to be equivalent to `DECAWM`. This is compounded by MSDN's description (emphasis mine): > If this mode is disabled, the **last character** in the row is > overwritten with any subsequent characters. ⚠️ Unresolved issues/quirks: * Focus/Unfocus events are injected into the output stream without checking whether the VT output is currently in a ground state. This may break whatever VT sequence is currently ongoing. This is an existing issue. * `VtIo::Writer::WriteInfos` should properly verify the width of each individual character. * Using `SetConsoleActiveScreenBuffer` destroys surrogate pairs and extended (VT) attributes. It could be translated to VT pages in the long term. * Similarly, `ScrollConsoleScreenBuffer` results in the same and could be translated to `DECCRA` and `DECFRA` in the near term. This is important because otherwise `vim` output may loose its extended attributes during scrolling. * Reflowing a long line until it wraps results in the cooked read prompt to be misaligned vertically. * `SCREEN_INFORMATION::s_RemoveScreenBuffer` should trigger a buffer switch similar to `SetConsoleActiveScreenBuffer`. * Translation of `COMMON_LVB_GRID_HORIZONTAL` to `SGR 53` was dropped and may be reintroduced alongside `UNDERSCORE` = `SGR 4`. * Move the `OSC 0 ; P t BEL` sequence to `WriteWindowTitle` and swap the `BEL` with the `ST` (`ESC \`). * PowerShell on Windows 10 ships with PSReadLine 2.0.0-beta2 which emits SGR 37/40 instead of 39/49. This results in black spaces when typing and there's no good way to fix that. * A test is missing that ensures that `FillConsoleOutputCharacterW` results in a `CSI n J` during the PowerShell shim. * A test is missing that ensures that `PtySignal::ClearBuffer` does not result in any VT being generated. Closes #262 Closes #1173 Closes #3016 Closes #4129 Closes #5228 Closes #8698 Closes #12336 Closes #15014 Closes #15888 Closes #16461 Closes #16911 Closes #17151 Closes #17313
664 lines
21 KiB
C++
664 lines
21 KiB
C++
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT license.
|
|
|
|
#include "precomp.h"
|
|
#include "ConsoleArguments.hpp"
|
|
#include "../types/inc/utils.hpp"
|
|
#include <shellapi.h>
|
|
using namespace Microsoft::Console::Utils;
|
|
|
|
const std::wstring_view ConsoleArguments::HEADLESS_ARG = L"--headless";
|
|
const std::wstring_view ConsoleArguments::SERVER_HANDLE_ARG = L"--server";
|
|
const std::wstring_view ConsoleArguments::SIGNAL_HANDLE_ARG = L"--signal";
|
|
const std::wstring_view ConsoleArguments::HANDLE_PREFIX = L"0x";
|
|
const std::wstring_view ConsoleArguments::CLIENT_COMMANDLINE_ARG = L"--";
|
|
const std::wstring_view ConsoleArguments::FORCE_V1_ARG = L"-ForceV1";
|
|
const std::wstring_view ConsoleArguments::FORCE_NO_HANDOFF_ARG = L"-ForceNoHandoff";
|
|
const std::wstring_view ConsoleArguments::FILEPATH_LEADER_PREFIX = L"\\??\\";
|
|
const std::wstring_view ConsoleArguments::WIDTH_ARG = L"--width";
|
|
const std::wstring_view ConsoleArguments::HEIGHT_ARG = L"--height";
|
|
const std::wstring_view ConsoleArguments::INHERIT_CURSOR_ARG = L"--inheritcursor";
|
|
const std::wstring_view ConsoleArguments::FEATURE_ARG = L"--feature";
|
|
const std::wstring_view ConsoleArguments::FEATURE_PTY_ARG = L"pty";
|
|
const std::wstring_view ConsoleArguments::COM_SERVER_ARG = L"-Embedding";
|
|
static constexpr std::wstring_view GLYPH_WIDTH{ L"--textMeasurement" };
|
|
// NOTE: Thinking about adding more commandline args that control conpty, for
|
|
// the Terminal? Make sure you add them to the commandline in
|
|
// ConsoleEstablishHandoff. We use that to initialize the ConsoleArguments for a
|
|
// defterm handoff s.t. they behave like a conpty connection that was started by
|
|
// the terminal. If you forget them there, the conpty won't obey them, only for
|
|
// defterm.
|
|
|
|
std::wstring EscapeArgument(std::wstring_view ac)
|
|
{
|
|
if (ac.empty())
|
|
{
|
|
return L"\"\"";
|
|
}
|
|
auto hasSpace = false;
|
|
auto n = ac.size();
|
|
for (auto c : ac)
|
|
{
|
|
switch (c)
|
|
{
|
|
case L'"':
|
|
case L'\\':
|
|
n++;
|
|
break;
|
|
case ' ':
|
|
case '\t':
|
|
hasSpace = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (hasSpace)
|
|
{
|
|
n += 2;
|
|
}
|
|
if (n == ac.size())
|
|
{
|
|
return std::wstring{ ac };
|
|
}
|
|
std::wstring buf;
|
|
if (hasSpace)
|
|
{
|
|
buf.push_back(L'"');
|
|
}
|
|
size_t slashes = 0;
|
|
for (auto c : ac)
|
|
{
|
|
switch (c)
|
|
{
|
|
case L'\\':
|
|
slashes++;
|
|
buf.push_back(L'\\');
|
|
break;
|
|
case L'"':
|
|
{
|
|
for (; slashes > 0; slashes--)
|
|
{
|
|
buf.push_back(L'\\');
|
|
}
|
|
buf.push_back(L'\\');
|
|
buf.push_back(c);
|
|
}
|
|
break;
|
|
default:
|
|
slashes = 0;
|
|
buf.push_back(c);
|
|
break;
|
|
}
|
|
}
|
|
if (hasSpace)
|
|
{
|
|
for (; slashes > 0; slashes--)
|
|
{
|
|
buf.push_back(L'\\');
|
|
}
|
|
buf.push_back(L'"');
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
ConsoleArguments::ConsoleArguments(const std::wstring& commandline,
|
|
const HANDLE hStdIn,
|
|
const HANDLE hStdOut) :
|
|
_commandline(commandline),
|
|
_vtInHandle(hStdIn),
|
|
_vtOutHandle(hStdOut)
|
|
{
|
|
_clientCommandline = L"";
|
|
_headless = false;
|
|
_runAsComServer = false;
|
|
_createServerHandle = true;
|
|
_serverHandle = 0;
|
|
_signalHandle = 0;
|
|
_forceV1 = false;
|
|
_forceNoHandoff = false;
|
|
_width = 0;
|
|
_height = 0;
|
|
_inheritCursor = false;
|
|
}
|
|
|
|
ConsoleArguments::ConsoleArguments() :
|
|
ConsoleArguments(L"", nullptr, nullptr)
|
|
{
|
|
}
|
|
|
|
ConsoleArguments& ConsoleArguments::operator=(const ConsoleArguments& other)
|
|
{
|
|
if (this != &other)
|
|
{
|
|
_commandline = other._commandline;
|
|
_clientCommandline = other._clientCommandline;
|
|
_vtInHandle = other._vtInHandle;
|
|
_vtOutHandle = other._vtOutHandle;
|
|
_headless = other._headless;
|
|
_createServerHandle = other._createServerHandle;
|
|
_serverHandle = other._serverHandle;
|
|
_signalHandle = other._signalHandle;
|
|
_forceV1 = other._forceV1;
|
|
_width = other._width;
|
|
_height = other._height;
|
|
_inheritCursor = other._inheritCursor;
|
|
_runAsComServer = other._runAsComServer;
|
|
_forceNoHandoff = other._forceNoHandoff;
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Consumes the argument at the given index off of the vector.
|
|
// Arguments:
|
|
// - args - The vector full of args
|
|
// - index - The item to consume/remove from the vector.
|
|
// Return Value:
|
|
// - <none>
|
|
void ConsoleArguments::s_ConsumeArg(_Inout_ std::vector<std::wstring>& args, _In_ size_t& index)
|
|
{
|
|
args.erase(args.begin() + index);
|
|
}
|
|
|
|
// Routine Description:
|
|
// Given the commandline of tokens `args`, tries to find the argument at
|
|
// index+1, and places its value into pSetting.
|
|
// If there aren't enough args, then returns E_INVALIDARG.
|
|
// If we found a value, then we take the elements at both index and index+1 out
|
|
// of args. We'll also decrement index, so that a caller who is using index
|
|
// as a loop index will autoincrement it to have it point at the correct
|
|
// next index.
|
|
//
|
|
// EX: for args=[--foo, bar, --baz]
|
|
// index=0 would place "bar" in pSetting,
|
|
// args is now [--baz], index is now -1, caller increments to 0
|
|
// index=2 would return E_INVALIDARG,
|
|
// args is still [--foo, bar, --baz], index is still 2, caller increments to 3.
|
|
// Arguments:
|
|
// args: A collection of wstrings representing command-line arguments
|
|
// index: the index of the argument of which to get the value for. The value
|
|
// should be at (index+1). index will be decremented by one on success.
|
|
// pSetting: receives the string at index+1
|
|
// Return Value:
|
|
// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating
|
|
// failure.
|
|
[[nodiscard]] HRESULT ConsoleArguments::s_GetArgumentValue(_Inout_ std::vector<std::wstring>& args,
|
|
_Inout_ size_t& index,
|
|
_Out_opt_ std::wstring* const pSetting)
|
|
{
|
|
auto hasNext = (index + 1) < args.size();
|
|
if (hasNext)
|
|
{
|
|
s_ConsumeArg(args, index);
|
|
if (pSetting != nullptr)
|
|
{
|
|
*pSetting = args[index];
|
|
}
|
|
s_ConsumeArg(args, index);
|
|
}
|
|
return (hasNext) ? S_OK : E_INVALIDARG;
|
|
}
|
|
|
|
// Routine Description:
|
|
// Similar to s_GetArgumentValue.
|
|
// Attempts to get the next arg as a "feature" arg - this can be used for
|
|
// feature detection.
|
|
// If the next arg is not recognized, then we don't support that feature.
|
|
// Currently, the only supported feature arg is `pty`, to identify pty support.
|
|
// Arguments:
|
|
// args: A collection of wstrings representing command-line arguments
|
|
// index: the index of the argument of which to get the value for. The value
|
|
// should be at (index+1). index will be decremented by one on success.
|
|
// pSetting: receives the string at index+1
|
|
// Return Value:
|
|
// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating
|
|
// failure.
|
|
[[nodiscard]] HRESULT ConsoleArguments::s_HandleFeatureValue(_Inout_ std::vector<std::wstring>& args, _Inout_ size_t& index)
|
|
{
|
|
auto hr = E_INVALIDARG;
|
|
auto hasNext = (index + 1) < args.size();
|
|
if (hasNext)
|
|
{
|
|
s_ConsumeArg(args, index);
|
|
auto value = args[index];
|
|
if (value == FEATURE_PTY_ARG)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
s_ConsumeArg(args, index);
|
|
}
|
|
return (hasNext) ? hr : E_INVALIDARG;
|
|
}
|
|
|
|
// Method Description:
|
|
// Routine Description:
|
|
// Given the commandline of tokens `args`, tries to find the argument at
|
|
// index+1, and places its value into pSetting. See above for examples.
|
|
// This implementation attempts to parse a short from the argument.
|
|
// Arguments:
|
|
// args: A collection of wstrings representing command-line arguments
|
|
// index: the index of the argument of which to get the value for. The value
|
|
// should be at (index+1). index will be decremented by one on success.
|
|
// pSetting: receives the short at index+1
|
|
// Return Value:
|
|
// S_OK if we parsed the short successfully, otherwise E_INVALIDARG indicating
|
|
// failure. This could be the case for non-numeric arguments, or for >SHORT_MAX args.
|
|
[[nodiscard]] HRESULT ConsoleArguments::s_GetArgumentValue(_Inout_ std::vector<std::wstring>& args,
|
|
_Inout_ size_t& index,
|
|
_Out_opt_ short* const pSetting)
|
|
{
|
|
auto succeeded = (index + 1) < args.size();
|
|
if (succeeded)
|
|
{
|
|
s_ConsumeArg(args, index);
|
|
if (pSetting != nullptr)
|
|
{
|
|
try
|
|
{
|
|
size_t pos = 0;
|
|
auto value = std::stoi(args[index], &pos);
|
|
// If the entire string was a number, pos will be equal to the
|
|
// length of the string. Otherwise, a string like 8foo will
|
|
// be parsed as "8"
|
|
if (value > SHORT_MAX || pos != args[index].length())
|
|
{
|
|
succeeded = false;
|
|
}
|
|
else
|
|
{
|
|
*pSetting = static_cast<short>(value);
|
|
succeeded = true;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
succeeded = false;
|
|
}
|
|
}
|
|
s_ConsumeArg(args, index);
|
|
}
|
|
return (succeeded) ? S_OK : E_INVALIDARG;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Parsing helper that will turn a string into a handle value if possible.
|
|
// Arguments:
|
|
// - handleAsText - The string representation of the handle that was passed in on the command line
|
|
// - handleAsVal - The location to store the value if we can appropriately convert it.
|
|
// Return Value:
|
|
// - S_OK if we could successfully parse the given text and store it in the handle value location.
|
|
// - E_INVALIDARG if we couldn't parse the text as a valid hex-encoded handle number OR
|
|
// if the handle value was already filled.
|
|
[[nodiscard]] HRESULT ConsoleArguments::s_ParseHandleArg(const std::wstring& handleAsText, _Inout_ DWORD& handleAsVal)
|
|
{
|
|
auto hr = S_OK;
|
|
|
|
// The handle should have a valid prefix.
|
|
if (handleAsText.substr(0, HANDLE_PREFIX.length()) != HANDLE_PREFIX)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
else if (0 == handleAsVal)
|
|
{
|
|
handleAsVal = wcstoul(handleAsText.c_str(), nullptr /*endptr*/, 16 /*base*/);
|
|
|
|
// If the handle didn't parse into a reasonable handle ID, invalid.
|
|
if (handleAsVal == 0)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If we're trying to set the handle a second time, invalid.
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Routine Description:
|
|
// Given the commandline of tokens `args`, creates a wstring containing all of
|
|
// the remaining args after index joined with spaces. If skipFirst==true,
|
|
// then we omit the argument at index from this finished string. skipFirst
|
|
// should only be true if the first arg is
|
|
// ConsoleArguments::CLIENT_COMMANDLINE_ARG. Removes all the args starting
|
|
// at index from the collection.
|
|
// The finished commandline is placed in _clientCommandline
|
|
// Arguments:
|
|
// args: A collection of wstrings representing command-line arguments
|
|
// index: the index of the argument of which to start the commandline from.
|
|
// skipFirst: if true, omit the arg at index (which should be "--")
|
|
// Return Value:
|
|
// S_OK if we parsed the string successfully, otherwise E_INVALIDARG indicating
|
|
// failure.
|
|
[[nodiscard]] HRESULT ConsoleArguments::_GetClientCommandline(_Inout_ std::vector<std::wstring>& args, const size_t index, const bool skipFirst)
|
|
{
|
|
auto start = args.begin() + index;
|
|
|
|
// Erase the first token.
|
|
// Used to get rid of the explicit commandline token "--"
|
|
if (skipFirst)
|
|
{
|
|
// Make sure that the arg we're deleting is "--"
|
|
FAIL_FAST_IF(!(CLIENT_COMMANDLINE_ARG == start->c_str()));
|
|
args.erase(start);
|
|
}
|
|
|
|
_clientCommandline = L"";
|
|
size_t j = 0;
|
|
for (j = index; j < args.size(); j++)
|
|
{
|
|
_clientCommandline += EscapeArgument(args[j]); // escape commandline
|
|
if (j + 1 < args.size())
|
|
{
|
|
_clientCommandline += L" ";
|
|
}
|
|
}
|
|
args.erase(args.begin() + index, args.begin() + j);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Routine Description:
|
|
// Attempts to parse the commandline that this ConsoleArguments was initialized
|
|
// with. Fills all of our members with values that were specified on the
|
|
// commandline.
|
|
// Arguments:
|
|
// <none>
|
|
// Return Value:
|
|
// S_OK if we parsed our _commandline successfully, otherwise E_INVALIDARG
|
|
// indicating failure.
|
|
[[nodiscard]] HRESULT ConsoleArguments::ParseCommandline()
|
|
{
|
|
// If the commandline was empty, quick return.
|
|
if (_commandline.length() == 0)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
std::vector<std::wstring> args;
|
|
auto hr = S_OK;
|
|
|
|
// Make a mutable copy of the commandline for tokenizing
|
|
auto copy = _commandline;
|
|
|
|
// Tokenize the commandline
|
|
auto argc = 0;
|
|
wil::unique_hlocal_ptr<PWSTR[]> argv;
|
|
argv.reset(CommandLineToArgvW(copy.c_str(), &argc));
|
|
RETURN_LAST_ERROR_IF(argv == nullptr);
|
|
|
|
for (auto i = 1; i < argc; ++i)
|
|
{
|
|
args.push_back(argv[i]);
|
|
}
|
|
|
|
// Parse args out of the commandline.
|
|
// As we handle a token, remove it from the args.
|
|
// At the end of parsing, there should be nothing left.
|
|
for (size_t i = 0; i < args.size();)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
|
|
auto arg = args[i];
|
|
|
|
if (arg.substr(0, HANDLE_PREFIX.length()) == HANDLE_PREFIX ||
|
|
arg == SERVER_HANDLE_ARG)
|
|
{
|
|
// server handle token accepted two ways:
|
|
// --server 0x4 (new method)
|
|
// 0x4 (legacy method)
|
|
// If we see >1 of these, it's invalid.
|
|
auto serverHandleVal = arg;
|
|
|
|
if (arg == SERVER_HANDLE_ARG)
|
|
{
|
|
hr = s_GetArgumentValue(args, i, &serverHandleVal);
|
|
}
|
|
else
|
|
{
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = s_ParseHandleArg(serverHandleVal, _serverHandle);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_createServerHandle = false;
|
|
}
|
|
}
|
|
}
|
|
else if (arg == SIGNAL_HANDLE_ARG)
|
|
{
|
|
std::wstring signalHandleVal;
|
|
hr = s_GetArgumentValue(args, i, &signalHandleVal);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = s_ParseHandleArg(signalHandleVal, _signalHandle);
|
|
}
|
|
}
|
|
else if (arg == FORCE_V1_ARG)
|
|
{
|
|
// -ForceV1 command line switch for NTVDM support
|
|
_forceV1 = true;
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg == FORCE_NO_HANDOFF_ARG)
|
|
{
|
|
// Prevent default application handoff to a different console/terminal
|
|
_forceNoHandoff = true;
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg == COM_SERVER_ARG)
|
|
{
|
|
_runAsComServer = true;
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg.substr(0, FILEPATH_LEADER_PREFIX.length()) == FILEPATH_LEADER_PREFIX)
|
|
{
|
|
// beginning of command line -- includes file path
|
|
// skipped for historical reasons.
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg == WIDTH_ARG)
|
|
{
|
|
hr = s_GetArgumentValue(args, i, &_width);
|
|
}
|
|
else if (arg == HEIGHT_ARG)
|
|
{
|
|
hr = s_GetArgumentValue(args, i, &_height);
|
|
}
|
|
else if (arg == FEATURE_ARG)
|
|
{
|
|
hr = s_HandleFeatureValue(args, i);
|
|
}
|
|
else if (arg == HEADLESS_ARG)
|
|
{
|
|
_headless = true;
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg == INHERIT_CURSOR_ARG)
|
|
{
|
|
_inheritCursor = true;
|
|
s_ConsumeArg(args, i);
|
|
hr = S_OK;
|
|
}
|
|
else if (arg == GLYPH_WIDTH)
|
|
{
|
|
hr = s_GetArgumentValue(args, i, &_textMeasurement);
|
|
}
|
|
else if (arg == CLIENT_COMMANDLINE_ARG)
|
|
{
|
|
// Everything after this is the explicit commandline
|
|
hr = _GetClientCommandline(args, i, true);
|
|
break;
|
|
}
|
|
// TODO: handle the rest of the possible params (MSFT:13271366, MSFT:13631640)
|
|
// TODO: handle invalid args
|
|
// e.g. "conhost --foo bar" should not make the clientCommandline "--foo bar"
|
|
else
|
|
{
|
|
// If we encounter something that doesn't match one of our other
|
|
// args, then it's the start of the commandline
|
|
hr = _GetClientCommandline(args, i, false);
|
|
break;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// We should have consumed every token at this point.
|
|
// if not, it is some sort of parsing error.
|
|
// If we failed to parse an arg, then no need to assert.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
FAIL_FAST_IF(!args.empty());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Returns true if we already have opened handles to use for the VT server
|
|
// streams.
|
|
// - If false, try next to see if we have pipe names to open instead.
|
|
// Arguments:
|
|
// - <none> - uses internal state
|
|
// Return Value:
|
|
// - True or false (see description)
|
|
bool ConsoleArguments::HasVtHandles() const
|
|
{
|
|
return IsValidHandle(_vtInHandle) && IsValidHandle(_vtOutHandle);
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Returns true if we were passed a seemingly valid signal handle on startup.
|
|
// Arguments:
|
|
// - <none> - uses internal state
|
|
// Return Value:
|
|
// - True or false (see description)
|
|
bool ConsoleArguments::HasSignalHandle() const
|
|
{
|
|
return IsValidHandle(GetSignalHandle());
|
|
}
|
|
|
|
// Routine Description:
|
|
// - Returns true if we already have at least one handle for conpty streams.
|
|
// Arguments:
|
|
// - <none> - uses internal state
|
|
// Return Value:
|
|
// - True or false (see description)
|
|
bool ConsoleArguments::InConptyMode() const noexcept
|
|
{
|
|
// If we only have a signal handle, then that's fine, they probably called
|
|
// CreatePseudoConsole with neither handle.
|
|
// If we only have one of the other handles, that's fine they're still
|
|
// invoking us by passing in pipes, so they know what they're doing.
|
|
return IsValidHandle(_vtInHandle) || IsValidHandle(_vtOutHandle) || HasSignalHandle();
|
|
}
|
|
|
|
bool ConsoleArguments::IsHeadless() const
|
|
{
|
|
return _headless;
|
|
}
|
|
|
|
bool ConsoleArguments::ShouldCreateServerHandle() const
|
|
{
|
|
return _createServerHandle;
|
|
}
|
|
|
|
bool ConsoleArguments::ShouldRunAsComServer() const
|
|
{
|
|
return _runAsComServer;
|
|
}
|
|
|
|
HANDLE ConsoleArguments::GetServerHandle() const
|
|
{
|
|
return ULongToHandle(_serverHandle);
|
|
}
|
|
|
|
HANDLE ConsoleArguments::GetSignalHandle() const
|
|
{
|
|
return ULongToHandle(_signalHandle);
|
|
}
|
|
|
|
HANDLE ConsoleArguments::GetVtInHandle() const
|
|
{
|
|
return _vtInHandle;
|
|
}
|
|
|
|
HANDLE ConsoleArguments::GetVtOutHandle() const
|
|
{
|
|
return _vtOutHandle;
|
|
}
|
|
|
|
std::wstring ConsoleArguments::GetOriginalCommandLine() const
|
|
{
|
|
return _commandline;
|
|
}
|
|
|
|
std::wstring ConsoleArguments::GetClientCommandline() const
|
|
{
|
|
return _clientCommandline;
|
|
}
|
|
|
|
const std::wstring& ConsoleArguments::GetTextMeasurement() const
|
|
{
|
|
return _textMeasurement;
|
|
}
|
|
|
|
bool ConsoleArguments::GetForceV1() const
|
|
{
|
|
return _forceV1;
|
|
}
|
|
|
|
bool ConsoleArguments::GetForceNoHandoff() const
|
|
{
|
|
return _forceNoHandoff;
|
|
}
|
|
|
|
short ConsoleArguments::GetWidth() const
|
|
{
|
|
return _width;
|
|
}
|
|
|
|
short ConsoleArguments::GetHeight() const
|
|
{
|
|
return _height;
|
|
}
|
|
|
|
bool ConsoleArguments::GetInheritCursor() const
|
|
{
|
|
return _inheritCursor;
|
|
}
|
|
|
|
#ifdef UNIT_TESTING
|
|
// Method Description:
|
|
// - This is a test helper method. It can be used to trick us into thinking
|
|
// we're in conpty mode, even without parsing any arguments.
|
|
// Arguments:
|
|
// - <none>
|
|
// Return Value:
|
|
// - <none>
|
|
void ConsoleArguments::EnableConptyModeForTests()
|
|
{
|
|
_headless = true;
|
|
_vtInHandle = GetStdHandle(STD_INPUT_HANDLE);
|
|
_vtOutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
}
|
|
#endif
|