Skip to content

Commit

Permalink
Add support for the VT answerback capability (#17660)
Browse files Browse the repository at this point in the history
The answerback feature allows for the user to define a message that the
terminal will transmit to the host whenever an `ENQ` (enquiry) control
character is received.

## Detailed Description of the Pull Request / Additional comments

In Windows Terminal, the message can be configured at the profile level
of the settings file, as a string property named `AnswerbackMessage`.

In ConHost, the message can be configured in the registry, again as a
string value with the name `AnswerbackMessage`.

## Validation Steps Performed

I've confirmed that the control is working as intended in both Windows
Terminal and ConHost using Vttest.

Closes #11946
  • Loading branch information
j4james authored Aug 7, 2024
1 parent 2c452e0 commit 746cf1f
Show file tree
Hide file tree
Showing 24 changed files with 62 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/actions/spelling/expect/expect.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ANSISYS
ANSISYSRC
ANSISYSSC
answerback
ANSWERBACKMESSAGE
antialiasing
ANull
anycpu
Expand Down
1 change: 1 addition & 0 deletions dep/Console/winconp.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ GetConsoleKeyboardLayoutNameW(
#define CONSOLE_REGISTRY_DEFAULTFOREGROUND L"DefaultForeground"
#define CONSOLE_REGISTRY_DEFAULTBACKGROUND L"DefaultBackground"
#define CONSOLE_REGISTRY_TERMINALSCROLLING L"TerminalScrolling"
#define CONSOLE_REGISTRY_ANSWERBACKMESSAGE L"AnswerbackMessage"
// end V2 console settings

/*
Expand Down
4 changes: 4 additions & 0 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3032,6 +3032,10 @@
"description": "By default Windows treats Ctrl+Alt as an alias for AltGr. When altGrAliasing is set to false, this behavior will be disabled.",
"type": "boolean"
},
"answerbackMessage": {
"description": "The response that is sent when an ENQ control character is received.",
"type": "string"
},
"source": {
"description": "Stores the name of the profile generator that originated this profile.",
"type": [
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalCore/ICoreSettings.idl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Microsoft.Terminal.Core

Boolean SnapOnInput;
Boolean AltGrAliasing;
String AnswerbackMessage;

String StartingTitle;
Boolean SuppressApplicationTitle;
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalCore/Terminal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ void Terminal::UpdateSettings(ICoreSettings settings)

_snapOnInput = settings.SnapOnInput();
_altGrAliasing = settings.AltGrAliasing();
_answerbackMessage = settings.AnswerbackMessage();
_wordDelimiters = settings.WordDelimiters();
_suppressApplicationTitle = settings.SuppressApplicationTitle();
_startingTitle = settings.StartingTitle();
Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalCore/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class Microsoft::Terminal::Core::Terminal final :
void SetTextAttributes(const TextAttribute& attrs) noexcept override;
void SetSystemMode(const Mode mode, const bool enabled) noexcept override;
bool GetSystemMode(const Mode mode) const noexcept override;
void ReturnAnswerback() override;
void WarningBell() override;
void SetWindowTitle(const std::wstring_view title) override;
CursorType GetUserDefaultCursorStyle() const noexcept override;
Expand Down Expand Up @@ -371,6 +372,7 @@ class Microsoft::Terminal::Core::Terminal final :

size_t _hyperlinkPatternId = 0;

std::wstring _answerbackMessage;
std::wstring _workingDirectory;

// This default fake font value is only used to check if the font is a raster font.
Expand Down
5 changes: 5 additions & 0 deletions src/cascadia/TerminalCore/TerminalApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ bool Terminal::GetSystemMode(const Mode mode) const noexcept
return _systemMode.test(mode);
}

void Terminal::ReturnAnswerback()
{
ReturnResponse(_answerbackMessage);
}

void Terminal::WarningBell()
{
_pfnWarningBell();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/MTSMSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Author(s):
X(int32_t, HistorySize, "historySize", DEFAULT_HISTORY_SIZE) \
X(bool, SnapOnInput, "snapOnInput", true) \
X(bool, AltGrAliasing, "altGrAliasing", true) \
X(hstring, AnswerbackMessage, "answerbackMessage") \
X(hstring, Commandline, "commandline", L"%SystemRoot%\\System32\\cmd.exe") \
X(Microsoft::Terminal::Control::ScrollbarState, ScrollState, "scrollbarState", Microsoft::Terminal::Control::ScrollbarState::Visible) \
X(Microsoft::Terminal::Control::TextAntialiasingMode, AntialiasingMode, "antialiasingMode", Microsoft::Terminal::Control::TextAntialiasingMode::Grayscale) \
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/Profile.idl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ namespace Microsoft.Terminal.Settings.Model
INHERITABLE_PROFILE_SETTING(Int32, HistorySize);
INHERITABLE_PROFILE_SETTING(Boolean, SnapOnInput);
INHERITABLE_PROFILE_SETTING(Boolean, AltGrAliasing);
INHERITABLE_PROFILE_SETTING(String, AnswerbackMessage);
INHERITABLE_PROFILE_SETTING(BellStyle, BellStyle);

INHERITABLE_PROFILE_SETTING(Windows.Foundation.Collections.IMap<String COMMA String>, EnvironmentVariables);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/TerminalSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
_HistorySize = profile.HistorySize();
_SnapOnInput = profile.SnapOnInput();
_AltGrAliasing = profile.AltGrAliasing();
_AnswerbackMessage = profile.AnswerbackMessage();

// Fill in the remaining properties from the profile
_ProfileName = profile.Name();
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/TerminalSettingsModel/TerminalSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation

INHERITABLE_SETTING(Model::TerminalSettings, bool, SnapOnInput, true);
INHERITABLE_SETTING(Model::TerminalSettings, bool, AltGrAliasing, true);
INHERITABLE_SETTING(Model::TerminalSettings, hstring, AnswerbackMessage);
INHERITABLE_SETTING(Model::TerminalSettings, til::color, CursorColor, DEFAULT_CURSOR_COLOR);
INHERITABLE_SETTING(Model::TerminalSettings, Microsoft::Terminal::Core::CursorStyle, CursorShape, Core::CursorStyle::Vintage);
INHERITABLE_SETTING(Model::TerminalSettings, uint32_t, CursorHeight, DEFAULT_CURSOR_HEIGHT);
Expand Down
1 change: 1 addition & 0 deletions src/cascadia/inc/ControlProperties.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
X(int32_t, InitialCols, 80) \
X(bool, SnapOnInput, true) \
X(bool, AltGrAliasing, true) \
X(winrt::hstring, AnswerbackMessage) \
X(winrt::hstring, WordDelimiters, DEFAULT_WORD_DELIMITERS) \
X(bool, CopyOnSelect, false) \
X(bool, FocusFollowMouse, false) \
Expand Down
10 changes: 10 additions & 0 deletions src/host/outputStream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,16 @@ bool ConhostInternalGetSet::GetSystemMode(const Mode mode) const
}
}

// Routine Description:
// - Sends the configured answerback message in response to an ENQ query.
// Return Value:
// - <none>
void ConhostInternalGetSet::ReturnAnswerback()
{
const auto& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
ReturnResponse(gci.GetAnswerbackMessage());
}

// Routine Description:
// - Sends a notify message to play the "SystemHand" sound event.
// Return Value:
Expand Down
1 change: 1 addition & 0 deletions src/host/outputStream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ConhostInternalGetSet final : public Microsoft::Console::VirtualTerminal::
void SetSystemMode(const Mode mode, const bool enabled) override;
bool GetSystemMode(const Mode mode) const override;

void ReturnAnswerback() override;
void WarningBell() override;

void SetWindowTitle(const std::wstring_view title) override;
Expand Down
5 changes: 5 additions & 0 deletions src/host/settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,11 @@ void Settings::SetTerminalScrolling(const bool terminalScrollingEnabled) noexcep
_TerminalScrolling = terminalScrollingEnabled;
}

std::wstring_view Settings::GetAnswerbackMessage() const noexcept
{
return _answerbackMessage;
}

// Determines whether our primary renderer should be DirectX or GDI.
// This is based on user preference and velocity hold back state.
bool Settings::GetUseDx() const noexcept
Expand Down
3 changes: 3 additions & 0 deletions src/host/settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ class Settings
bool IsTerminalScrolling() const noexcept;
void SetTerminalScrolling(const bool terminalScrollingEnabled) noexcept;

std::wstring_view GetAnswerbackMessage() const noexcept;

bool GetUseDx() const noexcept;
bool GetCopyColor() const noexcept;
SettingsTextMeasurementMode GetTextMeasurementMode() const noexcept;
Expand Down Expand Up @@ -236,5 +238,6 @@ class Settings
bool _fInterceptCopyPaste;

bool _TerminalScrolling;
WCHAR _answerbackMessage[32] = {};
friend class RegistrySerialization;
};
1 change: 1 addition & 0 deletions src/propslib/RegistrySerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const RegistrySerialization::_RegPropertyMap RegistrySerialization::s_PropertyMa
{ _RegPropertyType::Dword, CONSOLE_REGISTRY_CURSORTYPE, SET_FIELD_AND_SIZE(_CursorType) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_INTERCEPTCOPYPASTE, SET_FIELD_AND_SIZE(_fInterceptCopyPaste) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_TERMINALSCROLLING, SET_FIELD_AND_SIZE(_TerminalScrolling) },
{ _RegPropertyType::String, CONSOLE_REGISTRY_ANSWERBACKMESSAGE, SET_FIELD_AND_SIZE(_answerbackMessage) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_USEDX, SET_FIELD_AND_SIZE(_fUseDx) },
{ _RegPropertyType::Boolean, CONSOLE_REGISTRY_COPYCOLOR, SET_FIELD_AND_SIZE(_fCopyColor) },
{ _RegPropertyType::Dword, L"TextMeasurement", SET_FIELD_AND_SIZE(_textMeasurement) },
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/ITermDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Microsoft::Console::VirtualTerminal::ITermDispatch
virtual bool SetAnsiMode(const bool ansiMode) = 0; // DECANM
virtual bool SetTopBottomScrollingMargins(const VTInt topMargin, const VTInt bottomMargin) = 0; // DECSTBM
virtual bool SetLeftRightScrollingMargins(const VTInt leftMargin, const VTInt rightMargin) = 0; // DECSLRM
virtual bool EnquireAnswerback() = 0; // ENQ
virtual bool WarningBell() = 0; // BEL
virtual bool CarriageReturn() = 0; // CR
virtual bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) = 0; // IND, NEL, LF, FF, VT
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/ITerminalApi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ namespace Microsoft::Console::VirtualTerminal
virtual void SetSystemMode(const Mode mode, const bool enabled) = 0;
virtual bool GetSystemMode(const Mode mode) const = 0;

virtual void ReturnAnswerback() = 0;
virtual void WarningBell() = 0;
virtual void SetWindowTitle(const std::wstring_view title) = 0;
virtual void UseAlternateScreenBuffer(const TextAttribute& attrs) = 0;
Expand Down
12 changes: 12 additions & 0 deletions src/terminal/adapter/adaptDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2476,6 +2476,18 @@ bool AdaptDispatch::SetLeftRightScrollingMargins(const VTInt leftMargin,
return true;
}

// Routine Description:
// - ENQ - Directs the terminal to send the answerback message.
// Arguments:
// - None
// Return Value:
// - True.
bool AdaptDispatch::EnquireAnswerback()
{
_api.ReturnAnswerback();
return true;
}

// Routine Description:
// - BEL - Rings the warning bell.
// Causes the terminal to emit an audible tone of brief duration.
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/adaptDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ namespace Microsoft::Console::VirtualTerminal
const VTInt bottomMargin) override; // DECSTBM
bool SetLeftRightScrollingMargins(const VTInt leftMargin,
const VTInt rightMargin) override; // DECSLRM
bool EnquireAnswerback() override; // ENQ
bool WarningBell() override; // BEL
bool CarriageReturn() override; // CR
bool LineFeed(const DispatchTypes::LineFeedType lineFeedType) override; // IND, NEL, LF, FF, VT
Expand Down
1 change: 1 addition & 0 deletions src/terminal/adapter/termDispatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Microsoft::Console::VirtualTerminal::TermDispatch : public Microsoft::Cons
bool SetAnsiMode(const bool /*ansiMode*/) override { return false; } // DECANM
bool SetTopBottomScrollingMargins(const VTInt /*topMargin*/, const VTInt /*bottomMargin*/) override { return false; } // DECSTBM
bool SetLeftRightScrollingMargins(const VTInt /*leftMargin*/, const VTInt /*rightMargin*/) override { return false; } // DECSLRM
bool EnquireAnswerback() override { return false; } // ENQ
bool WarningBell() override { return false; } // BEL
bool CarriageReturn() override { return false; } // CR
bool LineFeed(const DispatchTypes::LineFeedType /*lineFeedType*/) override { return false; } // IND, NEL, LF, FF, VT
Expand Down
5 changes: 5 additions & 0 deletions src/terminal/adapter/ut_adapter/adapterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ class TestGetSet final : public ITerminalApi
return _systemMode.test(mode);
}

void ReturnAnswerback()
{
Log::Comment(L"ReturnAnswerback MOCK called...");
}

void WarningBell() override
{
Log::Comment(L"WarningBell MOCK called...");
Expand Down
4 changes: 1 addition & 3 deletions src/terminal/parser/OutputStateMachineEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ bool OutputStateMachineEngine::ActionExecute(const wchar_t wch)
switch (wch)
{
case AsciiChars::ENQ:
// GH#11946: At some point we may want to add support for the VT
// answerback feature, which requires responding to an ENQ control
// with a user-defined reply, but until then we just ignore it.
_dispatch->EnquireAnswerback();
break;
case AsciiChars::BEL:
_dispatch->WarningBell();
Expand Down

0 comments on commit 746cf1f

Please sign in to comment.