Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from microsoft:main #95

Merged
merged 3 commits into from
Nov 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/actions/spelling/allow/allow.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
aci
admins
allcolors
Apc
Expand All @@ -8,6 +9,7 @@ breadcrumbs
bsd
calt
ccmp
ccon
changelog
clickable
clig
Expand Down Expand Up @@ -91,6 +93,7 @@ reserialize
reserializes
rlig
runtimes
servicebus
shcha
slnt
Sos
Expand All @@ -117,6 +120,7 @@ vsdevcmd
walkthrough
walkthroughs
We'd
westus
wildcards
XBox
YBox
Expand Down
53 changes: 49 additions & 4 deletions src/cascadia/TerminalConnection/AzureConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation

switch (bufferType)
{
case WINHTTP_WEB_SOCKET_BINARY_MESSAGE_BUFFER_TYPE:
case WINHTTP_WEB_SOCKET_UTF8_FRAGMENT_BUFFER_TYPE:
case WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE:
{
Expand Down Expand Up @@ -797,7 +798,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// - an optional HTTP method (defaults to POST if content is present, GET otherwise)
// Return value:
// - the response from the server as a json value
WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method)
WDJ::JsonObject AzureConnection::_SendRequestReturningJson(std::wstring_view uri, const WWH::IHttpContent& content, WWH::HttpMethod method, const Windows::Foundation::Uri referer)
{
if (!method)
{
Expand All @@ -810,6 +811,11 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
auto headers{ request.Headers() };
headers.Accept().TryParseAdd(L"application/json");

if (referer)
{
headers.Referer(referer);
}

const auto response{ _httpClient.SendRequestAsync(request).get() };
const auto string{ response.Content().ReadAsStringAsync().get() };
const auto jsonResult{ WDJ::JsonObject::Parse(string) };
Expand Down Expand Up @@ -974,17 +980,56 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
auto uri{ fmt::format(L"{}terminals?cols={}&rows={}&version=2019-01-01&shell={}", _cloudShellUri, _initialCols, _initialRows, shellType) };

WWH::HttpStringContent content{
L"",
L"{}",
WSS::UnicodeEncoding::Utf8,
// LOAD-BEARING. the API returns "'content-type' should be 'application/json' or 'multipart/form-data'"
L"application/json"
};

const auto terminalResponse = _SendRequestReturningJson(uri, content);
const auto terminalResponse = _SendRequestReturningJson(uri, content, WWH::HttpMethod::Post(), Windows::Foundation::Uri(_cloudShellUri));
_terminalID = terminalResponse.GetNamedString(L"id");

// we have to do some post-handling to get the proper socket endpoint
// the logic here is based on the way the cloud shell team itself does it
winrt::hstring finalSocketUri;
const std::wstring_view wCloudShellUri{ _cloudShellUri };

if (wCloudShellUri.find(L"servicebus") == std::wstring::npos)
{
// wCloudShellUri does not contain the word "servicebus", we can just use it to make the final URI

// remove the "https" from the cloud shell URI
const auto uriWithoutProtocol = wCloudShellUri.substr(5);

finalSocketUri = fmt::format(FMT_COMPILE(L"wss{}terminals/{}"), uriWithoutProtocol, _terminalID);
}
else
{
// if wCloudShellUri contains the word "servicebus", that means the returned socketUri is of the form
// wss://ccon-prod-westus-aci-03.servicebus.windows.net/cc-AAAA-AAAAAAAA//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
// we need to change it to:
// wss://ccon-prod-westus-aci-03.servicebus.windows.net/$hc/cc-AAAA-AAAAAAAA/terminals/aaaaaaaaaaaaaaaaaaaaaa

const auto socketUri = terminalResponse.GetNamedString(L"socketUri");
const std::wstring_view wSocketUri{ socketUri };

// get the substring up until the ".net"
const auto dotNetStart = wSocketUri.find(L".net");
THROW_HR_IF(E_UNEXPECTED, dotNetStart == std::wstring::npos);
const auto dotNetEnd = dotNetStart + 4;
const auto wSocketUriBody = wSocketUri.substr(0, dotNetEnd);

// get the portion between the ".net" and the "//" (this is the cc-AAAA-AAAAAAAA part)
const auto lastDoubleSlashPos = wSocketUri.find_last_of(L"//");
THROW_HR_IF(E_UNEXPECTED, lastDoubleSlashPos == std::wstring::npos);
const auto wSocketUriMiddle = wSocketUri.substr(dotNetEnd, lastDoubleSlashPos - (dotNetEnd));

// piece together the final uri, adding in the "$hc" and "terminals" where needed
finalSocketUri = fmt::format(FMT_COMPILE(L"{}/$hc{}terminals/{}"), wSocketUriBody, wSocketUriMiddle, _terminalID);
}

// Return the uri
return terminalResponse.GetNamedString(L"socketUri");
return winrt::hstring{ finalSocketUri };
}

// Method description:
Expand Down
2 changes: 1 addition & 1 deletion src/cascadia/TerminalConnection/AzureConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation

void _WriteStringWithNewline(const std::wstring_view str);
void _WriteCaughtExceptionRecord();
winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr);
winrt::Windows::Data::Json::JsonObject _SendRequestReturningJson(std::wstring_view uri, const winrt::Windows::Web::Http::IHttpContent& content = nullptr, winrt::Windows::Web::Http::HttpMethod method = nullptr, const winrt::Windows::Foundation::Uri referer = nullptr);
void _setAccessToken(std::wstring_view accessToken);
winrt::Windows::Data::Json::JsonObject _GetDeviceCode();
winrt::Windows::Data::Json::JsonObject _WaitForUser(const winrt::hstring& deviceCode, int pollInterval, int expiresIn);
Expand Down
162 changes: 111 additions & 51 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

#include <DefaultSettings.h>
#include <unicode.hpp>
#include <utils.hpp>
#include <WinUser.h>
#include <LibraryResources.h>

#include "EventArgs.h"
#include "../../types/inc/GlyphWidth.hpp"
#include "../../buffer/out/search.h"
#include "../../renderer/atlas/AtlasEngine.h"
#include "../../renderer/dx/DxRenderer.hpp"
Expand Down Expand Up @@ -443,6 +443,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - <none>
void ControlCore::_sendInputToConnection(std::wstring_view wstr)
{
if (wstr.empty())
{
return;
}

// The connection may call functions like WriteFile() which may block indefinitely.
// It's important we don't hold any mutexes across such calls.
_terminal->_assertUnlocked();

if (_isReadOnly)
{
_raiseReadOnlyWarning();
Expand Down Expand Up @@ -492,8 +501,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_handleControlC();
}

const auto lock = _terminal->LockForWriting();
return _terminal->SendCharEvent(ch, scanCode, modifiers);
TerminalInput::OutputType out;
{
const auto lock = _terminal->LockForReading();
out = _terminal->SendCharEvent(ch, scanCode, modifiers);
}
if (out)
{
_sendInputToConnection(*out);
return true;
}
return false;
}

void ControlCore::_handleControlC()
Expand Down Expand Up @@ -602,46 +620,56 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const ControlKeyStates modifiers,
const bool keyDown)
{
const auto lock = _terminal->LockForWriting();
if (!vkey)
{
return true;
}

// Update the selection, if it's present
// GH#8522, GH#3758 - Only modify the selection on key _down_. If we
// modify on key up, then there's chance that we'll immediately dismiss
// a selection created by an action bound to a keydown.
if (_shouldTryUpdateSelection(vkey) && keyDown)
TerminalInput::OutputType out;
{
// try to update the selection
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
_updateSelectionUI();
return true;
}
const auto lock = _terminal->LockForWriting();

// GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination.
if (!modifiers.IsWinPressed())
// Update the selection, if it's present
// GH#8522, GH#3758 - Only modify the selection on key _down_. If we
// modify on key up, then there's chance that we'll immediately dismiss
// a selection created by an action bound to a keydown.
if (_shouldTryUpdateSelection(vkey) && keyDown)
{
_terminal->ClearSelection();
_updateSelectionUI();
}
// try to update the selection
if (const auto updateSlnParams{ _terminal->ConvertKeyEventToUpdateSelectionParams(modifiers, vkey) })
{
_terminal->UpdateSelection(updateSlnParams->first, updateSlnParams->second, modifiers);
_updateSelectionUI();
return true;
}

// When there is a selection active, escape should clear it and NOT flow through
// to the terminal. With any other keypress, it should clear the selection AND
// flow through to the terminal.
if (vkey == VK_ESCAPE)
{
return true;
// GH#8791 - don't dismiss selection if Windows key was also pressed as a key-combination.
if (!modifiers.IsWinPressed())
{
_terminal->ClearSelection();
_updateSelectionUI();
}

// When there is a selection active, escape should clear it and NOT flow through
// to the terminal. With any other keypress, it should clear the selection AND
// flow through to the terminal.
if (vkey == VK_ESCAPE)
{
return true;
}
}
}

// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.
return vkey ? _terminal->SendKeyEvent(vkey,
scanCode,
modifiers,
keyDown) :
true;
// If the terminal translated the key, mark the event as handled.
// This will prevent the system from trying to get the character out
// of it and sending us a CharacterReceived event.
out = _terminal->SendKeyEvent(vkey, scanCode, modifiers, keyDown);
}
if (out)
{
_sendInputToConnection(*out);
return true;
}
return false;
}

bool ControlCore::SendMouseEvent(const til::point viewportPos,
Expand All @@ -650,8 +678,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const short wheelDelta,
const TerminalInput::MouseButtonState state)
{
const auto lock = _terminal->LockForWriting();
return _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state);
TerminalInput::OutputType out;
{
const auto lock = _terminal->LockForReading();
out = _terminal->SendMouseEvent(viewportPos, uiButton, states, wheelDelta, state);
}
if (out)
{
_sendInputToConnection(*out);
return true;
}
return false;
}

void ControlCore::UserScrollViewport(const int viewTop)
Expand Down Expand Up @@ -1324,8 +1361,19 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// before sending it over the terminal's connection.
void ControlCore::PasteText(const winrt::hstring& hstr)
{
using namespace ::Microsoft::Console::Utils;

auto filtered = FilterStringForPaste(hstr, CarriageReturnNewline | ControlCodes);
if (BracketedPasteEnabled())
{
filtered.insert(0, L"\x1b[200~");
filtered.append(L"\x1b[201~");
}

// It's important to not hold the terminal lock while calling this function as sending the data may take a long time.
_sendInputToConnection(filtered);

const auto lock = _terminal->LockForWriting();
_terminal->WritePastedText(hstr);
_terminal->ClearSelection();
_updateSelectionUI();
_terminal->TrySnapOnInput();
Expand Down Expand Up @@ -1894,17 +1942,27 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const auto endPoint = goRight ? clampedClick : cursorPos;

const auto delta = _terminal->GetTextBuffer().GetCellDistance(startPoint, endPoint);

const WORD key = goRight ? VK_RIGHT : VK_LEFT;

std::wstring buffer;
const auto append = [&](TerminalInput::OutputType&& out) {
if (out)
{
buffer.append(std::move(*out));
}
};

// Send an up and a down once per cell. This won't
// accurately handle wide characters, or continuation
// prompts, or cases where a single escape character in the
// command (e.g. ^[) takes up two cells.
for (size_t i = 0u; i < delta; i++)
{
_terminal->SendKeyEvent(key, 0, {}, true);
_terminal->SendKeyEvent(key, 0, {}, false);
append(_terminal->SendKeyEvent(key, 0, {}, true));
append(_terminal->SendKeyEvent(key, 0, {}, false));
}

_sendInputToConnection(buffer);
}
}
}
Expand All @@ -1915,7 +1973,6 @@ namespace winrt::Microsoft::Terminal::Control::implementation
// - Updates the renderer's representation of the selection as well as the selection marker overlay in TermControl
void ControlCore::_updateSelectionUI()
{
const auto lock = _terminal->LockForWriting();
_renderer->TriggerSelection();
// only show the markers if we're doing a keyboard selection or in mark mode
const bool showMarkers{ _terminal->SelectionMode() >= ::Microsoft::Terminal::Core::Terminal::SelectionInteractionMode::Keyboard };
Expand Down Expand Up @@ -2247,14 +2304,17 @@ namespace winrt::Microsoft::Terminal::Control::implementation

void ControlCore::_focusChanged(bool focused)
{
// GH#13461 - temporarily turn off read-only mode, send the focus event,
// then turn it back on. Even in focus mode, focus events are fine to
// send. We don't want to pop a warning every time the control is
// focused.
const auto previous = std::exchange(_isReadOnly, false);
const auto restore = wil::scope_exit([&]() { _isReadOnly = previous; });
const auto lock = _terminal->LockForWriting();
_terminal->FocusChanged(focused);
TerminalInput::OutputType out;
{
const auto lock = _terminal->LockForReading();
out = _terminal->FocusChanged(focused);
}
if (out && !out->empty())
{
// _sendInputToConnection() asserts that we aren't in focus mode,
// but window focus events are always fine to send.
_connection.WriteInput(*out);
}
}

bool ControlCore::_isBackgroundTransparent()
Expand Down
Loading
Loading