mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-06 03:07:04 +02:00
[New PowerToy] PowerAccent (#19212)
* add poweraccent (draft) for PR * removing french text for Spell checking job * add 'poweraccent' to spell checker * add 'damienleroy' to spell checker file * adding RuntimeIdentifiers for PowerAccent project * duplicate image for settings * update commandline arguments for launch settings * Removing WndProc for testing with inter-process connection * add PowerAccent sources for PowerToys * fix spellcheck * fixing stylecop conventions * Remove StyleCop.Analyzers because of duplicate * fixing command line reference * Fixing CS8012 for PowerAccent. * ARM64 processor * - Modify PowerAccent fluenticon for dark mode - Try fix arm64 release * Remove taskbar * init Oobe view * - added POwerAccent to App.xaml.cs - change style to markdown in Oobe display * - fixing poweraccent crash - change Oobe LearnMore link * Installer and signing * Cleanup Add settings * Issue template * Add some more characters * Disabled by default * Proper ToUnicodeEx calling and remove hacks * Fix spellcheck * Remove CommandLine dependency and debug prints. Add logs * fix signing * Fix binary metadata with version * Fix the added space bug * Only type space if it was the trigger method * Take account of InputTime for displaying UI * Fix code styling * Remove the Trace WriteLine hack and add a delay instead * Reinstate logs * Better explanations * Add telemetry for showing the menu * Update src/settings-ui/Settings.UI/Strings/en-us/Resources.resw * Update src/settings-ui/Settings.UI/Strings/en-us/Resources.resw * Update src/modules/poweraccent/PowerAccent.Core/Tools/KeyboardListener.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Add accented characters for S * Default to both activation methods * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs * Update src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs Co-authored-by: Damien LEROY <dleroy@veepee.com>
This commit is contained in:
27
src/modules/poweraccent/PowerAccent.Core/Models/KeysEnums.cs
Normal file
27
src/modules/poweraccent/PowerAccent.Core/Models/KeysEnums.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace PowerAccent.Core;
|
||||
|
||||
public enum LetterKey
|
||||
{
|
||||
A = User32.VK.VK_A,
|
||||
C = User32.VK.VK_C,
|
||||
E = User32.VK.VK_E,
|
||||
I = User32.VK.VK_I,
|
||||
N = User32.VK.VK_N,
|
||||
O = User32.VK.VK_O,
|
||||
S = User32.VK.VK_S,
|
||||
U = User32.VK.VK_U,
|
||||
Y = User32.VK.VK_Y,
|
||||
}
|
||||
|
||||
public enum TriggerKey
|
||||
{
|
||||
Left = User32.VK.VK_LEFT,
|
||||
Right = User32.VK.VK_RIGHT,
|
||||
Space = User32.VK.VK_SPACE,
|
||||
}
|
||||
58
src/modules/poweraccent/PowerAccent.Core/Models/Point.cs
Normal file
58
src/modules/poweraccent/PowerAccent.Core/Models/Point.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerAccent.Core;
|
||||
|
||||
public struct Point
|
||||
{
|
||||
public Point()
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
}
|
||||
|
||||
public Point(double x, double y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public Point(int x, int y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public Point(System.Drawing.Point point)
|
||||
{
|
||||
X = point.X;
|
||||
Y = point.Y;
|
||||
}
|
||||
|
||||
public double X { get; init; }
|
||||
|
||||
public double Y { get; init; }
|
||||
|
||||
public static implicit operator Point(System.Drawing.Point point) => new Point(point.X, point.Y);
|
||||
|
||||
public static Point operator /(Point point, double divider)
|
||||
{
|
||||
if (divider == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Point(point.X / divider, point.Y / divider);
|
||||
}
|
||||
|
||||
public static Point operator /(Point point, Point divider)
|
||||
{
|
||||
if (divider.X == 0 || divider.Y == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Point(point.X / divider.X, point.Y / divider.Y);
|
||||
}
|
||||
}
|
||||
68
src/modules/poweraccent/PowerAccent.Core/Models/Rect.cs
Normal file
68
src/modules/poweraccent/PowerAccent.Core/Models/Rect.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerAccent.Core;
|
||||
|
||||
public struct Rect
|
||||
{
|
||||
public Rect()
|
||||
{
|
||||
X = 0;
|
||||
Y = 0;
|
||||
Width = 0;
|
||||
Height = 0;
|
||||
}
|
||||
|
||||
public Rect(int x, int y, int width, int height)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public Rect(double x, double y, double width, double height)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public Rect(Point coord, Size size)
|
||||
{
|
||||
X = coord.X;
|
||||
Y = coord.Y;
|
||||
Width = size.Width;
|
||||
Height = size.Height;
|
||||
}
|
||||
|
||||
public double X { get; init; }
|
||||
|
||||
public double Y { get; init; }
|
||||
|
||||
public double Width { get; init; }
|
||||
|
||||
public double Height { get; init; }
|
||||
|
||||
public static Rect operator /(Rect rect, double divider)
|
||||
{
|
||||
if (divider == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Rect(rect.X / divider, rect.Y / divider, rect.Width / divider, rect.Height / divider);
|
||||
}
|
||||
|
||||
public static Rect operator /(Rect rect, Rect divider)
|
||||
{
|
||||
if (divider.X == 0 || divider.Y == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Rect(rect.X / divider.X, rect.Y / divider.Y, rect.Width / divider.Width, rect.Height / divider.Height);
|
||||
}
|
||||
}
|
||||
52
src/modules/poweraccent/PowerAccent.Core/Models/Size.cs
Normal file
52
src/modules/poweraccent/PowerAccent.Core/Models/Size.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerAccent.Core;
|
||||
|
||||
public struct Size
|
||||
{
|
||||
public Size()
|
||||
{
|
||||
Width = 0;
|
||||
Height = 0;
|
||||
}
|
||||
|
||||
public Size(double width, double height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public Size(int width, int height)
|
||||
{
|
||||
Width = width;
|
||||
Height = height;
|
||||
}
|
||||
|
||||
public double Width { get; init; }
|
||||
|
||||
public double Height { get; init; }
|
||||
|
||||
public static implicit operator Size(System.Drawing.Size size) => new Size(size.Width, size.Height);
|
||||
|
||||
public static Size operator /(Size size, double divider)
|
||||
{
|
||||
if (divider == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Size(size.Width / divider, size.Height / divider);
|
||||
}
|
||||
|
||||
public static Size operator /(Size size, Size divider)
|
||||
{
|
||||
if (divider.Width == 0 || divider.Height == 0 || divider.Width == 0 || divider.Height == 0)
|
||||
{
|
||||
throw new DivideByZeroException();
|
||||
}
|
||||
|
||||
return new Size(size.Width / divider.Width, size.Height / divider.Height);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\..\Version.props" />
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>disable</Nullable>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="3.3.15" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
206
src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs
Normal file
206
src/modules/poweraccent/PowerAccent.Core/PowerAccent.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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 PowerAccent.Core.Services;
|
||||
using PowerAccent.Core.Tools;
|
||||
|
||||
namespace PowerAccent.Core;
|
||||
|
||||
public class PowerAccent : IDisposable
|
||||
{
|
||||
private readonly SettingsService _settingService = new SettingsService();
|
||||
private readonly KeyboardListener _keyboardListener = new KeyboardListener();
|
||||
|
||||
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;
|
||||
|
||||
public PowerAccent()
|
||||
{
|
||||
_keyboardListener.KeyDown += PowerAccent_KeyDown;
|
||||
_keyboardListener.KeyUp += PowerAccent_KeyUp;
|
||||
}
|
||||
|
||||
private bool PowerAccent_KeyDown(object sender, KeyboardListener.RawKeyEventArgs args)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
|
||||
{
|
||||
_stopWatch = Stopwatch.StartNew();
|
||||
letterPressed = (LetterKey)args.Key;
|
||||
}
|
||||
|
||||
TriggerKey? triggerPressed = null;
|
||||
if (letterPressed.HasValue)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(TriggerKey), (int)args.Key))
|
||||
{
|
||||
triggerPressed = (TriggerKey)args.Key;
|
||||
|
||||
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)
|
||||
{
|
||||
// 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 =>
|
||||
{
|
||||
if (_visible)
|
||||
{
|
||||
OnChangeDisplay?.Invoke(true, _characters);
|
||||
}
|
||||
}, TaskScheduler.FromCurrentSynchronizationContext());
|
||||
}
|
||||
|
||||
if (_visible && triggerPressed.HasValue)
|
||||
{
|
||||
if (_selectedIndex == -1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (triggerPressed.Value == TriggerKey.Space)
|
||||
{
|
||||
if (_selectedIndex < _characters.Length - 1)
|
||||
{
|
||||
++_selectedIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
_selectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (triggerPressed.Value == TriggerKey.Left && _selectedIndex > 0)
|
||||
{
|
||||
--_selectedIndex;
|
||||
}
|
||||
|
||||
if (triggerPressed.Value == TriggerKey.Right && _selectedIndex < _characters.Length - 1)
|
||||
{
|
||||
++_selectedIndex;
|
||||
}
|
||||
|
||||
OnSelectCharacter?.Invoke(_selectedIndex, _characters[_selectedIndex]);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool PowerAccent_KeyUp(object sender, KeyboardListener.RawKeyEventArgs args)
|
||||
{
|
||||
if (Enum.IsDefined(typeof(LetterKey), (int)args.Key))
|
||||
{
|
||||
letterPressed = null;
|
||||
_stopWatch.Stop();
|
||||
if (_visible)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Point GetDisplayCoordinates(Size window)
|
||||
{
|
||||
var activeDisplay = WindowsFunctions.GetActiveDisplay();
|
||||
Rect screen = new Rect(activeDisplay.Location, activeDisplay.Size) / activeDisplay.Dpi;
|
||||
Position position = _settingService.Position;
|
||||
|
||||
/* Debug.WriteLine("Dpi: " + activeDisplay.Dpi); */
|
||||
|
||||
return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
|
||||
}
|
||||
|
||||
public char[] GetLettersFromKey(LetterKey letter)
|
||||
{
|
||||
return _settingService.GetLetterKey(letter);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_keyboardListener.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static char[] ToUpper(char[] array)
|
||||
{
|
||||
char[] result = new char[array.Length];
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
result[i] = char.ToUpper(array[i], System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace PowerAccent.Core.Services;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Enumerations;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.Json;
|
||||
|
||||
public class SettingsService
|
||||
{
|
||||
private const string PowerAccentModuleName = "PowerAccent";
|
||||
private readonly ISettingsUtils _settingsUtils;
|
||||
private readonly IFileSystemWatcher _watcher;
|
||||
private readonly object _loadingSettingsLock = new object();
|
||||
|
||||
public SettingsService()
|
||||
{
|
||||
_settingsUtils = new SettingsUtils();
|
||||
ReadSettings();
|
||||
_watcher = Helper.GetFileWatcher(PowerAccentModuleName, "settings.json", () => { ReadSettings(); });
|
||||
}
|
||||
|
||||
private void ReadSettings()
|
||||
{
|
||||
// TODO this IO call should by Async, update GetFileWatcher helper to support async
|
||||
lock (_loadingSettingsLock)
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_settingsUtils.SettingsExists(PowerAccentModuleName))
|
||||
{
|
||||
Logger.LogInfo("PowerAccent settings.json was missing, creating a new one");
|
||||
var defaultSettings = new PowerAccentSettings();
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
_settingsUtils.SaveSettings(JsonSerializer.Serialize(this, options), PowerAccentModuleName);
|
||||
}
|
||||
|
||||
var settings = _settingsUtils.GetSettingsOrDefault<PowerAccentSettings>(PowerAccentModuleName);
|
||||
if (settings != null)
|
||||
{
|
||||
ActivationKey = settings.Properties.ActivationKey;
|
||||
switch (settings.Properties.ToolbarPosition.Value)
|
||||
{
|
||||
case "Top center":
|
||||
Position = Position.Top;
|
||||
break;
|
||||
case "Bottom center":
|
||||
Position = Position.Bottom;
|
||||
break;
|
||||
case "Left":
|
||||
Position = Position.Left;
|
||||
break;
|
||||
case "Right":
|
||||
Position = Position.Right;
|
||||
break;
|
||||
case "Top right corner":
|
||||
Position = Position.TopRight;
|
||||
break;
|
||||
case "Top left corner":
|
||||
Position = Position.TopLeft;
|
||||
break;
|
||||
case "Bottom right corner":
|
||||
Position = Position.BottomRight;
|
||||
break;
|
||||
case "Bottom left corner":
|
||||
Position = Position.BottomLeft;
|
||||
break;
|
||||
case "Center":
|
||||
Position = Position.Center;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to read changed settings", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PowerAccentActivationKey _activationKey = PowerAccentActivationKey.Both;
|
||||
|
||||
public PowerAccentActivationKey ActivationKey
|
||||
{
|
||||
get
|
||||
{
|
||||
return _activationKey;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_activationKey = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Position _position = Position.Top;
|
||||
|
||||
public Position Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return _position;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_position = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int _inputTime = 200;
|
||||
|
||||
public int InputTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return _inputTime;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_inputTime = value;
|
||||
}
|
||||
}
|
||||
|
||||
public char[] GetLetterKey(LetterKey letter)
|
||||
{
|
||||
return GetDefaultLetterKey(letter);
|
||||
}
|
||||
|
||||
public static char[] GetDefaultLetterKey(LetterKey letter)
|
||||
{
|
||||
switch (letter)
|
||||
{
|
||||
case LetterKey.A:
|
||||
return new char[] { 'à', 'â', 'á', 'ä', 'ã', 'å', 'æ' };
|
||||
case LetterKey.C:
|
||||
return new char[] { 'ć', 'ĉ', 'č', 'ċ', 'ç', 'ḉ' };
|
||||
case LetterKey.E:
|
||||
return new char[] { 'é', 'è', 'ê', 'ë', 'ē', 'ė', '€' };
|
||||
case LetterKey.I:
|
||||
return new char[] { 'î', 'ï', 'í', 'ì', 'ī' };
|
||||
case LetterKey.N:
|
||||
return new char[] { 'ñ', 'ń' };
|
||||
case LetterKey.O:
|
||||
return new char[] { 'ô', 'ö', 'ó', 'ò', 'õ', 'ø', 'œ' };
|
||||
case LetterKey.S:
|
||||
return new char[] { 'š', 'ß', 'ś' };
|
||||
case LetterKey.U:
|
||||
return new char[] { 'û', 'ù', 'ü', 'ú', 'ū' };
|
||||
case LetterKey.Y:
|
||||
return new char[] { 'ÿ', 'ý' };
|
||||
}
|
||||
|
||||
throw new ArgumentException("Letter {0} is missing", letter.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public enum Position
|
||||
{
|
||||
Top,
|
||||
Bottom,
|
||||
Left,
|
||||
Right,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
Center,
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.Tracing;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry.Events;
|
||||
|
||||
namespace PowerAccent.Core.Telemetry
|
||||
{
|
||||
[EventData]
|
||||
public class PowerAccentShowAccentMenuEvent : EventBase, IEvent
|
||||
{
|
||||
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using PowerAccent.Core.Services;
|
||||
|
||||
namespace PowerAccent.Core.Tools
|
||||
{
|
||||
internal static class Calculation
|
||||
{
|
||||
public static Point GetRawCoordinatesFromCaret(Point caret, Rect screen, Size window)
|
||||
{
|
||||
var left = caret.X - (window.Width / 2);
|
||||
var top = caret.Y - window.Height - 20;
|
||||
|
||||
return new Point(
|
||||
left < screen.X ? screen.X : (left + window.Width > (screen.X + screen.Width) ? (screen.X + screen.Width) - window.Width : left),
|
||||
top < screen.Y ? caret.Y + 20 : top);
|
||||
}
|
||||
|
||||
public static Point GetRawCoordinatesFromPosition(Position position, Rect screen, Size window)
|
||||
{
|
||||
int offset = 10;
|
||||
|
||||
double pointX = position switch
|
||||
{
|
||||
Position.Top or Position.Bottom or Position.Center
|
||||
=> screen.X + (screen.Width / 2) - (window.Width / 2),
|
||||
Position.TopLeft or Position.Left or Position.BottomLeft
|
||||
=> screen.X + offset,
|
||||
Position.TopRight or Position.Right or Position.BottomRight
|
||||
=> screen.X + screen.Width - (window.Width + offset),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
double pointY = position switch
|
||||
{
|
||||
Position.TopLeft or Position.Top or Position.TopRight
|
||||
=> screen.Y + offset,
|
||||
Position.Left or Position.Center or Position.Right
|
||||
=> screen.Y + (screen.Height / 2) - (window.Height / 2),
|
||||
Position.BottomLeft or Position.Bottom or Position.BottomRight
|
||||
=> screen.Y + screen.Height - (window.Height + offset),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
return new Point(pointX, pointY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
#pragma warning disable SA1310 // FieldNamesMustNotContainUnderscore
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace PowerAccent.Core.Tools;
|
||||
|
||||
internal class KeyboardListener : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="KeyboardListener"/> class.
|
||||
/// Creates global keyboard listener.
|
||||
/// </summary>
|
||||
public KeyboardListener()
|
||||
{
|
||||
// We have to store the LowLevelKeyboardProc, so that it is not garbage collected by runtime
|
||||
_hookedLowLevelKeyboardProc = LowLevelKeyboardProc;
|
||||
|
||||
// Set the hook
|
||||
_hookId = InterceptKeys.SetHook(_hookedLowLevelKeyboardProc);
|
||||
|
||||
// Assign the asynchronous callback event
|
||||
hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when any of the keys is pressed down.
|
||||
/// </summary>
|
||||
public event RawKeyEventHandler KeyDown;
|
||||
|
||||
/// <summary>
|
||||
/// Fired when any of the keys is released.
|
||||
/// </summary>
|
||||
public event RawKeyEventHandler KeyUp;
|
||||
|
||||
/// <summary>
|
||||
/// Hook ID
|
||||
/// </summary>
|
||||
private readonly IntPtr _hookId = IntPtr.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the hooked callback in runtime.
|
||||
/// </summary>
|
||||
private readonly InterceptKeys.LowLevelKeyboardProc _hookedLowLevelKeyboardProc;
|
||||
|
||||
/// <summary>
|
||||
/// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
|
||||
/// </summary>
|
||||
private KeyboardCallbackAsync hookedKeyboardCallbackAsync;
|
||||
|
||||
/// <summary>
|
||||
/// Raw keyevent handler.
|
||||
/// </summary>
|
||||
/// <param name="sender">sender</param>
|
||||
/// <param name="args">raw keyevent arguments</param>
|
||||
public delegate bool RawKeyEventHandler(object sender, RawKeyEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronous callback hook.
|
||||
/// </summary>
|
||||
/// <param name="keyEvent">Keyboard event</param>
|
||||
/// <param name="vkCode">VKCode</param>
|
||||
/// <param name="character">Character</param>
|
||||
private delegate bool KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);
|
||||
|
||||
/// <summary>
|
||||
/// Actual callback hook.
|
||||
/// <remarks>Calls asynchronously the asyncCallback.</remarks>
|
||||
/// </summary>
|
||||
/// <param name="nCode">VKCode</param>
|
||||
/// <param name="wParam">wParam</param>
|
||||
/// <param name="lParam">lParam</param>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0)
|
||||
{
|
||||
if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
|
||||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP)
|
||||
{
|
||||
// Captures the character(s) pressed only on WM_KEYDOWN
|
||||
var chars = InterceptKeys.VKCodeToString(
|
||||
(uint)Marshal.ReadInt32(lParam),
|
||||
wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN);
|
||||
|
||||
if (!hookedKeyboardCallbackAsync.Invoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars))
|
||||
{
|
||||
return (IntPtr)1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return InterceptKeys.CallNextHookEx(_hookId, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
|
||||
/// </summary>
|
||||
/// <param name="keyEvent">Keyboard event</param>
|
||||
/// <param name="vkCode">VKCode</param>
|
||||
/// <param name="character">Character as string.</param>
|
||||
private bool KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
|
||||
{
|
||||
switch (keyEvent)
|
||||
{
|
||||
// KeyDown events
|
||||
case InterceptKeys.KeyEvent.WM_KEYDOWN:
|
||||
if (KeyDown != null)
|
||||
{
|
||||
return KeyDown.Invoke(this, new RawKeyEventArgs(vkCode, character));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// KeyUp events
|
||||
case InterceptKeys.KeyEvent.WM_KEYUP:
|
||||
if (KeyUp != null)
|
||||
{
|
||||
return KeyUp.Invoke(this, new RawKeyEventArgs(vkCode, character));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
InterceptKeys.UnhookWindowsHookEx(_hookId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raw KeyEvent arguments.
|
||||
/// </summary>
|
||||
public class RawKeyEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// WPF Key of the key.
|
||||
/// </summary>
|
||||
#pragma warning disable SA1401 // Fields should be private
|
||||
public uint Key;
|
||||
#pragma warning restore SA1401 // Fields should be private
|
||||
|
||||
/// <summary>
|
||||
/// Convert to string.
|
||||
/// </summary>
|
||||
/// <returns>Returns string representation of this key, if not possible empty string is returned.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return character;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unicode character of key pressed.
|
||||
/// </summary>
|
||||
private string character;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RawKeyEventArgs"/> class.
|
||||
/// Create raw keyevent arguments.
|
||||
/// </summary>
|
||||
/// <param name="vKCode">VKCode</param>
|
||||
/// <param name="character">Character</param>
|
||||
public RawKeyEventArgs(int vKCode, string character)
|
||||
{
|
||||
this.character = character;
|
||||
Key = (uint)vKCode; // User32.MapVirtualKey((uint)VKCode, User32.MAPVK.MAPVK_VK_TO_VSC_EX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Winapi Key interception helper class.
|
||||
/// </summary>
|
||||
internal static class InterceptKeys
|
||||
{
|
||||
public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
|
||||
|
||||
private const int WH_KEYBOARD_LL = 13;
|
||||
|
||||
/// <summary>
|
||||
/// Key event
|
||||
/// </summary>
|
||||
public enum KeyEvent : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Key down
|
||||
/// </summary>
|
||||
WM_KEYDOWN = 256,
|
||||
|
||||
/// <summary>
|
||||
/// Key up
|
||||
/// </summary>
|
||||
WM_KEYUP = 257,
|
||||
|
||||
/// <summary>
|
||||
/// System key up
|
||||
/// </summary>
|
||||
WM_SYSKEYUP = 261,
|
||||
|
||||
/// <summary>
|
||||
/// System key down
|
||||
/// </summary>
|
||||
WM_SYSKEYDOWN = 260,
|
||||
}
|
||||
|
||||
public static IntPtr SetHook(LowLevelKeyboardProc proc)
|
||||
{
|
||||
using (Process curProcess = Process.GetCurrentProcess())
|
||||
using (ProcessModule curModule = curProcess.MainModule)
|
||||
{
|
||||
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, (IntPtr)0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
public static extern IntPtr GetModuleHandle(string lpModuleName);
|
||||
|
||||
// Note: Sometimes single VKCode represents multiple chars, thus string.
|
||||
// E.g. typing "^1" (notice that when pressing 1 the both characters appear,
|
||||
// because of this behavior, "^" is called dead key)
|
||||
[DllImport("user32.dll")]
|
||||
#pragma warning disable CA1838 // Éviter les paramètres 'StringBuilder' pour les P/Invoke
|
||||
private static extern int ToUnicodeEx(uint wVirtKey, uint wScanCode, byte[] lpKeyState, [Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags, IntPtr dwhkl);
|
||||
#pragma warning restore CA1838 // Éviter les paramètres 'StringBuilder' pour les P/Invoke
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetKeyboardState(byte[] lpKeyState);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern uint MapVirtualKeyEx(uint uCode, uint uMapType, IntPtr dwhkl);
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
|
||||
private static extern IntPtr GetKeyboardLayout(uint dwLayout);
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("User32.dll")]
|
||||
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern uint GetCurrentThreadId();
|
||||
|
||||
private static uint lastVKCode;
|
||||
private static uint lastScanCode;
|
||||
private static byte[] lastKeyState = new byte[255];
|
||||
|
||||
/// <summary>
|
||||
/// Convert VKCode to Unicode.
|
||||
/// <remarks>isKeyDown is required for because of keyboard state inconsistencies!</remarks>
|
||||
/// </summary>
|
||||
/// <param name="vKCode">VKCode</param>
|
||||
/// <param name="isKeyDown">Is the key down event?</param>
|
||||
/// <returns>String representing single unicode character.</returns>
|
||||
public static string VKCodeToString(uint vKCode, bool isKeyDown)
|
||||
{
|
||||
// ToUnicodeEx needs StringBuilder, it populates that during execution.
|
||||
System.Text.StringBuilder sbString = new System.Text.StringBuilder(5);
|
||||
|
||||
byte[] bKeyState = new byte[255];
|
||||
bool bKeyStateStatus;
|
||||
|
||||
// Gets the current windows window handle, threadID, processID
|
||||
IntPtr currentHWnd = GetForegroundWindow();
|
||||
uint currentProcessID;
|
||||
uint currentWindowThreadID = GetWindowThreadProcessId(currentHWnd, out currentProcessID);
|
||||
|
||||
// This programs Thread ID
|
||||
uint thisProgramThreadId = GetCurrentThreadId();
|
||||
|
||||
// Attach to active thread so we can get that keyboard state
|
||||
if (AttachThreadInput(thisProgramThreadId, currentWindowThreadID, true))
|
||||
{
|
||||
// Current state of the modifiers in keyboard
|
||||
bKeyStateStatus = GetKeyboardState(bKeyState);
|
||||
|
||||
// Detach
|
||||
AttachThreadInput(thisProgramThreadId, currentWindowThreadID, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Could not attach, perhaps it is this process?
|
||||
bKeyStateStatus = GetKeyboardState(bKeyState);
|
||||
}
|
||||
|
||||
// On failure we return empty string.
|
||||
if (!bKeyStateStatus)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Gets the layout of keyboard
|
||||
IntPtr hkl = GetKeyboardLayout(currentWindowThreadID);
|
||||
|
||||
// Maps the virtual keycode
|
||||
uint lScanCode = MapVirtualKeyEx(vKCode, 0, hkl);
|
||||
|
||||
// Keyboard state goes inconsistent if this is not in place. In other words, we need to call above commands in UP events also.
|
||||
if (!isKeyDown)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
// Converts the VKCode to unicode
|
||||
const uint wFlags = 1 << 2; // If bit 2 is set, keyboard state is not changed (Windows 10, version 1607 and newer)
|
||||
int relevantKeyCountInBuffer = ToUnicodeEx(vKCode, lScanCode, bKeyState, sbString, sbString.Capacity, wFlags, hkl);
|
||||
|
||||
string ret = string.Empty;
|
||||
|
||||
switch (relevantKeyCountInBuffer)
|
||||
{
|
||||
// dead key
|
||||
case -1:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
break;
|
||||
|
||||
// Single character in buffer
|
||||
case 1:
|
||||
ret = sbString.Length == 0 ? string.Empty : sbString[0].ToString();
|
||||
break;
|
||||
|
||||
// Two or more (only two of them is relevant)
|
||||
case 2:
|
||||
default:
|
||||
ret = sbString.ToString().Substring(0, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Save these
|
||||
lastScanCode = lScanCode;
|
||||
lastVKCode = vKCode;
|
||||
lastKeyState = (byte[])bKeyState.Clone();
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
79
src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs
Normal file
79
src/modules/poweraccent/PowerAccent.Core/Tools/Logger.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using interop;
|
||||
|
||||
namespace PowerAccent.Core.Tools
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
private static readonly IFileSystem _fileSystem = new FileSystem();
|
||||
private static readonly string ApplicationLogPath = Path.Combine(Constants.AppDataPath(), "PowerAccent\\Logs");
|
||||
|
||||
static Logger()
|
||||
{
|
||||
if (!_fileSystem.Directory.Exists(ApplicationLogPath))
|
||||
{
|
||||
_fileSystem.Directory.CreateDirectory(ApplicationLogPath);
|
||||
}
|
||||
|
||||
// Using InvariantCulture since this is used for a log file name
|
||||
var logFilePath = _fileSystem.Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt");
|
||||
|
||||
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
||||
|
||||
Trace.AutoFlush = true;
|
||||
}
|
||||
|
||||
public static void LogError(string message)
|
||||
{
|
||||
Log(message, "ERROR");
|
||||
}
|
||||
|
||||
public static void LogError(string message, Exception ex)
|
||||
{
|
||||
Log(
|
||||
message + Environment.NewLine +
|
||||
ex?.Message + Environment.NewLine +
|
||||
"Inner exception: " + Environment.NewLine +
|
||||
ex?.InnerException?.Message + Environment.NewLine +
|
||||
"Stack trace: " + Environment.NewLine +
|
||||
ex?.StackTrace,
|
||||
"ERROR");
|
||||
}
|
||||
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
Log(message, "WARNING");
|
||||
}
|
||||
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
Log(message, "INFO");
|
||||
}
|
||||
|
||||
private static void Log(string message, string type)
|
||||
{
|
||||
Trace.WriteLine(type + ": " + DateTime.Now.TimeOfDay);
|
||||
Trace.Indent();
|
||||
Trace.WriteLine(GetCallerInfo());
|
||||
Trace.WriteLine(message);
|
||||
Trace.Unindent();
|
||||
}
|
||||
|
||||
private static string GetCallerInfo()
|
||||
{
|
||||
StackTrace stackTrace = new StackTrace();
|
||||
|
||||
var methodName = stackTrace.GetFrame(3)?.GetMethod();
|
||||
var className = methodName?.DeclaringType?.Name;
|
||||
return "[Method]: " + methodName?.Name + " [Class]: " + className;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// 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.Runtime.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
|
||||
namespace PowerAccent.Core.Tools;
|
||||
|
||||
internal static class WindowsFunctions
|
||||
{
|
||||
public static void Insert(char c, bool back = false)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (back)
|
||||
{
|
||||
// Split in 2 different SendInput (Powershell doesn't take back issue)
|
||||
var inputsBack = new User32.INPUT[]
|
||||
{
|
||||
new User32.INPUT { type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT { wVk = (ushort)User32.VK.VK_BACK } },
|
||||
new User32.INPUT { type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT { wVk = (ushort)User32.VK.VK_BACK, dwFlags = User32.KEYEVENTF.KEYEVENTF_KEYUP } },
|
||||
};
|
||||
|
||||
var temp1 = User32.SendInput((uint)inputsBack.Length, inputsBack, sizeof(User32.INPUT));
|
||||
System.Threading.Thread.Sleep(1); // Some apps, like Terminal, need a little wait to process the sent backspace or they'll ignore it.
|
||||
}
|
||||
|
||||
// Letter
|
||||
var inputsInsert = new User32.INPUT[1]
|
||||
{
|
||||
new User32.INPUT { type = User32.INPUTTYPE.INPUT_KEYBOARD, ki = new User32.KEYBDINPUT { wVk = 0, dwFlags = User32.KEYEVENTF.KEYEVENTF_UNICODE, wScan = c } },
|
||||
};
|
||||
var temp2 = User32.SendInput((uint)inputsInsert.Length, inputsInsert, sizeof(User32.INPUT));
|
||||
}
|
||||
}
|
||||
|
||||
public static Point GetCaretPosition()
|
||||
{
|
||||
User32.GUITHREADINFO guiInfo = new ();
|
||||
guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
|
||||
User32.GetGUIThreadInfo(0, ref guiInfo);
|
||||
System.Drawing.Point caretPosition = new System.Drawing.Point(guiInfo.rcCaret.left, guiInfo.rcCaret.top);
|
||||
User32.ClientToScreen(guiInfo.hwndCaret, ref caretPosition);
|
||||
|
||||
if (caretPosition.X == 0)
|
||||
{
|
||||
System.Drawing.Point testPoint;
|
||||
User32.GetCaretPos(out testPoint);
|
||||
return testPoint;
|
||||
}
|
||||
|
||||
return caretPosition;
|
||||
}
|
||||
|
||||
public static (Point Location, Size Size, double Dpi) GetActiveDisplay()
|
||||
{
|
||||
User32.GUITHREADINFO guiInfo = new ();
|
||||
guiInfo.cbSize = (uint)Marshal.SizeOf(guiInfo);
|
||||
User32.GetGUIThreadInfo(0, ref guiInfo);
|
||||
var res = User32.MonitorFromWindow(guiInfo.hwndActive, User32.MonitorFlags.MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
User32.MONITORINFO monitorInfo = new ();
|
||||
monitorInfo.cbSize = (uint)Marshal.SizeOf(monitorInfo);
|
||||
User32.GetMonitorInfo(res, ref monitorInfo);
|
||||
|
||||
double dpi = User32.GetDpiForWindow(guiInfo.hwndActive) / 96d;
|
||||
|
||||
return (monitorInfo.rcWork.Location, monitorInfo.rcWork.Size, dpi);
|
||||
}
|
||||
|
||||
public static bool IsCapitalState()
|
||||
{
|
||||
var capital = User32.GetKeyState((int)User32.VK.VK_CAPITAL);
|
||||
var shift = User32.GetKeyState((int)User32.VK.VK_SHIFT);
|
||||
return capital != 0 || shift < 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user