From 09cc5f492c4bf8ffcba3caffabe23283e23c8f14 Mon Sep 17 00:00:00 2001 From: James Holderness Date: Thu, 1 Oct 2020 02:00:06 +0100 Subject: [PATCH] Add support for the BEL control in Windows Terminal (#7679) This commit makes the Windows Terminal play an audible sound when the `BEL` control character is output. The `BEL` control was already being forwarded through conpty, so it was just a matter of hooking up the `WarningBell` dispatch method to actually play a sound. I've used the `PlaySound` API to output the sound configured for the "Critical Stop" system event (aka _SystemHand_), since that is the sound used in conhost. ## Validation I've manually confirmed that the terminal produces the expected sound when executing `echo ^G` in a cmd shell, or `printf "\a"` in a WSL bash shell. References: * There is a separate issue (#1608) to deal with configuring the `BEL` to trigger visual forms of notification. * There is also an issue (#2360) requesting an option to disable the `BEL`. Closes #4046 --- src/cascadia/TerminalApp/TerminalPage.cpp | 15 +++++++++++++++ src/cascadia/TerminalApp/TerminalPage.h | 1 + src/cascadia/TerminalControl/TermControl.cpp | 8 ++++++++ src/cascadia/TerminalControl/TermControl.h | 2 ++ src/cascadia/TerminalControl/TermControl.idl | 1 + src/cascadia/TerminalCore/ITerminalApi.hpp | 1 + src/cascadia/TerminalCore/Terminal.cpp | 5 +++++ src/cascadia/TerminalCore/Terminal.hpp | 3 +++ src/cascadia/TerminalCore/TerminalApi.cpp | 8 ++++++++ src/cascadia/TerminalCore/TerminalDispatch.cpp | 7 +++++++ src/cascadia/TerminalCore/TerminalDispatch.hpp | 1 + 11 files changed, 52 insertions(+) diff --git a/src/cascadia/TerminalApp/TerminalPage.cpp b/src/cascadia/TerminalApp/TerminalPage.cpp index 8dc7142a3f0..66b65dfdfcc 100644 --- a/src/cascadia/TerminalApp/TerminalPage.cpp +++ b/src/cascadia/TerminalApp/TerminalPage.cpp @@ -8,6 +8,7 @@ #include "AppLogic.h" #include "../../types/inc/utils.hpp" +#include #include #include "TerminalPage.g.cpp" @@ -1141,6 +1142,8 @@ namespace winrt::TerminalApp::implementation // Add an event handler when the terminal wants to paste data from the Clipboard. term.PasteFromClipboard({ this, &TerminalPage::_PasteFromClipboardHandler }); + term.WarningBell({ this, &TerminalPage::_WarningBellHandler }); + term.OpenHyperlink({ this, &TerminalPage::_OpenHyperlinkHandler }); // Bind Tab events to the TermControl and the Tab's Pane @@ -1801,6 +1804,18 @@ namespace winrt::TerminalApp::implementation CATCH_LOG(); } + // Method Description: + // - Plays a warning note when triggered by the BEL control character, + // using the sound configured for the "Critical Stop" system event. + // This matches the behavior of the Windows Console host. + // Arguments: + // - + void TerminalPage::_WarningBellHandler(const IInspectable sender, const IInspectable eventArgs) + { + const auto soundAlias = reinterpret_cast(SND_ALIAS_SYSTEMHAND); + PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY); + } + void TerminalPage::_OpenHyperlinkHandler(const IInspectable /*sender*/, const Microsoft::Terminal::TerminalControl::OpenHyperlinkEventArgs eventArgs) { try diff --git a/src/cascadia/TerminalApp/TerminalPage.h b/src/cascadia/TerminalApp/TerminalPage.h index 540921c0c23..4874627b10c 100644 --- a/src/cascadia/TerminalApp/TerminalPage.h +++ b/src/cascadia/TerminalApp/TerminalPage.h @@ -176,6 +176,7 @@ namespace winrt::TerminalApp::implementation winrt::fire_and_forget _PasteFromClipboardHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::PasteFromClipboardEventArgs eventArgs); + void _WarningBellHandler(const IInspectable sender, const IInspectable eventArgs); void _OpenHyperlinkHandler(const IInspectable sender, const Microsoft::Terminal::TerminalControl::OpenHyperlinkEventArgs eventArgs); void _ShowCouldNotOpenDialog(winrt::hstring reason, winrt::hstring uri); bool _CopyText(const bool singleLine, const Windows::Foundation::IReference& formats); diff --git a/src/cascadia/TerminalControl/TermControl.cpp b/src/cascadia/TerminalControl/TermControl.cpp index 2c9d3e65a08..299b6df5124 100644 --- a/src/cascadia/TerminalControl/TermControl.cpp +++ b/src/cascadia/TerminalControl/TermControl.cpp @@ -83,6 +83,9 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation _terminal = std::make_unique<::Microsoft::Terminal::Core::Terminal>(); + auto pfnWarningBell = std::bind(&TermControl::_TerminalWarningBell, this); + _terminal->SetWarningBellCallback(pfnWarningBell); + auto pfnTitleChanged = std::bind(&TermControl::_TerminalTitleChanged, this, std::placeholders::_1); _terminal->SetTitleChangedCallback(pfnTitleChanged); @@ -2172,6 +2175,11 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation } } + void TermControl::_TerminalWarningBell() + { + _WarningBellHandlers(*this, nullptr); + } + void TermControl::_TerminalTitleChanged(const std::wstring_view& wstr) { _titleChangedHandlers(winrt::hstring{ wstr }); diff --git a/src/cascadia/TerminalControl/TermControl.h b/src/cascadia/TerminalControl/TermControl.h index 6fb42cf405d..dd1118c3f1c 100644 --- a/src/cascadia/TerminalControl/TermControl.h +++ b/src/cascadia/TerminalControl/TermControl.h @@ -149,6 +149,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(CopyToClipboard, _clipboardCopyHandlers, TerminalControl::TermControl, TerminalControl::CopyToClipboardEventArgs); DECLARE_EVENT_WITH_TYPED_EVENT_HANDLER(OpenHyperlink, _openHyperlinkHandlers, TerminalControl::TermControl, TerminalControl::OpenHyperlinkEventArgs); + TYPED_EVENT(WarningBell, IInspectable, IInspectable); TYPED_EVENT(ConnectionStateChanged, TerminalControl::TermControl, IInspectable); TYPED_EVENT(Initialized, TerminalControl::TermControl, Windows::UI::Xaml::RoutedEventArgs); TYPED_EVENT(TabColorChanged, IInspectable, IInspectable); @@ -261,6 +262,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation void _SwapChainScaleChanged(Windows::UI::Xaml::Controls::SwapChainPanel const& sender, Windows::Foundation::IInspectable const& args); void _DoResizeUnderLock(const double newWidth, const double newHeight); void _RefreshSizeUnderLock(); + void _TerminalWarningBell(); void _TerminalTitleChanged(const std::wstring_view& wstr); void _TerminalTabColorChanged(const std::optional color); void _CopyToClipboard(const std::wstring_view& wstr); diff --git a/src/cascadia/TerminalControl/TermControl.idl b/src/cascadia/TerminalControl/TermControl.idl index 8c570838b82..d8bc580e0e9 100644 --- a/src/cascadia/TerminalControl/TermControl.idl +++ b/src/cascadia/TerminalControl/TermControl.idl @@ -60,6 +60,7 @@ namespace Microsoft.Terminal.TerminalControl event Windows.Foundation.TypedEventHandler CopyToClipboard; event Windows.Foundation.TypedEventHandler PasteFromClipboard; event Windows.Foundation.TypedEventHandler OpenHyperlink; + event Windows.Foundation.TypedEventHandler WarningBell; event Windows.Foundation.TypedEventHandler Initialized; // This is an event handler forwarder for the underlying connection. diff --git a/src/cascadia/TerminalCore/ITerminalApi.hpp b/src/cascadia/TerminalCore/ITerminalApi.hpp index 9661a2db51d..fda9cef77cc 100644 --- a/src/cascadia/TerminalCore/ITerminalApi.hpp +++ b/src/cascadia/TerminalCore/ITerminalApi.hpp @@ -34,6 +34,7 @@ namespace Microsoft::Terminal::Core virtual bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept = 0; virtual bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept = 0; + virtual bool WarningBell() noexcept = 0; virtual bool SetWindowTitle(std::wstring_view title) noexcept = 0; virtual bool SetColorTableEntry(const size_t tableIndex, const DWORD color) noexcept = 0; diff --git a/src/cascadia/TerminalCore/Terminal.cpp b/src/cascadia/TerminalCore/Terminal.cpp index d1a8cf43ffa..c3c4f58d7c6 100644 --- a/src/cascadia/TerminalCore/Terminal.cpp +++ b/src/cascadia/TerminalCore/Terminal.cpp @@ -965,6 +965,11 @@ void Terminal::SetWriteInputCallback(std::function pfn) noe _pfnWriteInput.swap(pfn); } +void Terminal::SetWarningBellCallback(std::function pfn) noexcept +{ + _pfnWarningBell.swap(pfn); +} + void Terminal::SetTitleChangedCallback(std::function pfn) noexcept { _pfnTitleChanged.swap(pfn); diff --git a/src/cascadia/TerminalCore/Terminal.hpp b/src/cascadia/TerminalCore/Terminal.hpp index bdec0a2a319..b1f6c25e920 100644 --- a/src/cascadia/TerminalCore/Terminal.hpp +++ b/src/cascadia/TerminalCore/Terminal.hpp @@ -90,6 +90,7 @@ class Microsoft::Terminal::Core::Terminal final : bool EraseCharacters(const size_t numChars) noexcept override; bool EraseInLine(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; bool EraseInDisplay(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::EraseType eraseType) noexcept override; + bool WarningBell() noexcept override; bool SetWindowTitle(std::wstring_view title) noexcept override; bool SetColorTableEntry(const size_t tableIndex, const COLORREF color) noexcept override; bool SetCursorStyle(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::CursorStyle cursorStyle) noexcept override; @@ -175,6 +176,7 @@ class Microsoft::Terminal::Core::Terminal final : #pragma endregion void SetWriteInputCallback(std::function pfn) noexcept; + void SetWarningBellCallback(std::function pfn) noexcept; void SetTitleChangedCallback(std::function pfn) noexcept; void SetTabColorChangedCallback(std::function)> pfn) noexcept; void SetCopyToClipboardCallback(std::function pfn) noexcept; @@ -207,6 +209,7 @@ class Microsoft::Terminal::Core::Terminal final : private: std::function _pfnWriteInput; + std::function _pfnWarningBell; std::function _pfnTitleChanged; std::function _pfnCopyToClipboard; std::function _pfnScrollPositionChanged; diff --git a/src/cascadia/TerminalCore/TerminalApi.cpp b/src/cascadia/TerminalCore/TerminalApi.cpp index 9a7e896fe95..00c39d25bcc 100644 --- a/src/cascadia/TerminalCore/TerminalApi.cpp +++ b/src/cascadia/TerminalCore/TerminalApi.cpp @@ -345,6 +345,14 @@ try } CATCH_LOG_RETURN_FALSE() +bool Terminal::WarningBell() noexcept +try +{ + _pfnWarningBell(); + return true; +} +CATCH_LOG_RETURN_FALSE() + bool Terminal::SetWindowTitle(std::wstring_view title) noexcept try { diff --git a/src/cascadia/TerminalCore/TerminalDispatch.cpp b/src/cascadia/TerminalCore/TerminalDispatch.cpp index 06874b38889..0db4a539f08 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.cpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.cpp @@ -109,6 +109,13 @@ try } CATCH_LOG_RETURN_FALSE() +bool TerminalDispatch::WarningBell() noexcept +try +{ + return _terminalApi.WarningBell(); +} +CATCH_LOG_RETURN_FALSE() + bool TerminalDispatch::CarriageReturn() noexcept try { diff --git a/src/cascadia/TerminalCore/TerminalDispatch.hpp b/src/cascadia/TerminalCore/TerminalDispatch.hpp index 993433f43f2..77458ea876d 100644 --- a/src/cascadia/TerminalCore/TerminalDispatch.hpp +++ b/src/cascadia/TerminalCore/TerminalDispatch.hpp @@ -30,6 +30,7 @@ class TerminalDispatch : public Microsoft::Console::VirtualTerminal::TermDispatc bool LineFeed(const ::Microsoft::Console::VirtualTerminal::DispatchTypes::LineFeedType lineFeedType) noexcept override; bool EraseCharacters(const size_t numChars) noexcept override; + bool WarningBell() noexcept override; bool CarriageReturn() noexcept override; bool SetWindowTitle(std::wstring_view title) noexcept override;