terminal/src/cascadia/TerminalAzBridge/main.cpp
Mike Griese 6e70c4ae07
Switch Connections to use ValueSets to initialize them (#10184)
#### ⚠️ targets #10051 

## Summary of the Pull Request

This PR does one big, primary thing. It removes all the constructors from any TerminalConnections, and changes them to use an `Initialize` method that accepts a `ValueSet` of properties.

Why?

For the upcoming window/content process work, we'll need the content process to be able to initialize the connection _in the content process_. However, the window process will be the one that knows what type of connection to make. Enter `ConnectionInformation`. This class will let us specify the class name of the type we want to create, and a set of settings to use when initializing that connection.

**IMPORTANT**: As a part of this, the constructor for a connection must have 0 arguments. `RoActivateInstance` lets you just conjure a WinRT type just by class name, but that class must have a 0 arg ctor. Hence the need for `Initialize`, to actually pass the settings.

We're using a `ValueSet` here because it's basically a json blob, with more steps. In the future, when extension authors want to have custom connections, we can always deserialize the json into a `ValueSet`, pass it to their connection's `Initialize`, and let then get what they need out of it.

## References
* Tear-out: #1256
* Megathread: #5000
* Project: https://github.com/microsoft/terminal/projects/5

## PR Checklist
* [x] Closes https://github.com/microsoft/terminal/projects/5#card-50760298
* [x] I work here
* [n/a] Tests added/passed
* [n/a] Requires documentation to be updated

## Detailed Description of the Pull Request / Additional comments

`ConnectionInformation` was included as a part of this PR, to demonstrate how this will eventually be used. `ConnectionInformation` is not _currently_ used.

## Validation Steps Performed

It still builds and runs.
2021-07-20 15:02:17 +00:00

109 lines
3.9 KiB
C++

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "pch.h"
#include "winrt/Microsoft.Terminal.TerminalConnection.h"
#include "ConsoleInputReader.h"
using namespace winrt;
using namespace winrt::Windows::Foundation;
using namespace winrt::Microsoft::Terminal::TerminalConnection;
static COORD GetConsoleScreenSize(HANDLE outputHandle)
{
CONSOLE_SCREEN_BUFFER_INFOEX csbiex{};
csbiex.cbSize = sizeof(csbiex);
GetConsoleScreenBufferInfoEx(outputHandle, &csbiex);
return {
(csbiex.srWindow.Right - csbiex.srWindow.Left) + 1,
(csbiex.srWindow.Bottom - csbiex.srWindow.Top) + 1
};
}
static ConnectionState RunConnectionToCompletion(const ITerminalConnection& connection, HANDLE outputHandle, HANDLE inputHandle)
{
connection.TerminalOutput([outputHandle](const winrt::hstring& output) {
WriteConsoleW(outputHandle, output.data(), output.size(), nullptr, nullptr);
});
// Detach a thread to spin the console read indefinitely.
// This application exits when the connection is closed, so
// the connection's lifetime will outlast this thread.
std::thread([connection, outputHandle, inputHandle] {
ConsoleInputReader reader{ inputHandle };
reader.SetWindowSizeChangedCallback([&]() {
const auto size = GetConsoleScreenSize(outputHandle);
connection.Resize(size.Y, size.X);
});
while (true)
{
auto input = reader.Read();
if (input)
{
connection.WriteInput(*input);
}
}
}).detach();
std::condition_variable stateChangeVar;
std::optional<ConnectionState> state;
std::mutex stateMutex;
connection.StateChanged([&](auto&& /*s*/, auto&& /*e*/) {
std::unique_lock<std::mutex> lg{ stateMutex };
state = connection.State();
stateChangeVar.notify_all();
});
connection.Start();
std::unique_lock<std::mutex> lg{ stateMutex };
stateChangeVar.wait(lg, [&]() {
if (!state.has_value())
{
return false;
}
return state.value() == ConnectionState::Closed || state.value() == ConnectionState::Failed;
});
return state.value();
}
int wmain(int /*argc*/, wchar_t** /*argv*/)
{
winrt::init_apartment(winrt::apartment_type::single_threaded);
DWORD inputMode{}, outputMode{};
HANDLE conIn{ GetStdHandle(STD_INPUT_HANDLE) }, conOut{ GetStdHandle(STD_OUTPUT_HANDLE) };
UINT codepage{ GetConsoleCP() }, outputCodepage{ GetConsoleOutputCP() };
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conIn, &inputMode));
RETURN_IF_WIN32_BOOL_FALSE(GetConsoleMode(conOut, &outputMode));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conIn, ENABLE_WINDOW_INPUT | ENABLE_VIRTUAL_TERMINAL_INPUT));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleMode(conOut, ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT | DISABLE_NEWLINE_AUTO_RETURN));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleCP(CP_UTF8));
RETURN_IF_WIN32_BOOL_FALSE(SetConsoleOutputCP(CP_UTF8));
auto restoreConsoleModes = wil::scope_exit([&]() {
SetConsoleMode(conIn, inputMode);
SetConsoleMode(conOut, outputMode);
SetConsoleCP(codepage);
SetConsoleOutputCP(outputCodepage);
});
const auto size = GetConsoleScreenSize(conOut);
AzureConnection azureConn{};
winrt::Windows::Foundation::Collections::ValueSet vs{};
vs.Insert(L"initialRows", winrt::Windows::Foundation::PropertyValue::CreateUInt32(gsl::narrow_cast<uint32_t>(size.Y)));
vs.Insert(L"initialCols", winrt::Windows::Foundation::PropertyValue::CreateUInt32(gsl::narrow_cast<uint32_t>(size.X)));
azureConn.Initialize(vs);
const auto state = RunConnectionToCompletion(azureConn, conOut, conIn);
return state == ConnectionState::Closed ? 0 : 1;
}