Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 49 additions & 6 deletions KeyStats.Windows/KeyStats/Helpers/KeyNameMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ namespace KeyStats.Helpers;

public static class KeyNameMapper
{
private const uint ExtendedKeyFlag = 0x01;

private static readonly Dictionary<int, string> VirtualKeyNames = new()
{
// Function keys
Expand Down Expand Up @@ -65,9 +67,9 @@ public static class KeyNameMapper
0x10, 0x11, 0x12, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x5B, 0x5C
};

public static string GetKeyName(int vkCode)
public static string GetKeyName(int vkCode, uint scanCode = 0, uint flags = 0)
{
var baseName = GetBaseKeyName(vkCode);
var baseName = GetBaseKeyName(vkCode, scanCode, flags);
var modifiers = GetModifierNames(vkCode);

if (modifiers.Count == 0)
Expand All @@ -78,8 +80,18 @@ public static string GetKeyName(int vkCode)
return string.Join("+", modifiers) + "+" + baseName;
}

private static string GetBaseKeyName(int vkCode)
private static string GetBaseKeyName(int vkCode, uint scanCode, uint flags)
{
if (vkCode == 0x0D && (flags & ExtendedKeyFlag) != 0)
{
return "NumEnter";
}

if (GetNumLockOffNumpadKeyName(vkCode, scanCode, flags) is string numpadKeyName)
{
return numpadKeyName;
}

if (VirtualKeyNames.TryGetValue(vkCode, out var name))
{
return name;
Expand Down Expand Up @@ -114,11 +126,18 @@ private static string GetBaseKeyName(int vkCode)
}

// Try to get the key name using Windows API
var scanCode = NativeInterop.MapVirtualKey((uint)vkCode, NativeInterop.MAPVK_VK_TO_VSC);
if (scanCode > 0)
var effectiveScanCode = scanCode != 0
? scanCode
: NativeInterop.MapVirtualKey((uint)vkCode, NativeInterop.MAPVK_VK_TO_VSC);
if (effectiveScanCode > 0)
{
var keyNameBuffer = new char[32];
var lParam = (int)(scanCode << 16);
var lParam = (int)((effectiveScanCode & 0xFF) << 16);
if ((flags & ExtendedKeyFlag) != 0)
{
lParam |= 1 << 24;
}

var result = NativeInterop.GetKeyNameText(lParam, keyNameBuffer, keyNameBuffer.Length);
if (result > 0)
{
Expand All @@ -129,6 +148,30 @@ private static string GetBaseKeyName(int vkCode)
return $"Key{vkCode}";
}

private static string? GetNumLockOffNumpadKeyName(int vkCode, uint scanCode, uint flags)
{
if ((flags & ExtendedKeyFlag) != 0)
{
return null;
}

return (vkCode, scanCode) switch
{
(0x2D, 0x52u) => "Num0",
(0x23, 0x4Fu) => "Num1",
(0x28, 0x50u) => "Num2",
(0x22, 0x51u) => "Num3",
(0x25, 0x4Bu) => "Num4",
(0x0C, 0x4Cu) => "Num5",
(0x27, 0x4Du) => "Num6",
(0x24, 0x47u) => "Num7",
(0x26, 0x48u) => "Num8",
(0x21, 0x49u) => "Num9",
(0x2E, 0x53u) => "Num.",
_ => null
};
}

private static List<string> GetModifierNames(int vkCode)
{
var modifiers = new List<string>();
Expand Down
2 changes: 1 addition & 1 deletion KeyStats.Windows/KeyStats/Services/InputMonitorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ private IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)

if (isNewKey)
{
var keyName = KeyNameMapper.GetKeyName(vkCode);
var keyName = KeyNameMapper.GetKeyName(vkCode, hookStruct.scanCode, hookStruct.flags);
var hWnd = NativeInterop.GetForegroundWindow();
NativeInterop.GetWindowThreadProcessId(hWnd, out uint pid);
ThreadPool.QueueUserWorkItem(_ =>
Expand Down
33 changes: 27 additions & 6 deletions KeyStats.Windows/KeyStats/Services/StatsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,17 @@ private static Dictionary<string, int> AggregateKeyboardHeatmapCounts(Dictionary
}

var rawKey = kvp.Key ?? string.Empty;
if (NormalizeKeyboardHeatmapKey(rawKey) is string exactKey)
{
aggregated[exactKey] = SafeAdd(aggregated.TryGetValue(exactKey, out var current) ? current : 0, count);
continue;
}

if (rawKey.IndexOf("Num+", StringComparison.OrdinalIgnoreCase) >= 0)
{
aggregated["Num+"] = SafeAdd(aggregated.TryGetValue("Num+", out var current) ? current : 0, count);
}

var components = rawKey
.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries)
.Select(part => part.Trim())
Expand Down Expand Up @@ -1353,24 +1364,32 @@ private static Dictionary<string, int> AggregateKeyboardHeatmapCounts(Dictionary
var suffix = upper.Substring(3);
if (suffix.Length == 1 && char.IsDigit(suffix[0]))
{
return suffix;
return "Num" + suffix;
}

if (suffix == ".")
{
return ".";
return "Num.";
}
if (suffix == "+")
{
return "=";
return "Num+";
}
if (suffix == "-")
{
return "-";
return "Num-";
}
if (suffix == "/")
{
return "/";
return "Num/";
}
if (suffix == "*")
{
return "Num*";
}
if (suffix == "ENTER")
{
return "NumEnter";
}
}

Expand Down Expand Up @@ -1460,6 +1479,8 @@ private static Dictionary<string, int> AggregateKeyboardHeatmapCounts(Dictionary
case "SCROLLLOCK":
case "SCROLL":
return "ScrollLock";
case "NUMLOCK":
return "NumLock";
case "PAUSE":
case "BREAK":
return "Pause";
Expand All @@ -1480,7 +1501,7 @@ private static Dictionary<string, int> AggregateKeyboardHeatmapCounts(Dictionary
case "DOWNARROW":
return "Down";
default:
return trimmed;
return null;
}
}

Expand Down
36 changes: 34 additions & 2 deletions KeyStats.Windows/KeyStats/Views/Controls/KeyboardHeatmapControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ public RenderMetrics(double scale, Point origin)
}

private const double LayoutInset = 8;
private const double MaximumKeyboardScale = 58;

private static readonly HashSet<string> NumberKeyIds = new(Enumerable.Range(0, 10).Select(i => i.ToString(CultureInfo.InvariantCulture)));
private static readonly IReadOnlyList<KeySpec> Layout = BuildLayout();
Expand Down Expand Up @@ -131,7 +130,7 @@ private void OnThemeChanged()

var scaleX = available.Width / LayoutBounds.Width;
var scaleY = available.Height / LayoutBounds.Height;
var scale = Math.Min(Math.Min(scaleX, scaleY), MaximumKeyboardScale);
var scale = Math.Min(scaleX, scaleY);
var width = LayoutBounds.Width * scale;
var height = LayoutBounds.Height * scale;
var origin = new Point(
Expand Down Expand Up @@ -517,6 +516,10 @@ void AppendRow(double y, double startX, IEnumerable<(string id, string label, do
var yNavTop = y1;
var yNavBottom = y1 + 1.0 + navRowGap;

items.Add(new KeySpec("PrintScreen", "prt\nsc", new Rect(navStartX, yFunction, navKeyWidth, 1.0)));
items.Add(new KeySpec("ScrollLock", "scroll\nlock", new Rect(navStartX + navStepX, yFunction, navKeyWidth, 1.0)));
items.Add(new KeySpec("Pause", "pause", new Rect(navStartX + navStepX * 2, yFunction, navKeyWidth, 1.0)));

items.Add(new KeySpec("Insert", "ins", new Rect(navStartX, yNavTop, navKeyWidth, 1.0)));
items.Add(new KeySpec("Home", "home", new Rect(navStartX + navStepX, yNavTop, navKeyWidth, 1.0)));
items.Add(new KeySpec("PageUp", "pg up", new Rect(navStartX + navStepX * 2, yNavTop, navKeyWidth, 1.0)));
Expand All @@ -534,6 +537,35 @@ void AppendRow(double y, double startX, IEnumerable<(string id, string label, do
items.Add(new KeySpec("Down", "down", new Rect(arrowStartX + arrowStep, yArrowBottom, arrowWidth, 1.0)));
items.Add(new KeySpec("Right", "right", new Rect(arrowStartX + arrowStep * 2, yArrowBottom, arrowWidth, 1.0)));

var numpadGap = 0.55;
var numStartX = navStartX + navStepX * 3 + numpadGap;
var numKeyWidth = 1.0;
var numStep = numKeyWidth + keyGap;
var tallKeyHeight = 2.0 + rowGap;
var wideKeyWidth = 2.0 + keyGap;

items.Add(new KeySpec("NumLock", "num\nlock", new Rect(numStartX, y1, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num/", "/", new Rect(numStartX + numStep, y1, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num*", "*", new Rect(numStartX + numStep * 2, y1, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num-", "-", new Rect(numStartX + numStep * 3, y1, numKeyWidth, 1.0)));

items.Add(new KeySpec("Num7", "7", new Rect(numStartX, y2, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num8", "8", new Rect(numStartX + numStep, y2, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num9", "9", new Rect(numStartX + numStep * 2, y2, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num+", "+", new Rect(numStartX + numStep * 3, y2, numKeyWidth, tallKeyHeight)));

items.Add(new KeySpec("Num4", "4", new Rect(numStartX, y3, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num5", "5", new Rect(numStartX + numStep, y3, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num6", "6", new Rect(numStartX + numStep * 2, y3, numKeyWidth, 1.0)));

items.Add(new KeySpec("Num1", "1", new Rect(numStartX, y4, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num2", "2", new Rect(numStartX + numStep, y4, numKeyWidth, 1.0)));
items.Add(new KeySpec("Num3", "3", new Rect(numStartX + numStep * 2, y4, numKeyWidth, 1.0)));
items.Add(new KeySpec("NumEnter", "enter", new Rect(numStartX + numStep * 3, y4, numKeyWidth, tallKeyHeight)));

items.Add(new KeySpec("Num0", "0", new Rect(numStartX, y5, wideKeyWidth, 1.0)));
items.Add(new KeySpec("Num.", ".", new Rect(numStartX + numStep * 2, y5, numKeyWidth, 1.0)));

return items;
}
}
7 changes: 3 additions & 4 deletions KeyStats.Windows/KeyStats/Views/KeyboardHeatmapWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
xmlns:controls="clr-namespace:KeyStats.Views.Controls"
xmlns:p="clr-namespace:KeyStats.Properties"
Title="{x:Static p:Strings.Heatmap_WindowTitle}"
Width="860"
Width="1080"
Height="460"
MinWidth="860"
MinHeight="460"
MaxWidth="860"
MaxHeight="460"
ResizeMode="CanResize"
WindowStartupLocation="CenterScreen"
Background="{DynamicResource WindowSurfaceBrush}"
ShowInTaskbar="True">
Expand Down Expand Up @@ -221,7 +220,7 @@
PopupAnimation="Fade"
Closed="DatePickerPopup_Closed">
<Border Margin="0,6,0,0"
Background="{DynamicResource CardBrush}"
Background="{DynamicResource SurfaceBrush}"
BorderBrush="{DynamicResource DividerBrush}"
BorderThickness="1"
CornerRadius="10">
Expand Down