[PowerAccent] Move low-level keyboard hook to c++ (#20190)

* Move llkeyboardhook to c++

* expect.txt

* Address PR comment - Resolve namespaces

* Address PR comments - CallNextHook and correct char selection

* Also unpress the letter key

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Stefan Markovic
2022-09-09 13:27:56 +02:00
committed by GitHub
parent 7f8c5c9f0c
commit 7b0f97597d
25 changed files with 760 additions and 548 deletions

View File

@@ -2,173 +2,159 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
using System.Windows;
using PowerAccent.Core.Services;
using PowerAccent.Core.Tools;
using PowerToys.PowerAccentKeyboardService;
namespace PowerAccent.Core;
public class PowerAccent : IDisposable
{
private readonly SettingsService _settingService = new SettingsService();
private readonly KeyboardListener _keyboardListener = new KeyboardListener();
private readonly SettingsService _settingService;
private LetterKey? letterPressed;
private bool _visible;
private char[] _characters = Array.Empty<char>();
private int _selectedIndex = -1;
private Stopwatch _stopWatch;
private bool _triggeredWithSpace;
public event Action<bool, char[]> OnChangeDisplay;
public event Action<int, char> OnSelectCharacter;
private KeyboardListener _keyboardListener;
public PowerAccent()
{
_keyboardListener.KeyDown += PowerAccent_KeyDown;
_keyboardListener.KeyUp += PowerAccent_KeyUp;
_keyboardListener = new KeyboardListener();
_keyboardListener.InitHook();
_settingService = new SettingsService(_keyboardListener);
SetEvents();
}
private bool PowerAccent_KeyDown(object sender, KeyboardListener.RawKeyEventArgs args)
private void SetEvents()
{
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
_keyboardListener.SetShowToolbarEvent(new PowerToys.PowerAccentKeyboardService.ShowToolbar((LetterKey letterKey) =>
{
_stopWatch = Stopwatch.StartNew();
letterPressed = (LetterKey)args.Key;
}
TriggerKey? triggerPressed = null;
if (letterPressed.HasValue)
{
if (Enum.IsDefined(typeof(TriggerKey), (int)args.Key))
Application.Current.Dispatcher.Invoke(() =>
{
triggerPressed = (TriggerKey)args.Key;
ShowToolbar(letterKey);
});
}));
if ((triggerPressed == TriggerKey.Space && _settingService.ActivationKey == PowerAccentActivationKey.LeftRightArrow) ||
((triggerPressed == TriggerKey.Left || triggerPressed == TriggerKey.Right) && _settingService.ActivationKey == PowerAccentActivationKey.Space))
{
triggerPressed = null;
}
}
}
if (!_visible && letterPressed.HasValue && triggerPressed.HasValue)
_keyboardListener.SetHideToolbarEvent(new PowerToys.PowerAccentKeyboardService.HideToolbar((InputType inputType) =>
{
// Keep track if it was triggered with space so that it can be typed on false starts.
_triggeredWithSpace = triggerPressed.Value == TriggerKey.Space;
_visible = true;
_characters = WindowsFunctions.IsCapitalState() ? ToUpper(_settingService.GetLetterKey(letterPressed.Value)) : _settingService.GetLetterKey(letterPressed.Value);
Task.Delay(_settingService.InputTime).ContinueWith(
t =>
Application.Current.Dispatcher.Invoke(() =>
{
SendInputAndHideToolbar(inputType);
});
}));
_keyboardListener.SetNextCharEvent(new PowerToys.PowerAccentKeyboardService.NextChar((TriggerKey triggerKey) =>
{
Application.Current.Dispatcher.Invoke(() =>
{
ProcessNextChar(triggerKey);
});
}));
}
private void ShowToolbar(LetterKey letterKey)
{
_visible = true;
_characters = WindowsFunctions.IsCapitalState() ? ToUpper(SettingsService.GetDefaultLetterKey(letterKey)) : SettingsService.GetDefaultLetterKey(letterKey);
Task.Delay(_settingService.InputTime).ContinueWith(
t =>
{
if (_visible)
{
if (_visible)
OnChangeDisplay?.Invoke(true, _characters);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SendInputAndHideToolbar(InputType inputType)
{
switch (inputType)
{
case InputType.Space:
{
WindowsFunctions.Insert(' ');
break;
}
case InputType.Char:
{
if (_selectedIndex != -1)
{
OnChangeDisplay?.Invoke(true, _characters);
WindowsFunctions.Insert(_characters[_selectedIndex], true);
}
}, TaskScheduler.FromCurrentSynchronizationContext());
break;
}
}
if (_visible && triggerPressed.HasValue)
OnChangeDisplay?.Invoke(false, null);
_selectedIndex = -1;
_visible = false;
}
private void ProcessNextChar(TriggerKey triggerKey)
{
if (_visible && _selectedIndex == -1)
{
if (_selectedIndex == -1)
if (triggerKey == TriggerKey.Left)
{
if (triggerPressed.Value == TriggerKey.Left)
{
_selectedIndex = (_characters.Length / 2) - 1;
}
if (triggerPressed.Value == TriggerKey.Right)
{
_selectedIndex = _characters.Length / 2;
}
if (triggerPressed.Value == TriggerKey.Space)
{
_selectedIndex = 0;
}
if (_selectedIndex < 0)
{
_selectedIndex = 0;
}
if (_selectedIndex > _characters.Length - 1)
{
_selectedIndex = _characters.Length - 1;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false;
_selectedIndex = (_characters.Length / 2) - 1;
}
if (triggerPressed.Value == TriggerKey.Space)
if (triggerKey == TriggerKey.Right)
{
if (_selectedIndex < _characters.Length - 1)
{
++_selectedIndex;
}
else
{
_selectedIndex = 0;
}
_selectedIndex = _characters.Length / 2;
}
if (triggerPressed.Value == TriggerKey.Left && _selectedIndex > 0)
if (triggerKey == TriggerKey.Space)
{
--_selectedIndex;
_selectedIndex = 0;
}
if (triggerPressed.Value == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
if (_selectedIndex < 0)
{
++_selectedIndex;
_selectedIndex = 0;
}
if (_selectedIndex > _characters.Length - 1)
{
_selectedIndex = _characters.Length - 1;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
return false;
return;
}
return true;
}
private bool PowerAccent_KeyUp(object sender, KeyboardListener.RawKeyEventArgs args)
{
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
if (triggerKey == TriggerKey.Space)
{
letterPressed = null;
_stopWatch.Stop();
if (_visible)
if (_selectedIndex < _characters.Length - 1)
{
if (_stopWatch.ElapsedMilliseconds < _settingService.InputTime)
{
/* Debug.WriteLine("Insert before inputTime - " + _stopWatch.ElapsedMilliseconds); */
// False start, we should output the space if it was the trigger.
if (_triggeredWithSpace)
{
WindowsFunctions.Insert(' ');
}
OnChangeDisplay?.Invoke(false, null);
_selectedIndex = -1;
_visible = false;
return false;
}
/* Debug.WriteLine("Insert after inputTime - " + _stopWatch.ElapsedMilliseconds); */
OnChangeDisplay?.Invoke(false, null);
if (_selectedIndex != -1)
{
WindowsFunctions.Insert(_characters[_selectedIndex], true);
}
_selectedIndex = -1;
_visible = false;
++_selectedIndex;
}
else
{
_selectedIndex = 0;
}
}
return true;
if (triggerKey == TriggerKey.Left && _selectedIndex > 0)
{
--_selectedIndex;
}
if (triggerKey == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
{
++_selectedIndex;
}
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
}
public Point GetDisplayCoordinates(Size window)
@@ -182,14 +168,9 @@ public class PowerAccent : IDisposable
return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
}
public char[] GetLettersFromKey(LetterKey letter)
{
return _settingService.GetLetterKey(letter);
}
public void Dispose()
{
_keyboardListener.Dispose();
_keyboardListener.UnInitHook();
GC.SuppressFinalize(this);
}