Pass <Alt> to the application (#6461)

For mysterious reasons lost to the sands of time, XAML will _never_ pass
us a VK_MENU event. This is something that'll probably get fixed in
WinUI 3, but considering we're stuck on system XAML for the time being,
the only way to work around this bug is to pass the event through
manually. This change generalizes the F7 handler into a "direct key
event" handler that uses the same focus and tunneling method to send
different key events, and then uses it to send VK_MENU.

## Validation Steps Performed

Opened the debug tap, verified that I was seeing alt key ups.
Also used some alt keybindings to make sure I didn't break them.

Closes #6421
This commit is contained in:
Mike Griese 2020-06-11 17:41:16 -05:00 committed by GitHub
parent 827cc42a45
commit e8ece1645c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 74 additions and 49 deletions

View file

@ -12,6 +12,7 @@ IBind
IClass
IComparable
ICustom
IDirect
IExplorer
IMap
IObject

View file

@ -906,20 +906,21 @@ namespace winrt::TerminalApp::implementation
// Method Description:
// - Implements the F7 handler (per GH#638)
// - Implements the Alt handler (per GH#6421)
// Return value:
// - whether F7 was handled
bool AppLogic::OnF7Pressed()
// - whether the key was handled
bool AppLogic::OnDirectKeyEvent(const uint32_t vkey, const bool down)
{
if (_root)
{
// Manually bubble the OnF7Pressed event up through the focus tree.
// Manually bubble the OnDirectKeyEvent event up through the focus tree.
auto xamlRoot{ _root->XamlRoot() };
auto focusedObject{ Windows::UI::Xaml::Input::FocusManager::GetFocusedElement(xamlRoot) };
do
{
if (auto f7Listener{ focusedObject.try_as<IF7Listener>() })
if (auto keyListener{ focusedObject.try_as<IDirectKeyListener>() })
{
if (f7Listener.OnF7Pressed())
if (keyListener.OnDirectKeyEvent(vkey, down))
{
return true;
}

View file

@ -45,7 +45,7 @@ namespace winrt::TerminalApp::implementation
hstring Title();
void TitlebarClicked();
bool OnF7Pressed();
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
void WindowCloseButtonClicked();

View file

@ -3,7 +3,7 @@
import "../TerminalPage.idl";
import "../ShortcutActionDispatch.idl";
import "../IF7Listener.idl";
import "../IDirectKeyListener.idl";
namespace TerminalApp
{
@ -14,7 +14,7 @@ namespace TerminalApp
FullscreenMode,
};
[default_interface] runtimeclass AppLogic : IF7Listener
[default_interface] runtimeclass AppLogic : IDirectKeyListener
{
AppLogic();

View file

@ -7,10 +7,8 @@ namespace TerminalApp
// Instead, we just pin the uuid and include it in both TermControl and App
// If you update this one, please update the one in TerminalControl\TermControl.idl
// If you change this interface, please update the guid.
// If you press F7 and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")]
interface IF7Listener
{
Boolean OnF7Pressed();
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
}
}

View file

@ -201,7 +201,7 @@
<ItemGroup>
<!-- If you add idl files here, make sure to include their implementation's
header in TerminalApp.vcxproj (as well as in this file) -->
<Midl Include="../IF7Listener.idl" />
<Midl Include="../IDirectKeyListener.idl" />
<Midl Include="../App.idl">
<DependentUpon>../App.xaml</DependentUpon>
</Midl>

View file

@ -683,36 +683,46 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
}
// Method Description:
// - Manually generate an F7 event into the key bindings or terminal.
// This is required as part of GH#638.
// - Manually handles key events for certain keys that can't be passed to us
// normally. Namely, the keys we're concerned with are F7 down and Alt up.
// Return value:
// - Whether F7 was handled.
bool TermControl::OnF7Pressed()
// - Whether the key was handled.
bool TermControl::OnDirectKeyEvent(const uint32_t vkey, const bool down)
{
bool handled{ false };
auto bindings{ _settings.KeyBindings() };
const auto modifiers{ _GetPressedModifierKeys() };
if (bindings)
auto handled = false;
if (vkey == VK_MENU && !down)
{
handled = bindings.TryKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
VK_F7,
});
}
if (!handled)
{
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
(void)_TrySendKeyEvent(VK_F7, 0, modifiers, true);
// GH#6438: Note that we're _not_ sending the key up here - that'll
// get passed through XAML to our KeyUp handler normally.
// Manually generate an Alt KeyUp event into the key bindings or terminal.
// This is required as part of GH#6421.
(void)_TrySendKeyEvent(VK_MENU, 0, modifiers, false);
handled = true;
}
else if (vkey == VK_F7 && down)
{
// Manually generate an F7 event into the key bindings or terminal.
// This is required as part of GH#638.
auto bindings{ _settings.KeyBindings() };
if (bindings)
{
handled = bindings.TryKeyChord({
modifiers.IsCtrlPressed(),
modifiers.IsAltPressed(),
modifiers.IsShiftPressed(),
VK_F7,
});
}
if (!handled)
{
// _TrySendKeyEvent pretends it didn't handle F7 for some unknown reason.
(void)_TrySendKeyEvent(VK_F7, 0, modifiers, true);
// GH#6438: Note that we're _not_ sending the key up here - that'll
// get passed through XAML to our KeyUp handler normally.
handled = true;
}
}
return handled;
}

View file

@ -84,7 +84,7 @@ namespace winrt::Microsoft::Terminal::TerminalControl::implementation
void CreateSearchBoxControl();
bool OnF7Pressed();
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
bool OnMouseWheel(const Windows::Foundation::Point location, const int32_t delta);

View file

@ -11,13 +11,11 @@ namespace Microsoft.Terminal.TerminalControl
// C++/winrt makes it difficult to share this idl between two projects,
// Instead, we just pin the uuid and include it in both TermControl and App
// If you update this one, please update TerminalApp\IF7Listener.idl.
// If you update this one, please update TerminalApp\IDirectKeyListener.idl.
// If you change this interface, please update the guid.
// If you press F7 and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")]
interface IF7Listener
{
Boolean OnF7Pressed();
// If you press F7 or Alt and get a runtime error, go make sure both copies are the same.
[uuid("339e1a87-5315-4da6-96f0-565549b6472b")] interface IDirectKeyListener {
Boolean OnDirectKeyEvent(UInt32 vkey, Boolean down);
}
runtimeclass CopyToClipboardEventArgs
@ -32,7 +30,7 @@ namespace Microsoft.Terminal.TerminalControl
void HandleClipboardData(String data);
}
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IF7Listener, IMouseWheelListener
[default_interface] runtimeclass TermControl : Windows.UI.Xaml.Controls.UserControl, IDirectKeyListener, IMouseWheelListener
{
TermControl();
TermControl(Microsoft.Terminal.Settings.IControlSettings settings, Microsoft.Terminal.TerminalConnection.ITerminalConnection connection);

View file

@ -63,11 +63,11 @@ AppHost::~AppHost()
_app = nullptr;
}
bool AppHost::OnF7Pressed()
bool AppHost::OnDirectKeyEvent(const uint32_t vkey, const bool down)
{
if (_logic)
{
return _logic.OnF7Pressed();
return _logic.OnDirectKeyEvent(vkey, down);
}
return false;
}

View file

@ -17,7 +17,7 @@ public:
void AppTitleChanged(const winrt::Windows::Foundation::IInspectable& sender, winrt::hstring newTitle);
void LastTabClosed(const winrt::Windows::Foundation::IInspectable& sender, const winrt::TerminalApp::LastTabClosedEventArgs& args);
void Initialize();
bool OnF7Pressed();
bool OnDirectKeyEvent(const uint32_t vkey, const bool down);
private:
bool _useNonClientArea;

View file

@ -77,6 +77,10 @@ static bool _messageIsF7Keypress(const MSG& message)
{
return (message.message == WM_KEYDOWN || message.message == WM_SYSKEYDOWN) && message.wParam == VK_F7;
}
static bool _messageIsAltKeyup(const MSG& message)
{
return (message.message == WM_KEYUP || message.message == WM_SYSKEYUP) && message.wParam == VK_MENU;
}
int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
@ -137,13 +141,26 @@ int __stdcall wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
// been handled we can discard the message before we even translate it.
if (_messageIsF7Keypress(message))
{
if (host.OnF7Pressed())
if (host.OnDirectKeyEvent(VK_F7, true))
{
// The application consumed the F7. Don't let Xaml get it.
continue;
}
}
// GH#6421 - System XAML will never send an Alt KeyUp event. So, similar
// to how we'll steal the F7 KeyDown above, we'll steal the Alt KeyUp
// here, and plumb it through.
if (_messageIsAltKeyup(message))
{
// Let's pass <Alt> to the application
if (host.OnDirectKeyEvent(VK_MENU, false))
{
// The application consumed the Alt. Don't let Xaml get it.
continue;
}
}
TranslateMessage(&message);
DispatchMessage(&message);
}