Skip to content

Commit

Permalink
(A better) Refactoring of how connection restarting is handled (#15240)
Browse files Browse the repository at this point in the history
A different take on #14548.

> We didn't love that a connection could transition back in the state
diagram, from Closed -> Start. That felt wrong. To remedy this, we're
going to allow the ControlCore to...

ASK the app to restart its connection. This is a much more sensible
approach, than leaving the ConnectionInfo in the core and having the
core do the restart itself. That's mental.

Cleanup from #14060

Closes #14327

Obsoletes #14548
  • Loading branch information
zadjii-msft authored Apr 28, 2023
1 parent bfcdc64 commit 0d6642a
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 31 deletions.
11 changes: 11 additions & 0 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ void Pane::_setupControlEvents()
_controlEvents._ConnectionStateChanged = _control.ConnectionStateChanged(winrt::auto_revoke, { this, &Pane::_ControlConnectionStateChangedHandler });
_controlEvents._WarningBell = _control.WarningBell(winrt::auto_revoke, { this, &Pane::_ControlWarningBellHandler });
_controlEvents._CloseTerminalRequested = _control.CloseTerminalRequested(winrt::auto_revoke, { this, &Pane::_CloseTerminalRequestedHandler });
_controlEvents._RestartTerminalRequested = _control.RestartTerminalRequested(winrt::auto_revoke, { this, &Pane::_RestartTerminalRequestedHandler });
}
void Pane::_removeControlEvents()
{
Expand Down Expand Up @@ -1103,6 +1104,16 @@ void Pane::_CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IIns
Close();
}

void Pane::_RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& /*sender*/,
const winrt::Windows::Foundation::IInspectable& /*args*/)
{
if (!_IsLeaf())
{
return;
}
_RestartTerminalRequestedHandlers(shared_from_this());
}

winrt::fire_and_forget Pane::_playBellSound(winrt::Windows::Foundation::Uri uri)
{
auto weakThis{ weak_from_this() };
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalApp/Pane.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ class Pane : public std::enable_shared_from_this<Pane>
WINRT_CALLBACK(LostFocus, winrt::delegate<std::shared_ptr<Pane>>);
WINRT_CALLBACK(PaneRaiseBell, winrt::Windows::Foundation::EventHandler<bool>);
WINRT_CALLBACK(Detached, winrt::delegate<std::shared_ptr<Pane>>);
WINRT_CALLBACK(RestartTerminalRequested, winrt::delegate<std::shared_ptr<Pane>>);

private:
struct PanePoint;
Expand Down Expand Up @@ -249,6 +250,7 @@ class Pane : public std::enable_shared_from_this<Pane>
winrt::Microsoft::Terminal::Control::TermControl::ConnectionStateChanged_revoker _ConnectionStateChanged;
winrt::Microsoft::Terminal::Control::TermControl::WarningBell_revoker _WarningBell;
winrt::Microsoft::Terminal::Control::TermControl::CloseTerminalRequested_revoker _CloseTerminalRequested;
winrt::Microsoft::Terminal::Control::TermControl::RestartTerminalRequested_revoker _RestartTerminalRequested;
} _controlEvents;
void _setupControlEvents();
void _removeControlEvents();
Expand Down Expand Up @@ -306,6 +308,7 @@ class Pane : public std::enable_shared_from_this<Pane>
void _ControlLostFocusHandler(const winrt::Windows::Foundation::IInspectable& sender,
const winrt::Windows::UI::Xaml::RoutedEventArgs& e);
void _CloseTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);
void _RestartTerminalRequestedHandler(const winrt::Windows::Foundation::IInspectable& sender, const winrt::Windows::Foundation::IInspectable& /*args*/);

std::pair<float, float> _CalcChildrenSizes(const float fullSize) const;
SnapChildrenSizeResult _CalcSnappedChildrenSizes(const bool widthOrHeight, const float fullSize) const;
Expand Down
57 changes: 55 additions & 2 deletions src/cascadia/TerminalApp/TerminalPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1165,7 +1165,8 @@ namespace winrt::TerminalApp::implementation
// Return value:
// - the desired connection
TerminalConnection::ITerminalConnection TerminalPage::_CreateConnectionFromSettings(Profile profile,
TerminalSettings settings)
TerminalSettings settings,
const bool inheritCursor)
{
TerminalConnection::ITerminalConnection connection{ nullptr };

Expand Down Expand Up @@ -1248,6 +1249,11 @@ namespace winrt::TerminalApp::implementation
valueSet.Insert(L"reloadEnvironmentVariables",
Windows::Foundation::PropertyValue::CreateBoolean(_settings.GlobalSettings().ReloadEnvironmentVariables()));

if (inheritCursor)
{
valueSet.Insert(L"inheritCursor", Windows::Foundation::PropertyValue::CreateBoolean(true));
}

conhostConn.Initialize(valueSet);

sessionGuid = conhostConn.Guid();
Expand All @@ -1267,6 +1273,44 @@ namespace winrt::TerminalApp::implementation
return connection;
}

TerminalConnection::ITerminalConnection TerminalPage::_duplicateConnectionForRestart(std::shared_ptr<Pane> pane)
{
const auto& control{ pane->GetTerminalControl() };
if (control == nullptr)
{
return nullptr;
}
const auto& connection = control.Connection();
auto profile{ pane->GetProfile() };

TerminalSettingsCreateResult controlSettings{ nullptr };

if (profile)
{
// TODO GH#5047 If we cache the NewTerminalArgs, we no longer need to do this.
profile = GetClosestProfileForDuplicationOfProfile(profile);
controlSettings = TerminalSettings::CreateWithProfile(_settings, profile, *_bindings);

// Replace the Starting directory with the CWD, if given
const auto workingDirectory = control.WorkingDirectory();
const auto validWorkingDirectory = !workingDirectory.empty();
if (validWorkingDirectory)
{
controlSettings.DefaultSettings().StartingDirectory(workingDirectory);
}

// To facilitate restarting defterm connections: grab the original
// commandline out of the connection and shove that back into the
// settings.
if (const auto& conpty{ connection.try_as<TerminalConnection::ConptyConnection>() })
{
controlSettings.DefaultSettings().Commandline(conpty.Commandline());
}
}

return _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings(), true);
}

// Method Description:
// - Called when the settings button is clicked. Launches a background
// thread to open the settings file in the default JSON editor.
Expand Down Expand Up @@ -2893,7 +2937,7 @@ namespace winrt::TerminalApp::implementation
return nullptr;
}

auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings());
auto connection = existingConnection ? existingConnection : _CreateConnectionFromSettings(profile, controlSettings.DefaultSettings(), false);
if (existingConnection)
{
connection.Resize(controlSettings.DefaultSettings().InitialRows(), controlSettings.DefaultSettings().InitialCols());
Expand Down Expand Up @@ -2935,6 +2979,15 @@ namespace winrt::TerminalApp::implementation
original->SetActive();
}

resultPane->RestartTerminalRequested([this](const auto& pane) {
auto connection = _duplicateConnectionForRestart(pane);
if (connection)
{
pane->GetTerminalControl().Connection(connection);
connection.Start();
}
});

return resultPane;
}

Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalApp/TerminalPage.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,8 @@ namespace winrt::TerminalApp::implementation
void _OpenNewTabDropdown();
HRESULT _OpenNewTab(const Microsoft::Terminal::Settings::Model::NewTerminalArgs& newTerminalArgs, winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection existingConnection = nullptr);
void _CreateNewTabFromPane(std::shared_ptr<Pane> pane, uint32_t insertPosition = -1);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _CreateConnectionFromSettings(Microsoft::Terminal::Settings::Model::Profile profile, Microsoft::Terminal::Settings::Model::TerminalSettings settings, const bool inheritCursor);
winrt::Microsoft::Terminal::TerminalConnection::ITerminalConnection _duplicateConnectionForRestart(std::shared_ptr<Pane> pane);

winrt::fire_and_forget _OpenNewWindow(const Microsoft::Terminal::Settings::Model::NewTerminalArgs newTerminalArgs);

Expand Down
8 changes: 0 additions & 8 deletions src/cascadia/TerminalConnection/ConnectionStateHolder.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
return _isStateOneOf(ConnectionState::Connected);
}

void _resetConnectionState()
{
{
std::lock_guard<std::mutex> stateLock{ _stateMutex };
_connectionState = ConnectionState::NotConnected;
}
}

private:
std::atomic<ConnectionState> _connectionState{ ConnectionState::NotConnected };
mutable std::mutex _stateMutex;
Expand Down
10 changes: 2 additions & 8 deletions src/cascadia/TerminalConnection/ConptyConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
{
_passthroughMode = winrt::unbox_value_or<bool>(settings.TryLookup(L"passthroughMode").try_as<Windows::Foundation::IPropertyValue>(), _passthroughMode);
}
_inheritCursor = winrt::unbox_value_or<bool>(settings.TryLookup(L"inheritCursor").try_as<Windows::Foundation::IPropertyValue>(), _inheritCursor);
_reloadEnvironmentVariables = winrt::unbox_value_or<bool>(settings.TryLookup(L"reloadEnvironmentVariables").try_as<Windows::Foundation::IPropertyValue>(),
_reloadEnvironmentVariables);
_profileGuid = winrt::unbox_value_or<winrt::guid>(settings.TryLookup(L"profileGuid").try_as<Windows::Foundation::IPropertyValue>(), _profileGuid);
Expand Down Expand Up @@ -316,13 +317,6 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
void ConptyConnection::Start()
try
{
bool usingExistingBuffer = false;
if (_isStateAtOrBeyond(ConnectionState::Closed))
{
_resetConnectionState();
usingExistingBuffer = true;
}

_transitionToState(ConnectionState::Connecting);

const til::size dimensions{ gsl::narrow<til::CoordType>(_cols), gsl::narrow<til::CoordType>(_rows) };
Expand All @@ -338,7 +332,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
// PseudoConsole sends a clear screen VT code which our renderer
// interprets into making all the previous lines be outside the
// current viewport.
if (usingExistingBuffer)
if (_inheritCursor)
{
flags |= PSEUDOCONSOLE_INHERIT_CURSOR;
}
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalConnection/ConptyConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ namespace winrt::Microsoft::Terminal::TerminalConnection::implementation
std::wstring _u16Str{};
std::array<char, 4096> _buffer{};
bool _passthroughMode{};
bool _inheritCursor{ false };
bool _reloadEnvironmentVariables{};
guid _profileGuid{};

Expand Down
71 changes: 60 additions & 11 deletions src/cascadia/TerminalControl/ControlCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,21 +78,14 @@ namespace winrt::Microsoft::Terminal::Control::implementation
ControlCore::ControlCore(Control::IControlSettings settings,
Control::IControlAppearance unfocusedAppearance,
TerminalConnection::ITerminalConnection connection) :
_connection{ connection },
_desiredFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, DEFAULT_FONT_SIZE, CP_UTF8 },
_actualFont{ DEFAULT_FONT_FACE, 0, DEFAULT_FONT_WEIGHT, { 0, DEFAULT_FONT_SIZE }, CP_UTF8, false }
{
_settings = winrt::make_self<implementation::ControlSettings>(settings, unfocusedAppearance);

_terminal = std::make_shared<::Microsoft::Terminal::Core::Terminal>();

// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = _connection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});

// This event is explicitly revoked in the destructor: does not need weak_ref
_connectionOutputEventToken = _connection.TerminalOutput({ this, &ControlCore::_connectionOutputHandler });
Connection(connection);

_terminal->SetWriteInputCallback([this](std::wstring_view wstr) {
_sendInputToConnection(wstr);
Expand Down Expand Up @@ -256,6 +249,62 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_AttachedHandlers(*this, nullptr);
}

TerminalConnection::ITerminalConnection ControlCore::Connection()
{
return _connection;
}

// Method Description:
// - Setup our event handlers for this connection. If we've currently got a
// connection, then this'll revoke the existing connection's handlers.
// - This will not call Start on the incoming connection. The caller should do that.
// - If the caller doesn't want the old connection to be closed, then they
// should grab a reference to it before calling this (so that it doesn't
// destruct, and close) during this call.
void ControlCore::Connection(const TerminalConnection::ITerminalConnection& newConnection)
{
auto oldState = ConnectionState(); // rely on ControlCore's automatic null handling
// revoke ALL old handlers immediately

_connectionOutputEventRevoker.revoke();
_connectionStateChangedRevoker.revoke();

_connection = newConnection;
if (_connection)
{
// Subscribe to the connection's disconnected event and call our connection closed handlers.
_connectionStateChangedRevoker = newConnection.StateChanged(winrt::auto_revoke, [this](auto&& /*s*/, auto&& /*v*/) {
_ConnectionStateChangedHandlers(*this, nullptr);
});

// Get our current size in rows/cols, and hook them up to
// this connection too.
{
const auto vp = _terminal->GetViewport();
const auto width = vp.Width();
const auto height = vp.Height();

newConnection.Resize(height, width);
}
// Window owner too.
if (auto conpty{ newConnection.try_as<TerminalConnection::ConptyConnection>() })
{
conpty.ReparentWindow(_owningHwnd);
}

// This event is explicitly revoked in the destructor: does not need weak_ref
_connectionOutputEventRevoker = _connection.TerminalOutput(winrt::auto_revoke, { this, &ControlCore::_connectionOutputHandler });
}

// Fire off a connection state changed notification, to let our hosting
// app know that we're in a different state now.
if (oldState != ConnectionState())
{ // rely on the null handling again
// send the notification
_ConnectionStateChangedHandlers(*this, nullptr);
}
}

bool ControlCore::Initialize(const float actualWidth,
const float actualHeight,
const float compositionScale)
Expand Down Expand Up @@ -427,8 +476,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation

if (ch == Enter)
{
_connection.Close();
_connection.Start();
// Ask the hosting application to give us a new connection.
_RestartTerminalRequestedHandlers(*this, nullptr);
return true;
}
}
Expand Down Expand Up @@ -1541,7 +1590,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_midiAudio.BeginSkip();

// Stop accepting new output and state changes before we disconnect everything.
_connection.TerminalOutput(_connectionOutputEventToken);
_connectionOutputEventRevoker.revoke();
_connectionStateChangedRevoker.revoke();
_connection.Close();
}
Expand Down
6 changes: 5 additions & 1 deletion src/cascadia/TerminalControl/ControlCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ namespace winrt::Microsoft::Terminal::Control::implementation
uint64_t OwningHwnd();
void OwningHwnd(uint64_t owner);

TerminalConnection::ITerminalConnection Connection();
void Connection(const TerminalConnection::ITerminalConnection& connection);

void AnchorContextMenu(til::point viewportRelativeCharacterPosition);

bool ShouldShowSelectCommand();
Expand Down Expand Up @@ -257,6 +260,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
TYPED_EVENT(UpdateSelectionMarkers, IInspectable, Control::UpdateSelectionMarkersEventArgs);
TYPED_EVENT(OpenHyperlink, IInspectable, Control::OpenHyperlinkEventArgs);
TYPED_EVENT(CloseTerminalRequested, IInspectable, IInspectable);
TYPED_EVENT(RestartTerminalRequested, IInspectable, IInspectable);

TYPED_EVENT(Attached, IInspectable, IInspectable);
// clang-format on
Expand All @@ -266,7 +270,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
bool _closing{ false };

TerminalConnection::ITerminalConnection _connection{ nullptr };
event_token _connectionOutputEventToken;
TerminalConnection::ITerminalConnection::TerminalOutput_revoker _connectionOutputEventRevoker;
TerminalConnection::ITerminalConnection::StateChanged_revoker _connectionStateChangedRevoker;

winrt::com_ptr<ControlSettings> _settings{ nullptr };
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalControl/ControlCore.idl
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ namespace Microsoft.Terminal.Control
void UpdateSettings(IControlSettings settings, IControlAppearance appearance);
void ApplyAppearance(Boolean focused);

Microsoft.Terminal.TerminalConnection.ITerminalConnection Connection;

IControlSettings Settings { get; };
IControlAppearance FocusedAppearance { get; };
IControlAppearance UnfocusedAppearance { get; };
Expand Down Expand Up @@ -170,6 +172,7 @@ namespace Microsoft.Terminal.Control
event Windows.Foundation.TypedEventHandler<Object, UpdateSelectionMarkersEventArgs> UpdateSelectionMarkers;
event Windows.Foundation.TypedEventHandler<Object, OpenHyperlinkEventArgs> OpenHyperlink;
event Windows.Foundation.TypedEventHandler<Object, Object> CloseTerminalRequested;
event Windows.Foundation.TypedEventHandler<Object, Object> RestartTerminalRequested;

event Windows.Foundation.TypedEventHandler<Object, Object> Attached;
};
Expand Down
10 changes: 10 additions & 0 deletions src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_revokers.ConnectionStateChanged = _core.ConnectionStateChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleConnectionStateChanged });
_revokers.ShowWindowChanged = _core.ShowWindowChanged(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleShowWindowChanged });
_revokers.CloseTerminalRequested = _core.CloseTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleCloseTerminalRequested });
_revokers.RestartTerminalRequested = _core.RestartTerminalRequested(winrt::auto_revoke, { get_weak(), &TermControl::_bubbleRestartTerminalRequested });

_revokers.PasteFromClipboard = _interactivity.PasteFromClipboard(winrt::auto_revoke, { get_weak(), &TermControl::_bubblePasteFromClipboard });

Expand Down Expand Up @@ -262,6 +263,15 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _interactivity.Id();
}

TerminalConnection::ITerminalConnection TermControl::Connection()
{
return _core.Connection();
}
void TermControl::Connection(const TerminalConnection::ITerminalConnection& newConnection)
{
_core.Connection(newConnection);
}

void TermControl::_throttledUpdateScrollbar(const ScrollBarUpdate& update)
{
// Assumptions:
Expand Down
Loading

0 comments on commit 0d6642a

Please sign in to comment.