Skip to content

Commit

Permalink
Add a setting to flash the pane when BEL is emitted (#9270)
Browse files Browse the repository at this point in the history
<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? -->
## Summary of the Pull Request
Adds a new bellStyle called `window`. When `window` is set and a BEL is emitted, we flash the pane that emitted it. 

Additionally, changes bellStyle in the SUI to a list of checkboxes instead of radio buttons, to match bellStyle being a flag-enum. Deprecates 'BellStyle::Visual' in the schema, but still allows it to be set in the json (it maps to `Window | Taskbar`)

<!-- Other than the issue solved, is this relevant to any other issues/existing PRs? --> 
## References
#6700 

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [ ] Closes #xxx
* [x] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/Terminal) and sign the CLA
* [ ] Tests added/passed
* [ ] Documentation updated. If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/terminal) and link it here: #xxx
* [ ] Schema updated.
* [x] I work here

<!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
GIF in Teams
  • Loading branch information
PankajBhojwani authored May 24, 2021
1 parent 27582a9 commit 227ec37
Show file tree
Hide file tree
Showing 21 changed files with 305 additions and 22 deletions.
8 changes: 5 additions & 3 deletions doc/cascadia/profiles.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,17 @@
"type": "string",
"enum": [
"audible",
"visual"
"window",
"taskbar"
]
}
},
{
"type": "string",
"enum": [
"audible",
"visual",
"taskbar",
"window",
"all",
"none"
]
Expand Down Expand Up @@ -1194,7 +1196,7 @@
},
"bellStyle": {
"default": "audible",
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound and flash the taskbar icon. An array of specific behaviors can also be used. Supported array values include `audible` and `visual`. When set to \"none\", nothing will happen.",
"description": "Controls what happens when the application emits a BEL character. When set to \"all\", the Terminal will play a sound, flash the taskbar icon (if the terminal window is not in focus) and flash the window. An array of specific behaviors can also be used. Supported array values include `audible`, `window` and `taskbar`. When set to \"none\", nothing will happen.",
"$ref": "#/definitions/BellStyle"
},
"closeOnExit": {
Expand Down
9 changes: 7 additions & 2 deletions src/cascadia/TerminalApp/Pane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,8 +378,13 @@ void Pane::_ControlWarningBellHandler(const winrt::Windows::Foundation::IInspect
PlaySound(soundAlias, NULL, SND_ALIAS_ID | SND_ASYNC | SND_SENTRY);
}

// raise the event with the bool value corresponding to the visual flag
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Visual));
if (WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Window))
{
_control.BellLightOn();
}

// raise the event with the bool value corresponding to the taskbar flag
_PaneRaiseBellHandlers(nullptr, WI_IsFlagSet(paneProfile.BellStyle(), winrt::Microsoft::Terminal::Settings::Model::BellStyle::Taskbar));
}
}
}
Expand Down
44 changes: 43 additions & 1 deletion src/cascadia/TerminalControl/TermControl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_lastAutoScrollUpdateTime{ std::nullopt },
_cursorTimer{},
_blinkTimer{},
_searchBox{ nullptr }
_searchBox{ nullptr },
_bellLightAnimation{ Window::Current().Compositor().CreateScalarKeyFrameAnimation() }
{
InitializeComponent();

Expand Down Expand Up @@ -167,6 +168,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
_autoScrollTimer.Interval(AutoScrollUpdateInterval);
_autoScrollTimer.Tick({ this, &TermControl::_UpdateAutoScroll });

// Add key frames and a duration to our bell light animation
_bellLightAnimation.InsertKeyFrame(0.0, 2.0);
_bellLightAnimation.InsertKeyFrame(1.0, 1.0);
_bellLightAnimation.Duration(winrt::Windows::Foundation::TimeSpan(std::chrono::milliseconds(TerminalWarningBellInterval)));

_ApplyUISettings(_settings);
}

Expand Down Expand Up @@ -2376,6 +2382,42 @@ namespace winrt::Microsoft::Terminal::Control::implementation
return _core->TaskbarProgress();
}

void TermControl::BellLightOn()
{
Windows::Foundation::Numerics::float2 zeroSize{ 0, 0 };
// If the grid has 0 size or if the bell timer is
// already active, do nothing
if (RootGrid().ActualSize() != zeroSize && !_bellLightTimer)
{
// Start the timer, when the timer ticks we switch off the light
DispatcherTimer invertTimer;
invertTimer.Interval(std::chrono::milliseconds(TerminalWarningBellInterval));
invertTimer.Tick({ get_weak(), &TermControl::_BellLightOff });
invertTimer.Start();
_bellLightTimer.emplace(std::move(invertTimer));

// Switch on the light and animate the intensity to fade out
VisualBellLight::SetIsTarget(RootGrid(), true);
BellLight().CompositionLight().StartAnimation(L"Intensity", _bellLightAnimation);
}
}

void TermControl::_BellLightOff(Windows::Foundation::IInspectable const& /* sender */,
Windows::Foundation::IInspectable const& /* e */)
{
if (_bellLightTimer)
{
// Stop the timer and switch off the light
_bellLightTimer->Stop();
_bellLightTimer.reset();

if (!_closing)
{
VisualBellLight::SetIsTarget(RootGrid(), false);
}
}
}

// Method Description:
// - Checks whether the control is in a read-only mode (in this mode node input is sent to connection).
// Return Value:
Expand Down
7 changes: 7 additions & 0 deletions src/cascadia/TerminalControl/TermControl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "TermControl.g.h"
#include "XamlLights.h"
#include "EventArgs.h"
#include "../../renderer/base/Renderer.hpp"
#include "../../renderer/dx/DxRenderer.hpp"
Expand Down Expand Up @@ -99,6 +100,8 @@ namespace winrt::Microsoft::Terminal::Control::implementation
const winrt::hstring& padding,
const uint32_t dpi);

void BellLightOn();

bool ReadOnly() const noexcept;
void ToggleReadOnly();

Expand Down Expand Up @@ -171,8 +174,11 @@ namespace winrt::Microsoft::Terminal::Control::implementation
Windows::UI::Xaml::DispatcherTimer _autoScrollTimer;
std::optional<std::chrono::high_resolution_clock::time_point> _lastAutoScrollUpdateTime;

winrt::Windows::UI::Composition::ScalarKeyFrameAnimation _bellLightAnimation;

std::optional<Windows::UI::Xaml::DispatcherTimer> _cursorTimer;
std::optional<Windows::UI::Xaml::DispatcherTimer> _blinkTimer;
std::optional<Windows::UI::Xaml::DispatcherTimer> _bellLightTimer;

event_token _coreOutputEventToken;

Expand Down Expand Up @@ -209,6 +215,7 @@ namespace winrt::Microsoft::Terminal::Control::implementation

void _CursorTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
void _BlinkTimerTick(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);
void _BellLightOff(Windows::Foundation::IInspectable const& sender, Windows::Foundation::IInspectable const& e);

void _SetEndSelectionPointAtCursor(Windows::Foundation::Point const& cursorPosition);

Expand Down
2 changes: 2 additions & 0 deletions src/cascadia/TerminalControl/TermControl.idl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ namespace Microsoft.Terminal.Control
void ToggleShaderEffects();
void SendInput(String input);

void BellLightOn();

Boolean ReadOnly { get; };
void ToggleReadOnly();
}
Expand Down
3 changes: 3 additions & 0 deletions src/cascadia/TerminalControl/TermControl.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
-->

<Grid x:Name="RootGrid">
<Grid.Lights>
<local:VisualBellLight x:Name="BellLight" />
</Grid.Lights>
<Image x:Name="BackgroundImage"
AutomationProperties.AccessibilityView="Raw" />
<Grid>
Expand Down
7 changes: 7 additions & 0 deletions src/cascadia/TerminalControl/TerminalControlLib.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<ClInclude Include="SearchBoxControl.h">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClInclude>
<ClInclude Include="XamlLights.h">
<DependentUpon>XamlLights.idl</DependentUpon>
</ClInclude>
<ClInclude Include="TermControl.h">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClInclude>
Expand Down Expand Up @@ -76,6 +79,9 @@
<ClCompile Include="SearchBoxControl.cpp">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</ClCompile>
<ClCompile Include="XamlLights.cpp">
<DependentUpon>XamlLights.idl</DependentUpon>
</ClCompile>
<ClCompile Include="TermControl.cpp">
<DependentUpon>TermControl.xaml</DependentUpon>
</ClCompile>
Expand All @@ -102,6 +108,7 @@
<Midl Include="SearchBoxControl.idl">
<DependentUpon>SearchBoxControl.xaml</DependentUpon>
</Midl>
<Midl Include="XamlLights.idl" />
<Midl Include="TermControl.idl">
<DependentUpon>TermControl.xaml</DependentUpon>
</Midl>
Expand Down
107 changes: 107 additions & 0 deletions src/cascadia/TerminalControl/XamlLights.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "pch.h"
#include "TermControl.h"
#include "XamlLights.h"
#include "VisualBellLight.g.cpp"

using namespace winrt::Windows::UI::Xaml;
using namespace winrt::Windows::UI::Xaml::Media;

namespace winrt::Microsoft::Terminal::Control::implementation
{
DependencyProperty VisualBellLight::_IsTargetProperty{ nullptr };

VisualBellLight::VisualBellLight()
{
_InitializeProperties();
}

void VisualBellLight::_InitializeProperties()
{
// Initialize any dependency properties here.
// This performs a lazy load on these properties, instead of
// initializing them when the DLL loads.
if (!_IsTargetProperty)
{
_IsTargetProperty =
DependencyProperty::RegisterAttached(
L"IsTarget",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<Control::VisualBellLight>(),
PropertyMetadata{ winrt::box_value(false), PropertyChangedCallback{ &VisualBellLight::OnIsTargetChanged } });
}
}

// Method Description:
// - This function is called when the first target UIElement is shown on the screen,
// this enables delaying composition object creation until it's actually necessary.
// Arguments:
// - newElement: unused
void VisualBellLight::OnConnected(UIElement const& /* newElement */)
{
if (!CompositionLight())
{
auto spotLight{ Window::Current().Compositor().CreateAmbientLight() };
spotLight.Color(Windows::UI::Colors::White());
CompositionLight(spotLight);
}
}

// Method Description:
// - This function is called when there are no more target UIElements on the screen
// - Disposes of composition resources when no longer in use
// Arguments:
// - oldElement: unused
void VisualBellLight::OnDisconnected(UIElement const& /* oldElement */)
{
if (CompositionLight())
{
CompositionLight(nullptr);
}
}

winrt::hstring VisualBellLight::GetId()
{
return VisualBellLight::GetIdStatic();
}

void VisualBellLight::OnIsTargetChanged(DependencyObject const& d, DependencyPropertyChangedEventArgs const& e)
{
const auto uielem{ d.try_as<UIElement>() };
const auto brush{ d.try_as<Brush>() };

if (!uielem && !brush)
{
// terminate early
return;
}

const auto isAdding = winrt::unbox_value<bool>(e.NewValue());
const auto id = GetIdStatic();

if (isAdding)
{
if (uielem)
{
XamlLight::AddTargetElement(id, uielem);
}
else
{
XamlLight::AddTargetBrush(id, brush);
}
}
else
{
if (uielem)
{
XamlLight::RemoveTargetElement(id, uielem);
}
else
{
XamlLight::RemoveTargetBrush(id, brush);
}
}
}
}
49 changes: 49 additions & 0 deletions src/cascadia/TerminalControl/XamlLights.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#include "cppwinrt_utils.h"
#include "VisualBellLight.g.h"

namespace winrt::Microsoft::Terminal::Control::implementation
{
struct VisualBellLight : VisualBellLightT<VisualBellLight>
{
VisualBellLight();

winrt::hstring GetId();

static Windows::UI::Xaml::DependencyProperty IsTargetProperty() { return _IsTargetProperty; }

static bool GetIsTarget(Windows::UI::Xaml::DependencyObject const& target)
{
return winrt::unbox_value<bool>(target.GetValue(_IsTargetProperty));
}

static void SetIsTarget(Windows::UI::Xaml::DependencyObject const& target, bool value)
{
target.SetValue(_IsTargetProperty, winrt::box_value(value));
}

void OnConnected(Windows::UI::Xaml::UIElement const& newElement);
void OnDisconnected(Windows::UI::Xaml::UIElement const& oldElement);

static void OnIsTargetChanged(Windows::UI::Xaml::DependencyObject const& d, Windows::UI::Xaml::DependencyPropertyChangedEventArgs const& e);

inline static winrt::hstring GetIdStatic()
{
// This specifies the unique name of the light. In most cases you should use the type's full name.
return winrt::xaml_typename<winrt::Microsoft::Terminal::Control::VisualBellLight>().Name;
}

private:
static void _InitializeProperties();
static Windows::UI::Xaml::DependencyProperty _IsTargetProperty;
};
}

namespace winrt::Microsoft::Terminal::Control::factory_implementation
{
BASIC_FACTORY(VisualBellLight);
}
13 changes: 13 additions & 0 deletions src/cascadia/TerminalControl/XamlLights.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

namespace Microsoft.Terminal.Control
{
[default_interface] runtimeclass VisualBellLight : Windows.UI.Xaml.Media.XamlLight
{
VisualBellLight();
static Windows.UI.Xaml.DependencyProperty IsTargetProperty { get; };
static Boolean GetIsTarget(Windows.UI.Xaml.DependencyObject target);
static void SetIsTarget(Windows.UI.Xaml.DependencyObject target, Boolean value);
}
}
Loading

0 comments on commit 227ec37

Please sign in to comment.