[KBM] Migrate Engine and Editor into separate processes (#10774)

* Move KBM engine into separate process (#10672)

* [KBM] Migrate KBM UI out of the runner (#10709)

* Clean up keyboard hook handles (#10817)

* [C++ common] Unhandled exception handler (#10821)

* [KBM] Use icon in the KeyboardManagerEditor (#10845)

* [KBM] Move resources from the Common project to the Editor. (#10844)

* KBM Editor tests (#10858)

* Rename engine executable (#10868)

* clean up (#10870)

* [KBM] Changed Editor and libraries output folders (#10871)

* [KBM] New logs structure (#10872)

* Add unhandled exception handling to the editor (#10874)

* [KBM] Trace for edit keyboard window

* Logging for XamlBridge message loop

* [KBM] Added Editor and Engine to the installer (#10876)

* Fix spelling

* Interprocess communication logs, remove unnecessary windows message logs

* [KBM] Separated telemetry for the engine and editor. (#10889)

* [KBM] Editor test project (#10891)

* Versions for the engine and the editor (#10897)

* Add the editor's and the engine's executables to signing process (#10900)

* [KBM editor] Run only one instance, exit when parent process exits (#10890)

* [KBM] Force kill editor process to avoid XAML crash (#10907)

* [KBM] Force kill editor process to avoid XAML crash

* Fix event releasing

Co-authored-by: mykhailopylyp <17161067+mykhailopylyp@users.noreply.github.com>

* Make the editor dpi aware (#10908)

* [KBM] KeyboardManagerCommon refactoring (#10909)

* Do not start the process if it is already started (#10910)

* logs

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp

* [KBM] Rename InitUnhandledExceptionHandler
to make it explicit that is for x64 only.
We will fix it properly when adding support for ARM64 and add a header with
the proper conditional building.

* [KBM] rename file/class/variables using camel case

* [KBM] Rename "event_locker" -> "EventLocker"

* [KBM] rename process_waiter
Add a TODO comment

* [KBM] rename methods
Add TODO comment

* [KBM] use uppercase for function names

* [KBM] use uppercase for methos, lowercase for properties

* [KBM] rename method, make methods private, formatting

* [KBM] rename private variables

* [KBM] use uppercase for function names

* [KBM] Added support to run the editor stand-alone when built in debug mode

* Update src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp

* Check success of event creation, comment (#10947)

* [KBM] code formatting (#10951)

* [KBM] code formatting

* Update src/modules/keyboardmanager/KeyboardManagerEditorLibrary/BufferValidationHelpers.cpp

* [KBM] tracing

* [KBM] Remappings not showing fix. (#10954)

* removed mutex

* retry loop for reading

* retry on reading config once

* log error

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>

Co-authored-by: Seraphima Zykova <zykovas91@gmail.com>
Co-authored-by: Enrico Giordani <enricogior@users.noreply.github.com>
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
This commit is contained in:
Mykhailo Pylyp
2021-04-26 22:01:38 +03:00
committed by GitHub
parent e9a0b58796
commit a8c99e9513
141 changed files with 5124 additions and 2374 deletions

View File

@@ -0,0 +1,362 @@
#include "pch.h"
#include "SingleKeyRemapControl.h"
#include <KeyboardManagerState.h>
#include <ShortcutControl.h>
#include <UIHelpers.h>
//Both static members are initialized to null
HWND SingleKeyRemapControl::EditKeyboardWindowHandle = nullptr;
KeyboardManagerState* SingleKeyRemapControl::keyboardManagerState = nullptr;
// Initialized as new vector
RemapBuffer SingleKeyRemapControl::singleKeyRemapBuffer;
SingleKeyRemapControl::SingleKeyRemapControl(StackPanel table, StackPanel row, const int colIndex)
{
typeKey = Button();
typeKey.as<Button>().Width(KeyboardManagerConstants::RemapTableDropDownWidth);
typeKey.as<Button>().Content(winrt::box_value(GET_RESOURCE_STRING(IDS_TYPE_BUTTON)));
singleKeyRemapControlLayout = StackPanel();
singleKeyRemapControlLayout.as<StackPanel>().Spacing(10);
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(typeKey.as<Button>());
// Key column
if (colIndex == 0)
{
keyDropDownControlObjects.emplace_back(std::make_unique<KeyDropDownControl>(false));
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(keyDropDownControlObjects[0]->GetComboBox());
// Set selection handler for the drop down
keyDropDownControlObjects[0]->SetSelectionHandler(table, row, colIndex, singleKeyRemapBuffer);
}
// Hybrid column
else
{
hybridDropDownStackPanel = StackPanel();
hybridDropDownStackPanel.as<StackPanel>().Spacing(KeyboardManagerConstants::ShortcutTableDropDownSpacing);
hybridDropDownStackPanel.as<StackPanel>().Orientation(Windows::UI::Xaml::Controls::Orientation::Horizontal);
KeyDropDownControl::AddDropDown(table, row, hybridDropDownStackPanel.as<StackPanel>(), colIndex, singleKeyRemapBuffer, keyDropDownControlObjects, nullptr, true, true);
singleKeyRemapControlLayout.as<StackPanel>().Children().Append(hybridDropDownStackPanel.as<StackPanel>());
}
typeKey.as<Button>().Click([&, table, colIndex, row](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
// Using the XamlRoot of the typeKey to get the root of the XAML host
if (colIndex == 0)
{
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectSingleKeyRemapWindowActivated, EditKeyboardWindowHandle);
createDetectKeyWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState);
}
else
{
keyboardManagerState->SetUIState(KeyboardManagerUIState::DetectShortcutWindowInEditKeyboardWindowActivated, EditKeyboardWindowHandle);
ShortcutControl::CreateDetectShortcutWindow(sender, sender.as<Button>().XamlRoot(), *keyboardManagerState, colIndex, table, keyDropDownControlObjects, row, nullptr, true, true, EditKeyboardWindowHandle, singleKeyRemapBuffer);
}
});
singleKeyRemapControlLayout.as<StackPanel>().UpdateLayout();
}
// Function to set the accessible names for all the controls in a row
void SingleKeyRemapControl::UpdateAccessibleNames(StackPanel sourceColumn, StackPanel mappedToColumn, Button deleteButton, int rowIndex)
{
sourceColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_SOURCEHEADER)));
mappedToColumn.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_EDITKEYBOARD_TARGETHEADER)));
deleteButton.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_AUTOMATIONPROPERTIES_ROW) + std::to_wstring(rowIndex) + L", " + GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
}
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
void SingleKeyRemapControl::AddNewControlKeyRemapRow(StackPanel& parent, std::vector<std::vector<std::unique_ptr<SingleKeyRemapControl>>>& keyboardRemapControlObjects, const DWORD originalKey, const KeyShortcutUnion newKey)
{
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
std::vector<std::unique_ptr<SingleKeyRemapControl>> newrow;
StackPanel row = StackPanel();
parent.Children().Append(row);
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 0));
newrow.emplace_back(std::make_unique<SingleKeyRemapControl>(parent, row, 1));
keyboardRemapControlObjects.push_back(std::move(newrow));
row.Padding({ 10, 10, 10, 10 });
row.Orientation(Orientation::Horizontal);
auto brush = Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundListLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>();
if (keyboardRemapControlObjects.size() % 2)
{
row.Background(brush);
}
// SingleKeyRemapControl for the original key.
auto originalElement = keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl();
originalElement.Width(KeyboardManagerConstants::RemapTableDropDownWidth);
row.Children().Append(originalElement);
// Arrow icon
FontIcon arrowIcon;
arrowIcon.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
arrowIcon.Glyph(L"\xE72A");
arrowIcon.VerticalAlignment(VerticalAlignment::Center);
arrowIcon.HorizontalAlignment(HorizontalAlignment::Center);
auto arrowIconContainer = UIHelpers::GetWrapped(arrowIcon, KeyboardManagerConstants::TableArrowColWidth).as<StackPanel>();
arrowIconContainer.Orientation(Orientation::Vertical);
arrowIconContainer.VerticalAlignment(VerticalAlignment::Center);
row.Children().Append(arrowIconContainer);
// SingleKeyRemapControl for the new remap key
auto targetElement = keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl();
targetElement.Width(KeyboardManagerConstants::ShortcutTargetColumnWidth);
row.Children().Append(targetElement);
// Set the key text if the two keys are not null (i.e. default args)
if (originalKey != NULL && !(newKey.index() == 0 && std::get<DWORD>(newKey) == NULL) && !(newKey.index() == 1 && !std::get<Shortcut>(newKey).IsValidShortcut()))
{
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ originalKey, newKey }, L""));
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][0]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(originalKey));
if (newKey.index() == 0)
{
keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects[0]->SetSelectedValue(std::to_wstring(std::get<DWORD>(newKey)));
}
else
{
KeyDropDownControl::AddShortcutToControl(std::get<Shortcut>(newKey), parent, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->hybridDropDownStackPanel.as<StackPanel>(), *keyboardManagerState, 1, keyboardRemapControlObjects[keyboardRemapControlObjects.size() - 1][1]->keyDropDownControlObjects, singleKeyRemapBuffer, row, nullptr, true, true);
}
}
else
{
// Initialize both keys to NULL
singleKeyRemapBuffer.push_back(std::make_pair<RemapBufferItem, std::wstring>(RemapBufferItem{ NULL, NULL }, L""));
}
// Delete row button
Windows::UI::Xaml::Controls::Button deleteRemapKeys;
FontIcon deleteSymbol;
deleteSymbol.FontFamily(Media::FontFamily(L"Segoe MDL2 Assets"));
deleteSymbol.Glyph(L"\xE74D");
deleteRemapKeys.Content(deleteSymbol);
deleteRemapKeys.Background(Media::SolidColorBrush(Colors::Transparent()));
deleteRemapKeys.HorizontalAlignment(HorizontalAlignment::Center);
deleteRemapKeys.Click([&, parent, row, brush](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
uint32_t rowIndex;
// Get index of delete button
UIElementCollection children = parent.Children();
bool indexFound = children.IndexOf(row, rowIndex);
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
if (!indexFound)
{
return;
}
// Update accessible names and background for each row after the deleted row
for (uint32_t i = rowIndex + 1; i < children.Size(); i++)
{
StackPanel row = children.GetAt(i).as<StackPanel>();
row.Background(i % 2 ? brush : Media::SolidColorBrush(Colors::Transparent()));
StackPanel sourceCol = row.Children().GetAt(0).as<StackPanel>();
StackPanel targetCol = row.Children().GetAt(2).as<StackPanel>();
Button delButton = row.Children().GetAt(3).as<Button>();
UpdateAccessibleNames(sourceCol, targetCol, delButton, i);
}
children.RemoveAt(rowIndex);
parent.UpdateLayout();
singleKeyRemapBuffer.erase(singleKeyRemapBuffer.begin() + rowIndex);
// delete the SingleKeyRemapControl objects so that they get destructed
keyboardRemapControlObjects.erase(keyboardRemapControlObjects.begin() + rowIndex);
});
// To set the accessible name of the delete button
deleteRemapKeys.SetValue(Automation::AutomationProperties::NameProperty(), box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
// Add tooltip for delete button which would appear on hover
ToolTip deleteRemapKeystoolTip;
deleteRemapKeystoolTip.Content(box_value(GET_RESOURCE_STRING(IDS_DELETE_REMAPPING_BUTTON)));
ToolTipService::SetToolTip(deleteRemapKeys, deleteRemapKeystoolTip);
row.Children().Append(deleteRemapKeys);
parent.UpdateLayout();
// Set accessible names
UpdateAccessibleNames(keyboardRemapControlObjects.back()[0]->getSingleKeyRemapControl(), keyboardRemapControlObjects.back()[1]->getSingleKeyRemapControl(), deleteRemapKeys, (int)keyboardRemapControlObjects.size());
}
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
StackPanel SingleKeyRemapControl::getSingleKeyRemapControl()
{
return singleKeyRemapControlLayout.as<StackPanel>();
}
// Function to create the detect remap key UI window
void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::IInspectable const& sender, XamlRoot xamlRoot, KeyboardManagerState& keyboardManagerState)
{
// ContentDialog for detecting remap key. This is the parent UI element.
ContentDialog detectRemapKeyBox;
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
detectRemapKeyBox.XamlRoot(xamlRoot);
detectRemapKeyBox.Title(box_value(GET_RESOURCE_STRING(IDS_TYPEKEY_TITLE)));
detectRemapKeyBox.IsPrimaryButtonEnabled(false);
detectRemapKeyBox.IsSecondaryButtonEnabled(false);
// Get the linked text block for the "Type Key" button that was clicked
ComboBox linkedRemapDropDown = UIHelpers::GetSiblingElement(sender).as<ComboBox>();
auto unregisterKeys = [&keyboardManagerState]() {
keyboardManagerState.ClearRegisteredKeyDelays();
};
auto onPressEnter = [linkedRemapDropDown,
detectRemapKeyBox,
&keyboardManagerState,
unregisterKeys] {
// Save the detected key in the linked text block
DWORD detectedKey = keyboardManagerState.GetDetectedSingleRemapKey();
if (detectedKey != NULL)
{
std::vector<DWORD> keyCodeList = keyboardManagerState.keyboardMap.GetKeyCodeList();
// Update the drop down list with the new language to ensure that the correct key is displayed
linkedRemapDropDown.ItemsSource(UIHelpers::ToBoxValue(keyboardManagerState.keyboardMap.GetKeyNameList()));
linkedRemapDropDown.SelectedValue(winrt::box_value(std::to_wstring(detectedKey)));
}
// Hide the type key UI
detectRemapKeyBox.Hide();
};
auto onReleaseEnter = [&keyboardManagerState,
unregisterKeys] {
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
};
auto onAccept = [onPressEnter,
onReleaseEnter] {
onPressEnter();
onReleaseEnter();
};
TextBlock primaryButtonText;
primaryButtonText.Text(GET_RESOURCE_STRING(IDS_OK_BUTTON));
Button primaryButton;
primaryButton.HorizontalAlignment(HorizontalAlignment::Stretch);
primaryButton.Margin({ 2, 2, 2, 2 });
primaryButton.Content(primaryButtonText);
primaryButton.Click([onAccept](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
onAccept();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[primaryButton, onPressEnter, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[primaryButton, onPressEnter] {
// Use the base medium low brush to be consistent with the theme
primaryButton.Background(Windows::UI::Xaml::Application::Current().Resources().Lookup(box_value(L"SystemControlBackgroundBaseMediumLowBrush")).as<Windows::UI::Xaml::Media::SolidColorBrush>());
onPressEnter();
});
},
[onReleaseEnter, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onReleaseEnter]() {
onReleaseEnter();
});
});
TextBlock cancelButtonText;
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
auto onCancel = [&keyboardManagerState,
detectRemapKeyBox,
unregisterKeys] {
detectRemapKeyBox.Hide();
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
};
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
onCancel();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[onCancel, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onCancel] {
onCancel();
});
},
nullptr);
// StackPanel parent for the displayed text in the dialog
Windows::UI::Xaml::Controls::StackPanel stackPanel;
detectRemapKeyBox.Content(stackPanel);
// Header textblock
TextBlock text;
text.Text(GET_RESOURCE_STRING(IDS_TYPEKEY_HEADER));
text.Margin({ 0, 0, 0, 10 });
stackPanel.Children().Append(text);
// Target StackPanel to place the selected key
Windows::UI::Xaml::Controls::StackPanel keyStackPanel;
keyStackPanel.Orientation(Orientation::Horizontal);
stackPanel.Children().Append(keyStackPanel);
TextBlock holdEscInfo;
holdEscInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDESC));
holdEscInfo.FontSize(12);
holdEscInfo.Margin({ 0, 20, 0, 0 });
stackPanel.Children().Append(holdEscInfo);
TextBlock holdEnterInfo;
holdEnterInfo.Text(GET_RESOURCE_STRING(IDS_TYPE_HOLDENTER));
holdEnterInfo.FontSize(12);
holdEnterInfo.Margin({ 0, 0, 0, 0 });
stackPanel.Children().Append(holdEnterInfo);
ColumnDefinition primaryButtonColumn;
ColumnDefinition cancelButtonColumn;
Grid buttonPanel;
buttonPanel.Margin({ 0, 20, 0, 0 });
buttonPanel.HorizontalAlignment(HorizontalAlignment::Stretch);
buttonPanel.ColumnDefinitions().Append(primaryButtonColumn);
buttonPanel.ColumnDefinitions().Append(cancelButtonColumn);
buttonPanel.SetColumn(primaryButton, 0);
buttonPanel.SetColumn(cancelButton, 1);
buttonPanel.Children().Append(primaryButton);
buttonPanel.Children().Append(cancelButton);
stackPanel.Children().Append(buttonPanel);
stackPanel.UpdateLayout();
// Configure the keyboardManagerState to store the UI information.
keyboardManagerState.ConfigureDetectSingleKeyRemapUI(keyStackPanel);
// Show the dialog
detectRemapKeyBox.ShowAsync();
}