[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

@@ -4,7 +4,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
@@ -24,15 +26,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private readonly ISettingsUtils _settingsUtils;
private const string PowerToyName = KeyboardManagerSettings.ModuleName;
private const string RemapKeyboardActionName = "RemapKeyboard";
private const string RemapKeyboardActionValue = "Open Remap Keyboard Window";
private const string EditShortcutActionName = "EditShortcut";
private const string EditShortcutActionValue = "Open Edit Shortcut Window";
private const string JsonFileType = ".json";
private static string ConfigFileMutexName => interop.Constants.KeyboardManagerConfigFileMutexName();
private const string KeyboardManagerEditorPath = "modules\\KeyboardManager\\KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe";
private Process editor;
private const int ConfigFileMutexWaitTimeoutMilliseconds = 1000;
private enum KeyboardManagerEditorType
{
KeyEditor = 0,
ShortcutEditor,
}
public KeyboardManagerSettings Settings { get; set; }
@@ -103,6 +106,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
GeneralSettingsConfig.Enabled.KeyboardManager = value;
OnPropertyChanged(nameof(Enabled));
if (!Enabled && editor != null)
{
editor.CloseMainWindow();
}
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(outgoing.ToString());
@@ -165,31 +174,73 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
public ICommand EditShortcutCommand => _editShortcutCommand ?? (_editShortcutCommand = new RelayCommand(OnEditShortcut));
// Note: FxCop suggests calling ConfigureAwait() for the following methods,
// and calling ConfigureAwait(true) has the same behavior as not explicitly
// calling it (continuations are scheduled on the task-creating thread)
private async void OnRemapKeyboard()
private void OnRemapKeyboard()
{
await Task.Run(() => OnRemapKeyboardBackground()).ConfigureAwait(true);
OpenEditor((int)KeyboardManagerEditorType.KeyEditor);
}
private async void OnEditShortcut()
private void OnEditShortcut()
{
await Task.Run(() => OnEditShortcutBackground()).ConfigureAwait(true);
OpenEditor((int)KeyboardManagerEditorType.ShortcutEditor);
}
private async Task OnRemapKeyboardBackground()
private static void BringProcessToFront(Process process)
{
Helper.AllowRunnerToForeground();
SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, RemapKeyboardActionName, RemapKeyboardActionValue));
await Task.CompletedTask.ConfigureAwait(true);
if (process == null)
{
return;
}
IntPtr handle = process.MainWindowHandle;
if (IsIconic(handle))
{
ShowWindow(handle, SWRESTORE);
}
SetForegroundWindow(handle);
}
private async Task OnEditShortcutBackground()
private const int SWRESTORE = 9;
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr handle);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
private static extern bool IsIconic(IntPtr handle);
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exceptions here (especially mutex errors) should not halt app execution, but they will be logged.")]
private void OpenEditor(int type)
{
Helper.AllowRunnerToForeground();
SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, EditShortcutActionName, EditShortcutActionValue));
await Task.CompletedTask.ConfigureAwait(true);
try
{
if (editor != null && editor.HasExited)
{
Logger.LogInfo($"Previous instance of {PowerToyName} editor exited");
editor = null;
}
if (editor != null)
{
Logger.LogInfo($"The {PowerToyName} editor instance {editor.Id} exists. Bringing the process to the front");
BringProcessToFront(editor);
return;
}
string path = Path.Combine(Environment.CurrentDirectory, KeyboardManagerEditorPath);
Logger.LogInfo($"Starting {PowerToyName} editor from {path}");
// InvariantCulture: type represents the KeyboardManagerEditorType enum value
editor = Process.Start(path, $"{type.ToString(CultureInfo.InvariantCulture)} {Process.GetCurrentProcess().Id}");
}
catch (Exception e)
{
Logger.LogError($"Exception encountered when opening an {PowerToyName} editor", e);
}
}
public void NotifyFileChanged()
@@ -201,42 +252,50 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
[SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Exceptions here (especially mutex errors) should not halt app execution, but they will be logged.")]
public bool LoadProfile()
{
// The KBM process out of runner creates the default.json file if it does not exist.
var success = true;
var readSuccessfully = false;
string fileName = Settings.Properties.ActiveConfiguration.Value + JsonFileType;
try
{
using (var profileFileMutex = Mutex.OpenExisting(ConfigFileMutexName))
// retry loop for reading
CancellationTokenSource ts = new CancellationTokenSource();
Task t = Task.Run(() =>
{
if (profileFileMutex.WaitOne(ConfigFileMutexWaitTimeoutMilliseconds))
while (!readSuccessfully && !ts.IsCancellationRequested)
{
// update the UI element here.
try
if (_settingsUtils.SettingsExists(PowerToyName, fileName))
{
string fileName = Settings.Properties.ActiveConfiguration.Value + JsonFileType;
if (_settingsUtils.SettingsExists(PowerToyName, fileName))
try
{
_profile = _settingsUtils.GetSettingsOrDefault<KeyboardManagerProfile>(PowerToyName, fileName);
readSuccessfully = true;
}
else
catch (Exception e)
{
// The KBM process out of runner creates the default.json file if it does not exist.
success = false;
Logger.LogError($"Exception encountered when reading {PowerToyName} settings", e);
}
}
FilterRemapKeysList(_profile?.RemapKeys?.InProcessRemapKeys);
}
finally
if (!readSuccessfully)
{
// Make sure to release the mutex.
profileFileMutex.ReleaseMutex();
Task.Delay(500).Wait();
}
}
else
{
success = false;
}
});
t.Wait(1000, ts.Token);
ts.Cancel();
ts.Dispose();
if (!readSuccessfully)
{
success = false;
}
FilterRemapKeysList(_profile?.RemapKeys?.InProcessRemapKeys);
}
catch (Exception e)
{